]>
Commit | Line | Data |
---|---|---|
59189a8b TH |
1 | /* |
2 | * Copyright (C) 2013 Gateworks Corporation | |
3 | * | |
4 | * Author: Tim Harvey <tharvey@gateworks.com> | |
5 | * | |
6 | * SPDX-License-Identifier: GPL-2.0+ | |
7 | */ | |
8 | ||
1221ce45 | 9 | #include <linux/errno.h> |
59189a8b TH |
10 | #include <common.h> |
11 | #include <i2c.h> | |
12 | #include <linux/ctype.h> | |
13 | ||
a419352d | 14 | #include "ventana_eeprom.h" |
59189a8b TH |
15 | #include "gsc.h" |
16 | ||
59189a8b TH |
17 | /* |
18 | * The Gateworks System Controller will fail to ACK a master transaction if | |
19 | * it is busy, which can occur during its 1HZ timer tick while reading ADC's. | |
20 | * When this does occur, it will never be busy long enough to fail more than | |
21 | * 2 back-to-back transfers. Thus we wrap i2c_read and i2c_write with | |
22 | * 3 retries. | |
23 | */ | |
24 | int gsc_i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) | |
25 | { | |
26 | int retry = 3; | |
27 | int n = 0; | |
28 | int ret; | |
29 | ||
30 | while (n++ < retry) { | |
31 | ret = i2c_read(chip, addr, alen, buf, len); | |
32 | if (!ret) | |
33 | break; | |
34 | debug("%s: 0x%02x 0x%02x retry%d: %d\n", __func__, chip, addr, | |
35 | n, ret); | |
36 | if (ret != -ENODEV) | |
37 | break; | |
38 | mdelay(10); | |
39 | } | |
40 | return ret; | |
41 | } | |
42 | ||
43 | int gsc_i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len) | |
44 | { | |
45 | int retry = 3; | |
46 | int n = 0; | |
47 | int ret; | |
48 | ||
49 | while (n++ < retry) { | |
50 | ret = i2c_write(chip, addr, alen, buf, len); | |
51 | if (!ret) | |
52 | break; | |
53 | debug("%s: 0x%02x 0x%02x retry%d: %d\n", __func__, chip, addr, | |
54 | n, ret); | |
55 | if (ret != -ENODEV) | |
56 | break; | |
57 | mdelay(10); | |
58 | } | |
e5131d53 | 59 | mdelay(100); |
59189a8b TH |
60 | return ret; |
61 | } | |
62 | ||
16e369f5 | 63 | static void read_hwmon(const char *name, uint reg, uint size) |
59189a8b TH |
64 | { |
65 | unsigned char buf[3]; | |
66 | uint ui; | |
67 | ||
68 | printf("%-8s:", name); | |
69 | memset(buf, 0, sizeof(buf)); | |
70 | if (gsc_i2c_read(GSC_HWMON_ADDR, reg, 1, buf, size)) { | |
71 | puts("fRD\n"); | |
72 | } else { | |
73 | ui = buf[0] | (buf[1]<<8) | (buf[2]<<16); | |
82a17e75 TH |
74 | if (reg == GSC_HWMON_TEMP && ui > 0x8000) |
75 | ui -= 0xffff; | |
59189a8b | 76 | if (ui == 0xffffff) |
16e369f5 | 77 | puts("invalid\n"); |
59189a8b | 78 | else |
16e369f5 | 79 | printf("%d\n", ui); |
59189a8b | 80 | } |
59189a8b TH |
81 | } |
82 | ||
ee5931d4 | 83 | int gsc_info(int verbose) |
59189a8b | 84 | { |
ee5931d4 | 85 | unsigned char buf[16]; |
59189a8b TH |
86 | |
87 | i2c_set_bus_num(0); | |
ee5931d4 TH |
88 | if (gsc_i2c_read(GSC_SC_ADDR, 0, 1, buf, 16)) |
89 | return CMD_RET_FAILURE; | |
90 | ||
91 | printf("GSC: v%d", buf[GSC_SC_FWVER]); | |
92 | printf(" 0x%04x", buf[GSC_SC_FWCRC] | buf[GSC_SC_FWCRC+1]<<8); | |
93 | printf(" WDT:%sabled", (buf[GSC_SC_CTRL1] & (1<<GSC_SC_CTRL1_WDEN)) | |
94 | ? "en" : "dis"); | |
95 | if (buf[GSC_SC_STATUS] & (1 << GSC_SC_IRQ_WATCHDOG)) { | |
96 | buf[GSC_SC_STATUS] &= ~(1 << GSC_SC_IRQ_WATCHDOG); | |
97 | puts(" WDT_RESET"); | |
98 | gsc_i2c_write(GSC_SC_ADDR, GSC_SC_STATUS, 1, | |
99 | &buf[GSC_SC_STATUS], 1); | |
100 | } | |
ca628b74 TH |
101 | if (!gsc_i2c_read(GSC_HWMON_ADDR, GSC_HWMON_TEMP, 1, buf, 2)) { |
102 | int ui = buf[0] | buf[1]<<8; | |
103 | if (ui > 0x8000) | |
104 | ui -= 0xffff; | |
105 | printf(" board temp at %dC", ui / 10); | |
106 | } | |
ee5931d4 TH |
107 | puts("\n"); |
108 | if (!verbose) | |
109 | return CMD_RET_SUCCESS; | |
110 | ||
16e369f5 TH |
111 | read_hwmon("Temp", GSC_HWMON_TEMP, 2); |
112 | read_hwmon("VIN", GSC_HWMON_VIN, 3); | |
113 | read_hwmon("VBATT", GSC_HWMON_VBATT, 3); | |
114 | read_hwmon("VDD_3P3", GSC_HWMON_VDD_3P3, 3); | |
45af3f74 TH |
115 | read_hwmon("VDD_ARM", GSC_HWMON_VDD_CORE, 3); |
116 | read_hwmon("VDD_SOC", GSC_HWMON_VDD_SOC, 3); | |
16e369f5 TH |
117 | read_hwmon("VDD_HIGH", GSC_HWMON_VDD_HIGH, 3); |
118 | read_hwmon("VDD_DDR", GSC_HWMON_VDD_DDR, 3); | |
119 | read_hwmon("VDD_5P0", GSC_HWMON_VDD_5P0, 3); | |
385575bc TH |
120 | if (strncasecmp((const char*) ventana_info.model, "GW553", 5)) |
121 | read_hwmon("VDD_2P5", GSC_HWMON_VDD_2P5, 3); | |
16e369f5 | 122 | read_hwmon("VDD_1P8", GSC_HWMON_VDD_1P8, 3); |
45af3f74 | 123 | read_hwmon("VDD_IO2", GSC_HWMON_VDD_IO2, 3); |
a419352d | 124 | switch (ventana_info.model[3]) { |
59189a8b | 125 | case '1': /* GW51xx */ |
45af3f74 | 126 | read_hwmon("VDD_IO3", GSC_HWMON_VDD_IO4, 3); /* -C rev */ |
59189a8b TH |
127 | break; |
128 | case '2': /* GW52xx */ | |
45af3f74 | 129 | break; |
59189a8b | 130 | case '3': /* GW53xx */ |
45af3f74 TH |
131 | read_hwmon("VDD_IO4", GSC_HWMON_VDD_IO4, 3); /* -C rev */ |
132 | read_hwmon("VDD_GPS", GSC_HWMON_VDD_IO3, 3); | |
59189a8b TH |
133 | break; |
134 | case '4': /* GW54xx */ | |
45af3f74 TH |
135 | read_hwmon("VDD_IO3", GSC_HWMON_VDD_IO4, 3); /* -C rev */ |
136 | read_hwmon("VDD_GPS", GSC_HWMON_VDD_IO3, 3); | |
59189a8b | 137 | break; |
3aa22674 | 138 | case '5': /* GW55xx */ |
3aa22674 | 139 | break; |
94a1d6c6 TH |
140 | case '6': /* GW560x */ |
141 | read_hwmon("VDD_IO4", GSC_HWMON_VDD_IO4, 3); | |
142 | read_hwmon("VDD_GPS", GSC_HWMON_VDD_IO3, 3); | |
143 | break; | |
59189a8b TH |
144 | } |
145 | return 0; | |
146 | } | |
147 | ||
2d833c85 TH |
148 | /* |
149 | * The Gateworks System Controller implements a boot | |
150 | * watchdog (always enabled) as a workaround for IMX6 boot related | |
151 | * errata such as: | |
152 | * ERR005768 - no fix scheduled | |
153 | * ERR006282 - fixed in silicon r1.2 | |
154 | * ERR007117 - fixed in silicon r1.3 | |
155 | * ERR007220 - fixed in silicon r1.3 | |
156 | * ERR007926 - no fix scheduled | |
157 | * see http://cache.freescale.com/files/32bit/doc/errata/IMX6DQCE.pdf | |
158 | * | |
159 | * Disable the boot watchdog | |
160 | */ | |
161 | int gsc_boot_wd_disable(void) | |
162 | { | |
163 | u8 reg; | |
164 | ||
165 | i2c_set_bus_num(CONFIG_I2C_GSC); | |
166 | if (!gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) { | |
167 | reg |= (1 << GSC_SC_CTRL1_WDDIS); | |
168 | if (!gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
169 | return 0; | |
170 | } | |
171 | puts("Error: could not disable GSC Watchdog\n"); | |
172 | return 1; | |
173 | } | |
174 | ||
bf52330a | 175 | #if defined(CONFIG_CMD_GSC) && !defined(CONFIG_SPL_BUILD) |
efa7ed72 TH |
176 | static int do_gsc_sleep(cmd_tbl_t *cmdtp, int flag, int argc, |
177 | char * const argv[]) | |
178 | { | |
179 | unsigned char reg; | |
180 | unsigned long secs = 0; | |
181 | ||
182 | if (argc < 2) | |
183 | return CMD_RET_USAGE; | |
184 | ||
185 | secs = simple_strtoul(argv[1], NULL, 10); | |
186 | printf("GSC Sleeping for %ld seconds\n", secs); | |
187 | ||
188 | i2c_set_bus_num(0); | |
189 | reg = (secs >> 24) & 0xff; | |
190 | if (gsc_i2c_write(GSC_SC_ADDR, 9, 1, ®, 1)) | |
191 | goto error; | |
192 | reg = (secs >> 16) & 0xff; | |
193 | if (gsc_i2c_write(GSC_SC_ADDR, 8, 1, ®, 1)) | |
194 | goto error; | |
195 | reg = (secs >> 8) & 0xff; | |
196 | if (gsc_i2c_write(GSC_SC_ADDR, 7, 1, ®, 1)) | |
197 | goto error; | |
198 | reg = secs & 0xff; | |
199 | if (gsc_i2c_write(GSC_SC_ADDR, 6, 1, ®, 1)) | |
200 | goto error; | |
201 | if (gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
202 | goto error; | |
203 | reg |= (1 << 2); | |
204 | if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
205 | goto error; | |
206 | reg &= ~(1 << 2); | |
207 | reg |= 0x3; | |
208 | if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
209 | goto error; | |
210 | ||
211 | return CMD_RET_SUCCESS; | |
212 | ||
213 | error: | |
214 | printf("i2c error\n"); | |
215 | return CMD_RET_FAILURE; | |
216 | } | |
217 | ||
ee5931d4 TH |
218 | static int do_gsc_wd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
219 | { | |
220 | unsigned char reg; | |
221 | ||
222 | if (argc < 2) | |
223 | return CMD_RET_USAGE; | |
224 | ||
225 | if (strcasecmp(argv[1], "enable") == 0) { | |
226 | int timeout = 0; | |
227 | ||
228 | if (argc > 2) | |
229 | timeout = simple_strtoul(argv[2], NULL, 10); | |
230 | i2c_set_bus_num(0); | |
231 | if (gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
232 | return CMD_RET_FAILURE; | |
233 | reg &= ~((1 << GSC_SC_CTRL1_WDEN) | (1 << GSC_SC_CTRL1_WDTIME)); | |
234 | if (timeout == 60) | |
235 | reg |= (1 << GSC_SC_CTRL1_WDTIME); | |
236 | else | |
237 | timeout = 30; | |
238 | reg |= (1 << GSC_SC_CTRL1_WDEN); | |
239 | if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
240 | return CMD_RET_FAILURE; | |
241 | printf("GSC Watchdog enabled with timeout=%d seconds\n", | |
242 | timeout); | |
243 | } else if (strcasecmp(argv[1], "disable") == 0) { | |
244 | i2c_set_bus_num(0); | |
245 | if (gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
246 | return CMD_RET_FAILURE; | |
247 | reg &= ~((1 << GSC_SC_CTRL1_WDEN) | (1 << GSC_SC_CTRL1_WDTIME)); | |
248 | if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
249 | return CMD_RET_FAILURE; | |
250 | printf("GSC Watchdog disabled\n"); | |
251 | } else { | |
252 | return CMD_RET_USAGE; | |
253 | } | |
254 | return CMD_RET_SUCCESS; | |
255 | } | |
256 | ||
257 | static int do_gsc(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | |
258 | { | |
259 | if (argc < 2) | |
260 | return gsc_info(1); | |
261 | ||
262 | if (strcasecmp(argv[1], "wd") == 0) | |
263 | return do_gsc_wd(cmdtp, flag, --argc, ++argv); | |
efa7ed72 TH |
264 | else if (strcasecmp(argv[1], "sleep") == 0) |
265 | return do_gsc_sleep(cmdtp, flag, --argc, ++argv); | |
ee5931d4 TH |
266 | |
267 | return CMD_RET_USAGE; | |
268 | } | |
269 | ||
270 | U_BOOT_CMD( | |
271 | gsc, 4, 1, do_gsc, "GSC configuration", | |
efa7ed72 | 272 | "[wd enable [30|60]]|[wd disable]|[sleep <secs>]\n" |
ee5931d4 | 273 | ); |
59189a8b TH |
274 | |
275 | #endif /* CONFIG_CMD_GSC */ |