blob: 46448a54db904e855771a080e586a5a0b34ee744 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Tim Harvey552c3582014-03-06 07:46:30 -08002/*
3 * Copyright (C) 2013 Gateworks Corporation
4 *
5 * Author: Tim Harvey <tharvey@gateworks.com>
Tim Harvey552c3582014-03-06 07:46:30 -08006 */
7
Simon Glass0f2af882020-05-10 11:40:05 -06008#include <common.h>
Simon Glassed38aef2020-05-10 11:40:03 -06009#include <command.h>
Simon Glass0f2af882020-05-10 11:40:05 -060010#include <log.h>
Simon Glassdbd79542020-05-10 11:40:11 -060011#include <linux/delay.h>
Masahiro Yamada56a931c2016-09-21 11:28:55 +090012#include <linux/errno.h>
Tim Harvey552c3582014-03-06 07:46:30 -080013#include <common.h>
14#include <i2c.h>
15#include <linux/ctype.h>
16
Tim Harvey948202c2021-03-01 14:33:32 -080017#include <asm/arch/sys_proto.h>
Tim Harveyafcaf962021-07-24 10:40:41 -070018#include <asm/global_data.h>
Tim Harvey895aace2022-03-07 16:24:00 -080019#include <dm/device.h>
20#include <dm/uclass.h>
Tim Harvey948202c2021-03-01 14:33:32 -080021
Tim Harveyd15181c2016-05-23 08:25:27 -070022#include "ventana_eeprom.h"
Tim Harvey552c3582014-03-06 07:46:30 -080023#include "gsc.h"
24
Tim Harveyafcaf962021-07-24 10:40:41 -070025DECLARE_GLOBAL_DATA_PTR;
26
Tim Harvey895aace2022-03-07 16:24:00 -080027#if CONFIG_IS_ENABLED(DM_I2C)
28struct udevice *i2c_get_dev(int busno, int slave)
29{
30 struct udevice *dev, *bus;
31 int ret;
32
33 ret = uclass_get_device_by_seq(UCLASS_I2C, busno, &bus);
34 if (ret)
35 return NULL;
36 ret = dm_i2c_probe(bus, slave, 0, &dev);
37 if (ret)
38 return NULL;
39
40 return dev;
41}
42#endif
43
Tim Harvey552c3582014-03-06 07:46:30 -080044/*
45 * The Gateworks System Controller will fail to ACK a master transaction if
46 * it is busy, which can occur during its 1HZ timer tick while reading ADC's.
47 * When this does occur, it will never be busy long enough to fail more than
48 * 2 back-to-back transfers. Thus we wrap i2c_read and i2c_write with
49 * 3 retries.
50 */
51int gsc_i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len)
52{
53 int retry = 3;
54 int n = 0;
55 int ret;
Tim Harvey895aace2022-03-07 16:24:00 -080056#if CONFIG_IS_ENABLED(DM_I2C)
57 struct udevice *dev;
58
59 dev = i2c_get_dev(CONFIG_I2C_GSC, chip);
60 if (!dev)
61 return -ENODEV;
62 ret = i2c_set_chip_offset_len(dev, alen);
63 if (ret) {
64 puts("EEPROM: Failed to set alen\n");
65 return ret;
66 }
67#else
68 i2c_set_bus_num(CONFIG_I2C_GSC);
69#endif
Tim Harvey552c3582014-03-06 07:46:30 -080070
71 while (n++ < retry) {
Tim Harvey895aace2022-03-07 16:24:00 -080072#if CONFIG_IS_ENABLED(DM_I2C)
73 ret = dm_i2c_read(dev, addr, buf, len);
74#else
Tim Harvey552c3582014-03-06 07:46:30 -080075 ret = i2c_read(chip, addr, alen, buf, len);
Tim Harvey895aace2022-03-07 16:24:00 -080076#endif
Tim Harvey552c3582014-03-06 07:46:30 -080077 if (!ret)
78 break;
79 debug("%s: 0x%02x 0x%02x retry%d: %d\n", __func__, chip, addr,
80 n, ret);
Tim Harvey679070a2022-03-24 08:32:00 -070081 if (ret != -EREMOTEIO)
Tim Harvey552c3582014-03-06 07:46:30 -080082 break;
83 mdelay(10);
84 }
85 return ret;
86}
87
88int gsc_i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len)
89{
90 int retry = 3;
91 int n = 0;
92 int ret;
Tim Harvey895aace2022-03-07 16:24:00 -080093#if CONFIG_IS_ENABLED(DM_I2C)
94 struct udevice *dev;
95
96 dev = i2c_get_dev(CONFIG_I2C_GSC, chip);
97 if (!dev)
98 return -ENODEV;
99 ret = i2c_set_chip_offset_len(dev, alen);
100 if (ret) {
101 puts("EEPROM: Failed to set alen\n");
102 return ret;
103 }
104#endif
Tim Harvey552c3582014-03-06 07:46:30 -0800105
106 while (n++ < retry) {
Tim Harvey895aace2022-03-07 16:24:00 -0800107#if CONFIG_IS_ENABLED(DM_I2C)
108 ret = dm_i2c_write(dev, addr, buf, len);
109#else
Tim Harvey552c3582014-03-06 07:46:30 -0800110 ret = i2c_write(chip, addr, alen, buf, len);
Tim Harvey895aace2022-03-07 16:24:00 -0800111#endif
Tim Harvey552c3582014-03-06 07:46:30 -0800112 if (!ret)
113 break;
114 debug("%s: 0x%02x 0x%02x retry%d: %d\n", __func__, chip, addr,
115 n, ret);
Tim Harvey679070a2022-03-24 08:32:00 -0700116 if (ret != -EREMOTEIO)
Tim Harvey552c3582014-03-06 07:46:30 -0800117 break;
118 mdelay(10);
119 }
Tim Harvey8d68c8e2014-08-07 22:35:44 -0700120 mdelay(100);
Tim Harvey552c3582014-03-06 07:46:30 -0800121 return ret;
122}
123
Tim Harveyafcaf962021-07-24 10:40:41 -0700124int gsc_get_board_temp(void)
125{
126 const void *fdt = gd->fdt_blob;
127 int node, reg, mode, val;
128 const char *label;
129 u8 buf[2];
130 int ret;
131
132 node = fdt_node_offset_by_compatible(fdt, -1, "gw,gsc-adc");
133 if (node <= 0)
134 return node;
Tim Harveyafcaf962021-07-24 10:40:41 -0700135
136 /* iterate over hwmon nodes */
137 node = fdt_first_subnode(fdt, node);
138 while (node > 0) {
139 reg = fdtdec_get_int(fdt, node, "reg", -1);
140 mode = fdtdec_get_int(fdt, node, "gw,mode", -1);
141 label = fdt_stringlist_get(fdt, node, "label", 0, NULL);
142
143 if ((reg == -1) || (mode == -1) || !label) {
144 printf("invalid dt:%s\n", fdt_get_name(fdt, node, NULL));
145 continue;
146 }
147
148 if ((mode != 0) || strcmp(label, "temp"))
149 continue;
150
151 memset(buf, 0, sizeof(buf));
152 ret = gsc_i2c_read(GSC_HWMON_ADDR, reg, 1, buf, sizeof(buf));
153 val = buf[0] | buf[1] << 8;
154 if (val >= 0) {
155 if (val > 0x8000)
156 val -= 0xffff;
157 return val;
158 }
159 node = fdt_next_subnode(fdt, node);
160 }
161
162 return 0;
163}
164
165/* display hardware monitor ADC channels */
166int gsc_hwmon(void)
Tim Harvey552c3582014-03-06 07:46:30 -0800167{
Tim Harveyafcaf962021-07-24 10:40:41 -0700168 const void *fdt = gd->fdt_blob;
169 int node, reg, mode, len, val, offset;
170 const char *label;
171 u8 buf[2];
172 int ret;
173
174 node = fdt_node_offset_by_compatible(fdt, -1, "gw,gsc-adc");
175 if (node <= 0)
176 return node;
Tim Harveyafcaf962021-07-24 10:40:41 -0700177
178 /* iterate over hwmon nodes */
179 node = fdt_first_subnode(fdt, node);
180 while (node > 0) {
181 reg = fdtdec_get_int(fdt, node, "reg", -1);
182 mode = fdtdec_get_int(fdt, node, "gw,mode", -1);
183 offset = fdtdec_get_int(fdt, node, "gw,voltage-offset-microvolt", 0);
184 label = fdt_stringlist_get(fdt, node, "label", 0, NULL);
185
186 if ((reg == -1) || (mode == -1) || !label)
187 printf("invalid dt:%s\n", fdt_get_name(fdt, node, NULL));
188
189 memset(buf, 0, sizeof(buf));
190 ret = gsc_i2c_read(GSC_HWMON_ADDR, reg, 1, buf, sizeof(buf));
191 val = buf[0] | buf[1] << 8;
192 if (val >= 0) {
193 const u32 *div;
194 int r[2];
195
196 switch (mode) {
197 case 0: /* temperature (C*10) */
198 if (val > 0x8000)
199 val -= 0xffff;
200 printf("%-8s: %d.%ldC\n", label, val / 10, abs(val % 10));
201 break;
202 case 1: /* prescaled voltage */
203 if (val != 0xffff)
204 printf("%-8s: %d.%03dV\n", label, val / 1000, val % 1000);
205 break;
206 case 2: /* scaled based on ref volt and resolution */
207 val *= 2500;
208 val /= 1 << 12;
Tim Harvey552c3582014-03-06 07:46:30 -0800209
Tim Harveyafcaf962021-07-24 10:40:41 -0700210 /* apply pre-scaler voltage divider */
211 div = fdt_getprop(fdt, node, "gw,voltage-divider-ohms", &len);
212 if (div && (len == sizeof(uint32_t) * 2)) {
213 r[0] = fdt32_to_cpu(div[0]);
214 r[1] = fdt32_to_cpu(div[1]);
215 if (r[0] && r[1]) {
216 val *= (r[0] + r[1]);
217 val /= r[1];
218 }
219 }
220
221 /* adjust by offset */
222 val += (offset / 1000);
223
224 printf("%-8s: %d.%03dV\n", label, val / 1000, val % 1000);
225 break;
226 }
227 }
228 node = fdt_next_subnode(fdt, node);
Tim Harvey552c3582014-03-06 07:46:30 -0800229 }
Tim Harveyafcaf962021-07-24 10:40:41 -0700230
231 return 0;
Tim Harvey552c3582014-03-06 07:46:30 -0800232}
233
Tim Harvey92e3d842015-04-08 12:54:59 -0700234int gsc_info(int verbose)
Tim Harvey552c3582014-03-06 07:46:30 -0800235{
Tim Harvey92e3d842015-04-08 12:54:59 -0700236 unsigned char buf[16];
Tim Harvey552c3582014-03-06 07:46:30 -0800237
Tim Harvey92e3d842015-04-08 12:54:59 -0700238 if (gsc_i2c_read(GSC_SC_ADDR, 0, 1, buf, 16))
239 return CMD_RET_FAILURE;
240
241 printf("GSC: v%d", buf[GSC_SC_FWVER]);
242 printf(" 0x%04x", buf[GSC_SC_FWCRC] | buf[GSC_SC_FWCRC+1]<<8);
243 printf(" WDT:%sabled", (buf[GSC_SC_CTRL1] & (1<<GSC_SC_CTRL1_WDEN))
244 ? "en" : "dis");
245 if (buf[GSC_SC_STATUS] & (1 << GSC_SC_IRQ_WATCHDOG)) {
246 buf[GSC_SC_STATUS] &= ~(1 << GSC_SC_IRQ_WATCHDOG);
247 puts(" WDT_RESET");
248 gsc_i2c_write(GSC_SC_ADDR, GSC_SC_STATUS, 1,
249 &buf[GSC_SC_STATUS], 1);
250 }
Tim Harveyafcaf962021-07-24 10:40:41 -0700251 printf(" board temp at %dC", gsc_get_board_temp() / 10);
Tim Harvey92e3d842015-04-08 12:54:59 -0700252 puts("\n");
253 if (!verbose)
254 return CMD_RET_SUCCESS;
255
Tim Harveyafcaf962021-07-24 10:40:41 -0700256 gsc_hwmon();
257
Tim Harvey552c3582014-03-06 07:46:30 -0800258 return 0;
259}
260
Tim Harvey40feabb2015-05-08 18:28:36 -0700261/*
262 * The Gateworks System Controller implements a boot
263 * watchdog (always enabled) as a workaround for IMX6 boot related
264 * errata such as:
265 * ERR005768 - no fix scheduled
266 * ERR006282 - fixed in silicon r1.2
267 * ERR007117 - fixed in silicon r1.3
268 * ERR007220 - fixed in silicon r1.3
269 * ERR007926 - no fix scheduled
270 * see http://cache.freescale.com/files/32bit/doc/errata/IMX6DQCE.pdf
271 *
272 * Disable the boot watchdog
273 */
274int gsc_boot_wd_disable(void)
275{
276 u8 reg;
277
Tim Harvey40feabb2015-05-08 18:28:36 -0700278 if (!gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1)) {
279 reg |= (1 << GSC_SC_CTRL1_WDDIS);
280 if (!gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
281 return 0;
282 }
283 puts("Error: could not disable GSC Watchdog\n");
284 return 1;
285}
286
Tim Harvey948202c2021-03-01 14:33:32 -0800287/* determine BOM revision from model */
288int get_bom_rev(const char *str)
289{
290 int rev_bom = 0;
291 int i;
292
293 for (i = strlen(str) - 1; i > 0; i--) {
294 if (str[i] == '-')
295 break;
296 if (str[i] >= '1' && str[i] <= '9') {
297 rev_bom = str[i] - '0';
298 break;
299 }
300 }
301 return rev_bom;
302}
303
304/* determine PCB revision from model */
305char get_pcb_rev(const char *str)
306{
307 char rev_pcb = 'A';
308 int i;
309
310 for (i = strlen(str) - 1; i > 0; i--) {
311 if (str[i] == '-')
312 break;
313 if (str[i] >= 'A') {
314 rev_pcb = str[i];
315 break;
316 }
317 }
318 return rev_pcb;
319}
320
321/*
322 * get dt name based on model and detail level:
323 */
324const char *gsc_get_dtb_name(int level, char *buf, int sz)
325{
326 const char *model = (const char *)ventana_info.model;
327 const char *pre = is_mx6dq() ? "imx6q-" : "imx6dl-";
328 int modelno, rev_pcb, rev_bom;
329
330 /* a few board models are dt equivalents to other models */
331 if (strncasecmp(model, "gw5906", 6) == 0)
332 model = "gw552x-d";
333 else if (strncasecmp(model, "gw5908", 6) == 0)
334 model = "gw53xx-f";
335 else if (strncasecmp(model, "gw5905", 6) == 0)
336 model = "gw5904-a";
337
338 modelno = ((model[2] - '0') * 1000)
339 + ((model[3] - '0') * 100)
340 + ((model[4] - '0') * 10)
341 + (model[5] - '0');
342 rev_pcb = tolower(get_pcb_rev(model));
343 rev_bom = get_bom_rev(model);
344
345 /* compare model/rev/bom in order of most specific to least */
346 snprintf(buf, sz, "%s%04d", pre, modelno);
347 switch (level) {
348 case 0: /* full model first (ie gw5400-a1) */
349 if (rev_bom) {
350 snprintf(buf, sz, "%sgw%04d-%c%d", pre, modelno, rev_pcb, rev_bom);
351 break;
352 }
353 fallthrough;
354 case 1: /* don't care about bom rev (ie gw5400-a) */
355 snprintf(buf, sz, "%sgw%04d-%c", pre, modelno, rev_pcb);
356 break;
357 case 2: /* don't care about the pcb rev (ie gw5400) */
358 snprintf(buf, sz, "%sgw%04d", pre, modelno);
359 break;
360 case 3: /* look for generic model (ie gw540x) */
361 snprintf(buf, sz, "%sgw%03dx", pre, modelno / 10);
362 break;
363 case 4: /* look for more generic model (ie gw54xx) */
364 snprintf(buf, sz, "%sgw%02dxx", pre, modelno / 100);
365 break;
366 default: /* give up */
367 return NULL;
368 }
369
370 return buf;
371}
372
Tom Rinib6f576a2018-01-03 09:16:29 -0500373#if defined(CONFIG_CMD_GSC) && !defined(CONFIG_SPL_BUILD)
Simon Glassed38aef2020-05-10 11:40:03 -0600374static int do_gsc_sleep(struct cmd_tbl *cmdtp, int flag, int argc,
375 char *const argv[])
Tim Harvey30ea7ee2016-05-24 11:03:50 -0700376{
377 unsigned char reg;
378 unsigned long secs = 0;
379
380 if (argc < 2)
381 return CMD_RET_USAGE;
382
Simon Glassff9b9032021-07-24 09:03:30 -0600383 secs = dectoul(argv[1], NULL);
Tim Harvey30ea7ee2016-05-24 11:03:50 -0700384 printf("GSC Sleeping for %ld seconds\n", secs);
385
Tim Harvey30ea7ee2016-05-24 11:03:50 -0700386 reg = (secs >> 24) & 0xff;
387 if (gsc_i2c_write(GSC_SC_ADDR, 9, 1, &reg, 1))
388 goto error;
389 reg = (secs >> 16) & 0xff;
390 if (gsc_i2c_write(GSC_SC_ADDR, 8, 1, &reg, 1))
391 goto error;
392 reg = (secs >> 8) & 0xff;
393 if (gsc_i2c_write(GSC_SC_ADDR, 7, 1, &reg, 1))
394 goto error;
395 reg = secs & 0xff;
396 if (gsc_i2c_write(GSC_SC_ADDR, 6, 1, &reg, 1))
397 goto error;
398 if (gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
399 goto error;
400 reg |= (1 << 2);
401 if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
402 goto error;
403 reg &= ~(1 << 2);
404 reg |= 0x3;
405 if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
406 goto error;
407
408 return CMD_RET_SUCCESS;
409
410error:
411 printf("i2c error\n");
412 return CMD_RET_FAILURE;
413}
414
Simon Glassed38aef2020-05-10 11:40:03 -0600415static int do_gsc_wd(struct cmd_tbl *cmdtp, int flag, int argc,
416 char *const argv[])
Tim Harvey92e3d842015-04-08 12:54:59 -0700417{
418 unsigned char reg;
419
420 if (argc < 2)
421 return CMD_RET_USAGE;
422
423 if (strcasecmp(argv[1], "enable") == 0) {
424 int timeout = 0;
425
426 if (argc > 2)
Simon Glassff9b9032021-07-24 09:03:30 -0600427 timeout = dectoul(argv[2], NULL);
Tim Harvey92e3d842015-04-08 12:54:59 -0700428 if (gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
429 return CMD_RET_FAILURE;
430 reg &= ~((1 << GSC_SC_CTRL1_WDEN) | (1 << GSC_SC_CTRL1_WDTIME));
431 if (timeout == 60)
432 reg |= (1 << GSC_SC_CTRL1_WDTIME);
433 else
434 timeout = 30;
435 reg |= (1 << GSC_SC_CTRL1_WDEN);
436 if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
437 return CMD_RET_FAILURE;
438 printf("GSC Watchdog enabled with timeout=%d seconds\n",
439 timeout);
440 } else if (strcasecmp(argv[1], "disable") == 0) {
Tim Harvey92e3d842015-04-08 12:54:59 -0700441 if (gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
442 return CMD_RET_FAILURE;
443 reg &= ~((1 << GSC_SC_CTRL1_WDEN) | (1 << GSC_SC_CTRL1_WDTIME));
444 if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
445 return CMD_RET_FAILURE;
446 printf("GSC Watchdog disabled\n");
447 } else {
448 return CMD_RET_USAGE;
449 }
450 return CMD_RET_SUCCESS;
451}
452
Simon Glassed38aef2020-05-10 11:40:03 -0600453static int do_gsc(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Tim Harvey92e3d842015-04-08 12:54:59 -0700454{
455 if (argc < 2)
456 return gsc_info(1);
457
458 if (strcasecmp(argv[1], "wd") == 0)
459 return do_gsc_wd(cmdtp, flag, --argc, ++argv);
Tim Harvey30ea7ee2016-05-24 11:03:50 -0700460 else if (strcasecmp(argv[1], "sleep") == 0)
461 return do_gsc_sleep(cmdtp, flag, --argc, ++argv);
Tim Harvey92e3d842015-04-08 12:54:59 -0700462
463 return CMD_RET_USAGE;
464}
465
466U_BOOT_CMD(
467 gsc, 4, 1, do_gsc, "GSC configuration",
Tim Harvey30ea7ee2016-05-24 11:03:50 -0700468 "[wd enable [30|60]]|[wd disable]|[sleep <secs>]\n"
Tim Harvey92e3d842015-04-08 12:54:59 -0700469 );
Tim Harvey552c3582014-03-06 07:46:30 -0800470
471#endif /* CONFIG_CMD_GSC */