]>
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 | ||
9 | #include <asm/errno.h> | |
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 | } | |
101 | puts("\n"); | |
102 | if (!verbose) | |
103 | return CMD_RET_SUCCESS; | |
104 | ||
16e369f5 TH |
105 | read_hwmon("Temp", GSC_HWMON_TEMP, 2); |
106 | read_hwmon("VIN", GSC_HWMON_VIN, 3); | |
107 | read_hwmon("VBATT", GSC_HWMON_VBATT, 3); | |
108 | read_hwmon("VDD_3P3", GSC_HWMON_VDD_3P3, 3); | |
45af3f74 TH |
109 | read_hwmon("VDD_ARM", GSC_HWMON_VDD_CORE, 3); |
110 | read_hwmon("VDD_SOC", GSC_HWMON_VDD_SOC, 3); | |
16e369f5 TH |
111 | read_hwmon("VDD_HIGH", GSC_HWMON_VDD_HIGH, 3); |
112 | read_hwmon("VDD_DDR", GSC_HWMON_VDD_DDR, 3); | |
113 | read_hwmon("VDD_5P0", GSC_HWMON_VDD_5P0, 3); | |
114 | read_hwmon("VDD_2P5", GSC_HWMON_VDD_2P5, 3); | |
115 | read_hwmon("VDD_1P8", GSC_HWMON_VDD_1P8, 3); | |
45af3f74 | 116 | read_hwmon("VDD_IO2", GSC_HWMON_VDD_IO2, 3); |
a419352d | 117 | switch (ventana_info.model[3]) { |
59189a8b | 118 | case '1': /* GW51xx */ |
45af3f74 | 119 | read_hwmon("VDD_IO3", GSC_HWMON_VDD_IO4, 3); /* -C rev */ |
59189a8b TH |
120 | break; |
121 | case '2': /* GW52xx */ | |
45af3f74 | 122 | break; |
59189a8b | 123 | case '3': /* GW53xx */ |
45af3f74 TH |
124 | read_hwmon("VDD_IO4", GSC_HWMON_VDD_IO4, 3); /* -C rev */ |
125 | read_hwmon("VDD_GPS", GSC_HWMON_VDD_IO3, 3); | |
59189a8b TH |
126 | break; |
127 | case '4': /* GW54xx */ | |
45af3f74 TH |
128 | read_hwmon("VDD_IO3", GSC_HWMON_VDD_IO4, 3); /* -C rev */ |
129 | read_hwmon("VDD_GPS", GSC_HWMON_VDD_IO3, 3); | |
59189a8b | 130 | break; |
3aa22674 | 131 | case '5': /* GW55xx */ |
3aa22674 | 132 | break; |
59189a8b TH |
133 | } |
134 | return 0; | |
135 | } | |
136 | ||
2d833c85 TH |
137 | /* |
138 | * The Gateworks System Controller implements a boot | |
139 | * watchdog (always enabled) as a workaround for IMX6 boot related | |
140 | * errata such as: | |
141 | * ERR005768 - no fix scheduled | |
142 | * ERR006282 - fixed in silicon r1.2 | |
143 | * ERR007117 - fixed in silicon r1.3 | |
144 | * ERR007220 - fixed in silicon r1.3 | |
145 | * ERR007926 - no fix scheduled | |
146 | * see http://cache.freescale.com/files/32bit/doc/errata/IMX6DQCE.pdf | |
147 | * | |
148 | * Disable the boot watchdog | |
149 | */ | |
150 | int gsc_boot_wd_disable(void) | |
151 | { | |
152 | u8 reg; | |
153 | ||
154 | i2c_set_bus_num(CONFIG_I2C_GSC); | |
155 | if (!gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) { | |
156 | reg |= (1 << GSC_SC_CTRL1_WDDIS); | |
157 | if (!gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
158 | return 0; | |
159 | } | |
160 | puts("Error: could not disable GSC Watchdog\n"); | |
161 | return 1; | |
162 | } | |
163 | ||
ee5931d4 | 164 | #ifdef CONFIG_CMD_GSC |
efa7ed72 TH |
165 | static int do_gsc_sleep(cmd_tbl_t *cmdtp, int flag, int argc, |
166 | char * const argv[]) | |
167 | { | |
168 | unsigned char reg; | |
169 | unsigned long secs = 0; | |
170 | ||
171 | if (argc < 2) | |
172 | return CMD_RET_USAGE; | |
173 | ||
174 | secs = simple_strtoul(argv[1], NULL, 10); | |
175 | printf("GSC Sleeping for %ld seconds\n", secs); | |
176 | ||
177 | i2c_set_bus_num(0); | |
178 | reg = (secs >> 24) & 0xff; | |
179 | if (gsc_i2c_write(GSC_SC_ADDR, 9, 1, ®, 1)) | |
180 | goto error; | |
181 | reg = (secs >> 16) & 0xff; | |
182 | if (gsc_i2c_write(GSC_SC_ADDR, 8, 1, ®, 1)) | |
183 | goto error; | |
184 | reg = (secs >> 8) & 0xff; | |
185 | if (gsc_i2c_write(GSC_SC_ADDR, 7, 1, ®, 1)) | |
186 | goto error; | |
187 | reg = secs & 0xff; | |
188 | if (gsc_i2c_write(GSC_SC_ADDR, 6, 1, ®, 1)) | |
189 | goto error; | |
190 | if (gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
191 | goto error; | |
192 | reg |= (1 << 2); | |
193 | if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
194 | goto error; | |
195 | reg &= ~(1 << 2); | |
196 | reg |= 0x3; | |
197 | if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
198 | goto error; | |
199 | ||
200 | return CMD_RET_SUCCESS; | |
201 | ||
202 | error: | |
203 | printf("i2c error\n"); | |
204 | return CMD_RET_FAILURE; | |
205 | } | |
206 | ||
ee5931d4 TH |
207 | static int do_gsc_wd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
208 | { | |
209 | unsigned char reg; | |
210 | ||
211 | if (argc < 2) | |
212 | return CMD_RET_USAGE; | |
213 | ||
214 | if (strcasecmp(argv[1], "enable") == 0) { | |
215 | int timeout = 0; | |
216 | ||
217 | if (argc > 2) | |
218 | timeout = simple_strtoul(argv[2], NULL, 10); | |
219 | i2c_set_bus_num(0); | |
220 | if (gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
221 | return CMD_RET_FAILURE; | |
222 | reg &= ~((1 << GSC_SC_CTRL1_WDEN) | (1 << GSC_SC_CTRL1_WDTIME)); | |
223 | if (timeout == 60) | |
224 | reg |= (1 << GSC_SC_CTRL1_WDTIME); | |
225 | else | |
226 | timeout = 30; | |
227 | reg |= (1 << GSC_SC_CTRL1_WDEN); | |
228 | if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
229 | return CMD_RET_FAILURE; | |
230 | printf("GSC Watchdog enabled with timeout=%d seconds\n", | |
231 | timeout); | |
232 | } else if (strcasecmp(argv[1], "disable") == 0) { | |
233 | i2c_set_bus_num(0); | |
234 | if (gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
235 | return CMD_RET_FAILURE; | |
236 | reg &= ~((1 << GSC_SC_CTRL1_WDEN) | (1 << GSC_SC_CTRL1_WDTIME)); | |
237 | if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) | |
238 | return CMD_RET_FAILURE; | |
239 | printf("GSC Watchdog disabled\n"); | |
240 | } else { | |
241 | return CMD_RET_USAGE; | |
242 | } | |
243 | return CMD_RET_SUCCESS; | |
244 | } | |
245 | ||
246 | static int do_gsc(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | |
247 | { | |
248 | if (argc < 2) | |
249 | return gsc_info(1); | |
250 | ||
251 | if (strcasecmp(argv[1], "wd") == 0) | |
252 | return do_gsc_wd(cmdtp, flag, --argc, ++argv); | |
efa7ed72 TH |
253 | else if (strcasecmp(argv[1], "sleep") == 0) |
254 | return do_gsc_sleep(cmdtp, flag, --argc, ++argv); | |
ee5931d4 TH |
255 | |
256 | return CMD_RET_USAGE; | |
257 | } | |
258 | ||
259 | U_BOOT_CMD( | |
260 | gsc, 4, 1, do_gsc, "GSC configuration", | |
efa7ed72 | 261 | "[wd enable [30|60]]|[wd disable]|[sleep <secs>]\n" |
ee5931d4 | 262 | ); |
59189a8b TH |
263 | |
264 | #endif /* CONFIG_CMD_GSC */ |