1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2013 Gateworks Corporation
4  *
5  * Author: Tim Harvey <tharvey@gateworks.com>
6  */
7 
8 #include <common.h>
9 #include <command.h>
10 #include <log.h>
11 #include <linux/delay.h>
12 #include <linux/errno.h>
13 #include <common.h>
14 #include <i2c.h>
15 #include <linux/ctype.h>
16 
17 #include <asm/arch/sys_proto.h>
18 #include <asm/global_data.h>
19 
20 #include "ventana_eeprom.h"
21 #include "gsc.h"
22 
23 DECLARE_GLOBAL_DATA_PTR;
24 
25 /*
26  * The Gateworks System Controller will fail to ACK a master transaction if
27  * it is busy, which can occur during its 1HZ timer tick while reading ADC's.
28  * When this does occur, it will never be busy long enough to fail more than
29  * 2 back-to-back transfers.  Thus we wrap i2c_read and i2c_write with
30  * 3 retries.
31  */
gsc_i2c_read(uchar chip,uint addr,int alen,uchar * buf,int len)32 int gsc_i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len)
33 {
34 	int retry = 3;
35 	int n = 0;
36 	int ret;
37 
38 	while (n++ < retry) {
39 		ret = i2c_read(chip, addr, alen, buf, len);
40 		if (!ret)
41 			break;
42 		debug("%s: 0x%02x 0x%02x retry%d: %d\n", __func__, chip, addr,
43 		      n, ret);
44 		if (ret != -ENODEV)
45 			break;
46 		mdelay(10);
47 	}
48 	return ret;
49 }
50 
gsc_i2c_write(uchar chip,uint addr,int alen,uchar * buf,int len)51 int gsc_i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len)
52 {
53 	int retry = 3;
54 	int n = 0;
55 	int ret;
56 
57 	while (n++ < retry) {
58 		ret = i2c_write(chip, addr, alen, buf, len);
59 		if (!ret)
60 			break;
61 		debug("%s: 0x%02x 0x%02x retry%d: %d\n", __func__, chip, addr,
62 		      n, ret);
63 		if (ret != -ENODEV)
64 			break;
65 		mdelay(10);
66 	}
67 	mdelay(100);
68 	return ret;
69 }
70 
gsc_get_board_temp(void)71 int gsc_get_board_temp(void)
72 {
73 	const void *fdt = gd->fdt_blob;
74 	int node, reg, mode, val;
75 	const char *label;
76 	u8 buf[2];
77 	int ret;
78 
79 	node = fdt_node_offset_by_compatible(fdt, -1, "gw,gsc-adc");
80 	if (node <= 0)
81 		return node;
82 	i2c_set_bus_num(0);
83 
84 	/* iterate over hwmon nodes */
85 	node = fdt_first_subnode(fdt, node);
86 	while (node > 0) {
87 		reg = fdtdec_get_int(fdt, node, "reg", -1);
88 		mode = fdtdec_get_int(fdt, node, "gw,mode", -1);
89 		label = fdt_stringlist_get(fdt, node, "label", 0, NULL);
90 
91 		if ((reg == -1) || (mode == -1) || !label) {
92 			printf("invalid dt:%s\n", fdt_get_name(fdt, node, NULL));
93 			continue;
94 		}
95 
96 		if ((mode != 0) || strcmp(label, "temp"))
97 			continue;
98 
99 		memset(buf, 0, sizeof(buf));
100 		ret = gsc_i2c_read(GSC_HWMON_ADDR, reg, 1, buf, sizeof(buf));
101 		val = buf[0] | buf[1] << 8;
102 		if (val >= 0) {
103 			if (val > 0x8000)
104 				val -= 0xffff;
105 			return val;
106 		}
107 		node = fdt_next_subnode(fdt, node);
108 	}
109 
110 	return 0;
111 }
112 
113 /* display hardware monitor ADC channels */
gsc_hwmon(void)114 int gsc_hwmon(void)
115 {
116 	const void *fdt = gd->fdt_blob;
117 	int node, reg, mode, len, val, offset;
118 	const char *label;
119 	u8 buf[2];
120 	int ret;
121 
122 	node = fdt_node_offset_by_compatible(fdt, -1, "gw,gsc-adc");
123 	if (node <= 0)
124 		return node;
125 	i2c_set_bus_num(0);
126 
127 	/* iterate over hwmon nodes */
128 	node = fdt_first_subnode(fdt, node);
129 	while (node > 0) {
130 		reg = fdtdec_get_int(fdt, node, "reg", -1);
131 		mode = fdtdec_get_int(fdt, node, "gw,mode", -1);
132 		offset = fdtdec_get_int(fdt, node, "gw,voltage-offset-microvolt", 0);
133 		label = fdt_stringlist_get(fdt, node, "label", 0, NULL);
134 
135 		if ((reg == -1) || (mode == -1) || !label)
136 			printf("invalid dt:%s\n", fdt_get_name(fdt, node, NULL));
137 
138 		memset(buf, 0, sizeof(buf));
139 		ret = gsc_i2c_read(GSC_HWMON_ADDR, reg, 1, buf, sizeof(buf));
140 		val = buf[0] | buf[1] << 8;
141 		if (val >= 0) {
142 			const u32 *div;
143 			int r[2];
144 
145 			switch (mode) {
146 			case 0: /* temperature (C*10) */
147 				if (val > 0x8000)
148 					val -= 0xffff;
149 				printf("%-8s: %d.%ldC\n", label, val / 10, abs(val % 10));
150 				break;
151 			case 1: /* prescaled voltage */
152 				if (val != 0xffff)
153 					printf("%-8s: %d.%03dV\n", label, val / 1000, val % 1000);
154 				break;
155 			case 2: /* scaled based on ref volt and resolution */
156 				val *= 2500;
157 				val /= 1 << 12;
158 
159 				/* apply pre-scaler voltage divider */
160 				div  = fdt_getprop(fdt, node, "gw,voltage-divider-ohms", &len);
161 				if (div && (len == sizeof(uint32_t) * 2)) {
162 					r[0] = fdt32_to_cpu(div[0]);
163 					r[1] = fdt32_to_cpu(div[1]);
164 					if (r[0] && r[1]) {
165 						val *= (r[0] + r[1]);
166 						val /= r[1];
167 					}
168 				}
169 
170 				/* adjust by offset */
171 				val += (offset / 1000);
172 
173 				printf("%-8s: %d.%03dV\n", label, val / 1000, val % 1000);
174 				break;
175 			}
176 		}
177 		node = fdt_next_subnode(fdt, node);
178 	}
179 
180 	return 0;
181 }
182 
gsc_info(int verbose)183 int gsc_info(int verbose)
184 {
185 	unsigned char buf[16];
186 
187 	i2c_set_bus_num(0);
188 	if (gsc_i2c_read(GSC_SC_ADDR, 0, 1, buf, 16))
189 		return CMD_RET_FAILURE;
190 
191 	printf("GSC:   v%d", buf[GSC_SC_FWVER]);
192 	printf(" 0x%04x", buf[GSC_SC_FWCRC] | buf[GSC_SC_FWCRC+1]<<8);
193 	printf(" WDT:%sabled", (buf[GSC_SC_CTRL1] & (1<<GSC_SC_CTRL1_WDEN))
194 		? "en" : "dis");
195 	if (buf[GSC_SC_STATUS] & (1 << GSC_SC_IRQ_WATCHDOG)) {
196 		buf[GSC_SC_STATUS] &= ~(1 << GSC_SC_IRQ_WATCHDOG);
197 		puts(" WDT_RESET");
198 		gsc_i2c_write(GSC_SC_ADDR, GSC_SC_STATUS, 1,
199 			      &buf[GSC_SC_STATUS], 1);
200 	}
201 	printf(" board temp at %dC", gsc_get_board_temp() / 10);
202 	puts("\n");
203 	if (!verbose)
204 		return CMD_RET_SUCCESS;
205 
206 	gsc_hwmon();
207 
208 	return 0;
209 }
210 
211 /*
212  *  The Gateworks System Controller implements a boot
213  *  watchdog (always enabled) as a workaround for IMX6 boot related
214  *  errata such as:
215  *    ERR005768 - no fix scheduled
216  *    ERR006282 - fixed in silicon r1.2
217  *    ERR007117 - fixed in silicon r1.3
218  *    ERR007220 - fixed in silicon r1.3
219  *    ERR007926 - no fix scheduled
220  *  see http://cache.freescale.com/files/32bit/doc/errata/IMX6DQCE.pdf
221  *
222  * Disable the boot watchdog
223  */
gsc_boot_wd_disable(void)224 int gsc_boot_wd_disable(void)
225 {
226 	u8 reg;
227 
228 	i2c_set_bus_num(CONFIG_I2C_GSC);
229 	if (!gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1)) {
230 		reg |= (1 << GSC_SC_CTRL1_WDDIS);
231 		if (!gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
232 			return 0;
233 	}
234 	puts("Error: could not disable GSC Watchdog\n");
235 	return 1;
236 }
237 
238 /* determine BOM revision from model */
get_bom_rev(const char * str)239 int get_bom_rev(const char *str)
240 {
241 	int  rev_bom = 0;
242 	int i;
243 
244 	for (i = strlen(str) - 1; i > 0; i--) {
245 		if (str[i] == '-')
246 			break;
247 		if (str[i] >= '1' && str[i] <= '9') {
248 			rev_bom = str[i] - '0';
249 			break;
250 		}
251 	}
252 	return rev_bom;
253 }
254 
255 /* determine PCB revision from model */
get_pcb_rev(const char * str)256 char get_pcb_rev(const char *str)
257 {
258 	char rev_pcb = 'A';
259 	int i;
260 
261 	for (i = strlen(str) - 1; i > 0; i--) {
262 		if (str[i] == '-')
263 			break;
264 		if (str[i] >= 'A') {
265 			rev_pcb = str[i];
266 			break;
267 		}
268 	}
269 	return rev_pcb;
270 }
271 
272 /*
273  * get dt name based on model and detail level:
274  */
gsc_get_dtb_name(int level,char * buf,int sz)275 const char *gsc_get_dtb_name(int level, char *buf, int sz)
276 {
277 	const char *model = (const char *)ventana_info.model;
278 	const char *pre = is_mx6dq() ? "imx6q-" : "imx6dl-";
279 	int modelno, rev_pcb, rev_bom;
280 
281 	/* a few board models are dt equivalents to other models */
282 	if (strncasecmp(model, "gw5906", 6) == 0)
283 		model = "gw552x-d";
284 	else if (strncasecmp(model, "gw5908", 6) == 0)
285 		model = "gw53xx-f";
286 	else if (strncasecmp(model, "gw5905", 6) == 0)
287 		model = "gw5904-a";
288 
289 	modelno = ((model[2] - '0') * 1000)
290 		  + ((model[3] - '0') * 100)
291 		  + ((model[4] - '0') * 10)
292 		  + (model[5] - '0');
293 	rev_pcb = tolower(get_pcb_rev(model));
294 	rev_bom = get_bom_rev(model);
295 
296 	/* compare model/rev/bom in order of most specific to least */
297 	snprintf(buf, sz, "%s%04d", pre, modelno);
298 	switch (level) {
299 	case 0: /* full model first (ie gw5400-a1) */
300 		if (rev_bom) {
301 			snprintf(buf, sz, "%sgw%04d-%c%d", pre, modelno, rev_pcb, rev_bom);
302 			break;
303 		}
304 		fallthrough;
305 	case 1: /* don't care about bom rev (ie gw5400-a) */
306 		snprintf(buf, sz, "%sgw%04d-%c", pre, modelno, rev_pcb);
307 		break;
308 	case 2: /* don't care about the pcb rev (ie gw5400) */
309 		snprintf(buf, sz, "%sgw%04d", pre, modelno);
310 		break;
311 	case 3: /* look for generic model (ie gw540x) */
312 		snprintf(buf, sz, "%sgw%03dx", pre, modelno / 10);
313 		break;
314 	case 4: /* look for more generic model (ie gw54xx) */
315 		snprintf(buf, sz, "%sgw%02dxx", pre, modelno / 100);
316 		break;
317 	default: /* give up */
318 		return NULL;
319 	}
320 
321 	return buf;
322 }
323 
324 #if defined(CONFIG_CMD_GSC) && !defined(CONFIG_SPL_BUILD)
do_gsc_sleep(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])325 static int do_gsc_sleep(struct cmd_tbl *cmdtp, int flag, int argc,
326 			char *const argv[])
327 {
328 	unsigned char reg;
329 	unsigned long secs = 0;
330 
331 	if (argc < 2)
332 		return CMD_RET_USAGE;
333 
334 	secs = dectoul(argv[1], NULL);
335 	printf("GSC Sleeping for %ld seconds\n", secs);
336 
337 	i2c_set_bus_num(0);
338 	reg = (secs >> 24) & 0xff;
339 	if (gsc_i2c_write(GSC_SC_ADDR, 9, 1, &reg, 1))
340 		goto error;
341 	reg = (secs >> 16) & 0xff;
342 	if (gsc_i2c_write(GSC_SC_ADDR, 8, 1, &reg, 1))
343 		goto error;
344 	reg = (secs >> 8) & 0xff;
345 	if (gsc_i2c_write(GSC_SC_ADDR, 7, 1, &reg, 1))
346 		goto error;
347 	reg = secs & 0xff;
348 	if (gsc_i2c_write(GSC_SC_ADDR, 6, 1, &reg, 1))
349 		goto error;
350 	if (gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
351 		goto error;
352 	reg |= (1 << 2);
353 	if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
354 		goto error;
355 	reg &= ~(1 << 2);
356 	reg |= 0x3;
357 	if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
358 		goto error;
359 
360 	return CMD_RET_SUCCESS;
361 
362 error:
363 	printf("i2c error\n");
364 	return CMD_RET_FAILURE;
365 }
366 
do_gsc_wd(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])367 static int do_gsc_wd(struct cmd_tbl *cmdtp, int flag, int argc,
368 		     char *const argv[])
369 {
370 	unsigned char reg;
371 
372 	if (argc < 2)
373 		return CMD_RET_USAGE;
374 
375 	if (strcasecmp(argv[1], "enable") == 0) {
376 		int timeout = 0;
377 
378 		if (argc > 2)
379 			timeout = dectoul(argv[2], NULL);
380 		i2c_set_bus_num(0);
381 		if (gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
382 			return CMD_RET_FAILURE;
383 		reg &= ~((1 << GSC_SC_CTRL1_WDEN) | (1 << GSC_SC_CTRL1_WDTIME));
384 		if (timeout == 60)
385 			reg |= (1 << GSC_SC_CTRL1_WDTIME);
386 		else
387 			timeout = 30;
388 		reg |= (1 << GSC_SC_CTRL1_WDEN);
389 		if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
390 			return CMD_RET_FAILURE;
391 		printf("GSC Watchdog enabled with timeout=%d seconds\n",
392 		       timeout);
393 	} else if (strcasecmp(argv[1], "disable") == 0) {
394 		i2c_set_bus_num(0);
395 		if (gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
396 			return CMD_RET_FAILURE;
397 		reg &= ~((1 << GSC_SC_CTRL1_WDEN) | (1 << GSC_SC_CTRL1_WDTIME));
398 		if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, &reg, 1))
399 			return CMD_RET_FAILURE;
400 		printf("GSC Watchdog disabled\n");
401 	} else {
402 		return CMD_RET_USAGE;
403 	}
404 	return CMD_RET_SUCCESS;
405 }
406 
do_gsc(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])407 static int do_gsc(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
408 {
409 	if (argc < 2)
410 		return gsc_info(1);
411 
412 	if (strcasecmp(argv[1], "wd") == 0)
413 		return do_gsc_wd(cmdtp, flag, --argc, ++argv);
414 	else if (strcasecmp(argv[1], "sleep") == 0)
415 		return do_gsc_sleep(cmdtp, flag, --argc, ++argv);
416 
417 	return CMD_RET_USAGE;
418 }
419 
420 U_BOOT_CMD(
421 	gsc, 4, 1, do_gsc, "GSC configuration",
422 	"[wd enable [30|60]]|[wd disable]|[sleep <secs>]\n"
423 	);
424 
425 #endif /* CONFIG_CMD_GSC */
426