]>
Commit | Line | Data |
---|---|---|
ad2d1639 MF |
1 | /* |
2 | * smc911x_eeprom.c - EEPROM interface to SMC911x parts. | |
3 | * Only tested on SMSC9118 though ... | |
4 | * | |
c44efcf9 | 5 | * Copyright 2004-2009 Analog Devices Inc. |
ad2d1639 MF |
6 | * |
7 | * Licensed under the GPL-2 or later. | |
8 | * | |
9 | * Based on smc91111_eeprom.c which: | |
10 | * Heavily borrowed from the following peoples GPL'ed software: | |
11 | * - Wolfgang Denk, DENX Software Engineering, wd@denx.de | |
a187559e | 12 | * Das U-Boot |
ad2d1639 MF |
13 | * - Ladislav Michl ladis@linux-mips.org |
14 | * A rejected patch on the U-Boot mailing list | |
15 | */ | |
16 | ||
17 | #include <common.h> | |
24b852a7 | 18 | #include <console.h> |
ad2d1639 | 19 | #include <exports.h> |
90526e9f | 20 | #include <net.h> |
4d91a6ec | 21 | #include <linux/ctype.h> |
eb46efa3 | 22 | #include <linux/types.h> |
ad2d1639 MF |
23 | #include "../drivers/net/smc911x.h" |
24 | ||
eb46efa3 MV |
25 | #define DRIVERNAME "smc911x" |
26 | ||
27 | #if defined (CONFIG_SMC911X_32_BIT) && \ | |
28 | defined (CONFIG_SMC911X_16_BIT) | |
29 | #error "SMC911X: Only one of CONFIG_SMC911X_32_BIT and \ | |
30 | CONFIG_SMC911X_16_BIT shall be set" | |
31 | #endif | |
32 | ||
33 | struct chip_id { | |
34 | u16 id; | |
35 | char *name; | |
36 | }; | |
37 | ||
38 | static const struct chip_id chip_ids[] = { | |
39 | { CHIP_89218, "LAN89218" }, | |
40 | { CHIP_9115, "LAN9115" }, | |
41 | { CHIP_9116, "LAN9116" }, | |
42 | { CHIP_9117, "LAN9117" }, | |
43 | { CHIP_9118, "LAN9118" }, | |
44 | { CHIP_9211, "LAN9211" }, | |
45 | { CHIP_9215, "LAN9215" }, | |
46 | { CHIP_9216, "LAN9216" }, | |
47 | { CHIP_9217, "LAN9217" }, | |
48 | { CHIP_9218, "LAN9218" }, | |
49 | { CHIP_9220, "LAN9220" }, | |
50 | { CHIP_9221, "LAN9221" }, | |
51 | { 0, NULL }, | |
52 | }; | |
53 | ||
54 | #if defined (CONFIG_SMC911X_32_BIT) | |
f0d73f5c | 55 | static u32 smc911x_reg_read(struct eth_device *dev, u32 offset) |
eb46efa3 MV |
56 | { |
57 | return *(volatile u32*)(dev->iobase + offset); | |
58 | } | |
eb46efa3 | 59 | |
f0d73f5c | 60 | static void smc911x_reg_write(struct eth_device *dev, u32 offset, u32 val) |
eb46efa3 MV |
61 | { |
62 | *(volatile u32*)(dev->iobase + offset) = val; | |
63 | } | |
eb46efa3 | 64 | #elif defined (CONFIG_SMC911X_16_BIT) |
f0d73f5c | 65 | static u32 smc911x_reg_read(struct eth_device *dev, u32 offset) |
eb46efa3 MV |
66 | { |
67 | volatile u16 *addr_16 = (u16 *)(dev->iobase + offset); | |
f0d73f5c | 68 | return (*addr_16 & 0x0000ffff) | (*(addr_16 + 1) << 16); |
eb46efa3 | 69 | } |
f0d73f5c | 70 | static void smc911x_reg_write(struct eth_device *dev, u32 offset, u32 val) |
eb46efa3 MV |
71 | { |
72 | *(volatile u16 *)(dev->iobase + offset) = (u16)val; | |
73 | *(volatile u16 *)(dev->iobase + offset + 2) = (u16)(val >> 16); | |
74 | } | |
75 | #else | |
76 | #error "SMC911X: undefined bus width" | |
77 | #endif /* CONFIG_SMC911X_16_BIT */ | |
78 | ||
79 | static u32 smc911x_get_mac_csr(struct eth_device *dev, u8 reg) | |
80 | { | |
81 | while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) | |
82 | ; | |
83 | smc911x_reg_write(dev, MAC_CSR_CMD, | |
84 | MAC_CSR_CMD_CSR_BUSY | MAC_CSR_CMD_R_NOT_W | reg); | |
85 | while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) | |
86 | ; | |
87 | ||
88 | return smc911x_reg_read(dev, MAC_CSR_DATA); | |
89 | } | |
90 | ||
91 | static void smc911x_set_mac_csr(struct eth_device *dev, u8 reg, u32 data) | |
92 | { | |
93 | while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) | |
94 | ; | |
95 | smc911x_reg_write(dev, MAC_CSR_DATA, data); | |
96 | smc911x_reg_write(dev, MAC_CSR_CMD, MAC_CSR_CMD_CSR_BUSY | reg); | |
97 | while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) | |
98 | ; | |
99 | } | |
100 | ||
101 | static int smc911x_detect_chip(struct eth_device *dev) | |
102 | { | |
103 | unsigned long val, i; | |
104 | ||
105 | val = smc911x_reg_read(dev, BYTE_TEST); | |
106 | if (val == 0xffffffff) { | |
107 | /* Special case -- no chip present */ | |
108 | return -1; | |
109 | } else if (val != 0x87654321) { | |
110 | printf(DRIVERNAME ": Invalid chip endian 0x%08lx\n", val); | |
111 | return -1; | |
112 | } | |
113 | ||
114 | val = smc911x_reg_read(dev, ID_REV) >> 16; | |
115 | for (i = 0; chip_ids[i].id != 0; i++) { | |
116 | if (chip_ids[i].id == val) break; | |
117 | } | |
118 | if (!chip_ids[i].id) { | |
119 | printf(DRIVERNAME ": Unknown chip ID %04lx\n", val); | |
120 | return -1; | |
121 | } | |
122 | ||
123 | dev->priv = (void *)&chip_ids[i]; | |
124 | ||
125 | return 0; | |
126 | } | |
127 | ||
128 | static void smc911x_reset(struct eth_device *dev) | |
129 | { | |
130 | int timeout; | |
131 | ||
132 | /* | |
133 | * Take out of PM setting first | |
134 | * Device is already wake up if PMT_CTRL_READY bit is set | |
135 | */ | |
136 | if ((smc911x_reg_read(dev, PMT_CTRL) & PMT_CTRL_READY) == 0) { | |
137 | /* Write to the bytetest will take out of powerdown */ | |
138 | smc911x_reg_write(dev, BYTE_TEST, 0x0); | |
139 | ||
140 | timeout = 10; | |
141 | ||
142 | while (timeout-- && | |
143 | !(smc911x_reg_read(dev, PMT_CTRL) & PMT_CTRL_READY)) | |
144 | udelay(10); | |
145 | if (timeout < 0) { | |
146 | printf(DRIVERNAME | |
147 | ": timeout waiting for PM restore\n"); | |
148 | return; | |
149 | } | |
150 | } | |
151 | ||
152 | /* Disable interrupts */ | |
153 | smc911x_reg_write(dev, INT_EN, 0); | |
154 | ||
155 | smc911x_reg_write(dev, HW_CFG, HW_CFG_SRST); | |
156 | ||
157 | timeout = 1000; | |
158 | while (timeout-- && smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) | |
159 | udelay(10); | |
160 | ||
161 | if (timeout < 0) { | |
162 | printf(DRIVERNAME ": reset timeout\n"); | |
163 | return; | |
164 | } | |
165 | ||
166 | /* Reset the FIFO level and flow control settings */ | |
167 | smc911x_set_mac_csr(dev, FLOW, FLOW_FCPT | FLOW_FCEN); | |
168 | smc911x_reg_write(dev, AFC_CFG, 0x0050287F); | |
169 | ||
170 | /* Set to LED outputs */ | |
171 | smc911x_reg_write(dev, GPIO_CFG, 0x70070000); | |
172 | } | |
173 | ||
ad2d1639 MF |
174 | /** |
175 | * smsc_ctrlc - detect press of CTRL+C (common ctrlc() isnt exported!?) | |
176 | */ | |
177 | static int smsc_ctrlc(void) | |
178 | { | |
179 | return (tstc() && getc() == 0x03); | |
180 | } | |
181 | ||
182 | /** | |
183 | * usage - dump usage information | |
184 | */ | |
185 | static void usage(void) | |
186 | { | |
187 | puts( | |
188 | "MAC/EEPROM Commands:\n" | |
189 | " P : Print the MAC addresses\n" | |
190 | " D : Dump the EEPROM contents\n" | |
191 | " M : Dump the MAC contents\n" | |
192 | " C : Copy the MAC address from the EEPROM to the MAC\n" | |
193 | " W : Write a register in the EEPROM or in the MAC\n" | |
194 | " Q : Quit\n" | |
195 | "\n" | |
196 | "Some commands take arguments:\n" | |
197 | " W <E|M> <register> <value>\n" | |
198 | " E: EEPROM M: MAC\n" | |
199 | ); | |
200 | } | |
201 | ||
202 | /** | |
203 | * dump_regs - dump the MAC registers | |
204 | * | |
205 | * Registers 0x00 - 0x50 are FIFOs. The 0x50+ are the control registers | |
206 | * and they're all 32bits long. 0xB8+ are reserved, so don't bother. | |
207 | */ | |
c44efcf9 | 208 | static void dump_regs(struct eth_device *dev) |
ad2d1639 MF |
209 | { |
210 | u8 i, j = 0; | |
211 | for (i = 0x50; i < 0xB8; i += sizeof(u32)) | |
069f4364 | 212 | printf("%02x: 0x%08x %c", i, |
c44efcf9 | 213 | smc911x_reg_read(dev, i), |
ad2d1639 MF |
214 | (j++ % 2 ? '\n' : ' ')); |
215 | } | |
216 | ||
217 | /** | |
218 | * do_eeprom_cmd - handle eeprom communication | |
219 | */ | |
c44efcf9 | 220 | static int do_eeprom_cmd(struct eth_device *dev, int cmd, u8 reg) |
ad2d1639 | 221 | { |
c44efcf9 | 222 | if (smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) { |
ad2d1639 | 223 | printf("eeprom_cmd: busy at start (E2P_CMD = 0x%08x)\n", |
c44efcf9 | 224 | smc911x_reg_read(dev, E2P_CMD)); |
ad2d1639 MF |
225 | return -1; |
226 | } | |
227 | ||
c44efcf9 | 228 | smc911x_reg_write(dev, E2P_CMD, E2P_CMD_EPC_BUSY | cmd | reg); |
ad2d1639 | 229 | |
c44efcf9 | 230 | while (smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) |
ad2d1639 MF |
231 | if (smsc_ctrlc()) { |
232 | printf("eeprom_cmd: timeout (E2P_CMD = 0x%08x)\n", | |
c44efcf9 | 233 | smc911x_reg_read(dev, E2P_CMD)); |
ad2d1639 MF |
234 | return -1; |
235 | } | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
240 | /** | |
241 | * read_eeprom_reg - read specified register in EEPROM | |
242 | */ | |
c44efcf9 | 243 | static u8 read_eeprom_reg(struct eth_device *dev, u8 reg) |
ad2d1639 | 244 | { |
c44efcf9 MF |
245 | int ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_READ, reg); |
246 | return (ret ? : smc911x_reg_read(dev, E2P_DATA)); | |
ad2d1639 MF |
247 | } |
248 | ||
249 | /** | |
250 | * write_eeprom_reg - write specified value into specified register in EEPROM | |
251 | */ | |
c44efcf9 | 252 | static int write_eeprom_reg(struct eth_device *dev, u8 value, u8 reg) |
ad2d1639 MF |
253 | { |
254 | int ret; | |
255 | ||
256 | /* enable erasing/writing */ | |
c44efcf9 | 257 | ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_EWEN, reg); |
ad2d1639 MF |
258 | if (ret) |
259 | goto done; | |
260 | ||
261 | /* erase the eeprom reg */ | |
c44efcf9 | 262 | ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_ERASE, reg); |
ad2d1639 MF |
263 | if (ret) |
264 | goto done; | |
265 | ||
266 | /* write the eeprom reg */ | |
c44efcf9 MF |
267 | smc911x_reg_write(dev, E2P_DATA, value); |
268 | ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_WRITE, reg); | |
ad2d1639 MF |
269 | if (ret) |
270 | goto done; | |
271 | ||
272 | /* disable erasing/writing */ | |
c44efcf9 | 273 | ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_EWDS, reg); |
ad2d1639 MF |
274 | |
275 | done: | |
276 | return ret; | |
277 | } | |
278 | ||
279 | /** | |
280 | * skip_space - find first non-whitespace in given pointer | |
281 | */ | |
282 | static char *skip_space(char *buf) | |
283 | { | |
4d91a6ec | 284 | while (isblank(buf[0])) |
ad2d1639 MF |
285 | ++buf; |
286 | return buf; | |
287 | } | |
288 | ||
289 | /** | |
290 | * write_stuff - handle writing of MAC registers / eeprom | |
291 | */ | |
c44efcf9 | 292 | static void write_stuff(struct eth_device *dev, char *line) |
ad2d1639 MF |
293 | { |
294 | char dest; | |
295 | char *endp; | |
296 | u8 reg; | |
297 | u32 value; | |
298 | ||
299 | /* Skip over the "W " part of the command */ | |
300 | line = skip_space(line + 1); | |
301 | ||
302 | /* Figure out destination */ | |
303 | switch (line[0]) { | |
304 | case 'E': | |
305 | case 'M': | |
306 | dest = line[0]; | |
307 | break; | |
308 | default: | |
309 | invalid_usage: | |
310 | printf("ERROR: Invalid write usage\n"); | |
311 | usage(); | |
312 | return; | |
313 | } | |
314 | ||
315 | /* Get the register to write */ | |
316 | line = skip_space(line + 1); | |
317 | reg = simple_strtoul(line, &endp, 16); | |
318 | if (line == endp) | |
319 | goto invalid_usage; | |
320 | ||
321 | /* Get the value to write */ | |
322 | line = skip_space(endp); | |
323 | value = simple_strtoul(line, &endp, 16); | |
324 | if (line == endp) | |
325 | goto invalid_usage; | |
326 | ||
327 | /* Check for trailing cruft */ | |
328 | line = skip_space(endp); | |
329 | if (line[0]) | |
330 | goto invalid_usage; | |
331 | ||
332 | /* Finally, execute the command */ | |
333 | if (dest == 'E') { | |
334 | printf("Writing EEPROM register %02x with %02x\n", reg, value); | |
c44efcf9 | 335 | write_eeprom_reg(dev, value, reg); |
ad2d1639 MF |
336 | } else { |
337 | printf("Writing MAC register %02x with %08x\n", reg, value); | |
c44efcf9 | 338 | smc911x_reg_write(dev, reg, value); |
ad2d1639 MF |
339 | } |
340 | } | |
341 | ||
342 | /** | |
343 | * copy_from_eeprom - copy MAC address in eeprom to address registers | |
344 | */ | |
c44efcf9 | 345 | static void copy_from_eeprom(struct eth_device *dev) |
ad2d1639 MF |
346 | { |
347 | ulong addrl = | |
c44efcf9 MF |
348 | read_eeprom_reg(dev, 0x01) | |
349 | read_eeprom_reg(dev, 0x02) << 8 | | |
350 | read_eeprom_reg(dev, 0x03) << 16 | | |
351 | read_eeprom_reg(dev, 0x04) << 24; | |
ad2d1639 | 352 | ulong addrh = |
c44efcf9 MF |
353 | read_eeprom_reg(dev, 0x05) | |
354 | read_eeprom_reg(dev, 0x06) << 8; | |
355 | smc911x_set_mac_csr(dev, ADDRL, addrl); | |
356 | smc911x_set_mac_csr(dev, ADDRH, addrh); | |
ad2d1639 MF |
357 | puts("EEPROM contents copied to MAC\n"); |
358 | } | |
359 | ||
360 | /** | |
361 | * print_macaddr - print MAC address registers and MAC address in eeprom | |
362 | */ | |
c44efcf9 | 363 | static void print_macaddr(struct eth_device *dev) |
ad2d1639 MF |
364 | { |
365 | puts("Current MAC Address in MAC: "); | |
c44efcf9 MF |
366 | ulong addrl = smc911x_get_mac_csr(dev, ADDRL); |
367 | ulong addrh = smc911x_get_mac_csr(dev, ADDRH); | |
ad2d1639 MF |
368 | printf("%02x:%02x:%02x:%02x:%02x:%02x\n", |
369 | (u8)(addrl), (u8)(addrl >> 8), (u8)(addrl >> 16), | |
370 | (u8)(addrl >> 24), (u8)(addrh), (u8)(addrh >> 8)); | |
371 | ||
372 | puts("Current MAC Address in EEPROM: "); | |
373 | int i; | |
374 | for (i = 1; i < 6; ++i) | |
c44efcf9 MF |
375 | printf("%02x:", read_eeprom_reg(dev, i)); |
376 | printf("%02x\n", read_eeprom_reg(dev, i)); | |
ad2d1639 MF |
377 | } |
378 | ||
379 | /** | |
380 | * dump_eeprom - dump the whole content of the EEPROM | |
381 | */ | |
c44efcf9 | 382 | static void dump_eeprom(struct eth_device *dev) |
ad2d1639 MF |
383 | { |
384 | int i; | |
385 | puts("EEPROM:\n"); | |
386 | for (i = 0; i < 7; ++i) | |
c44efcf9 | 387 | printf("%02x: 0x%02x\n", i, read_eeprom_reg(dev, i)); |
ad2d1639 MF |
388 | } |
389 | ||
390 | /** | |
391 | * smc911x_init - get the MAC/EEPROM up and ready for use | |
392 | */ | |
c44efcf9 | 393 | static int smc911x_init(struct eth_device *dev) |
ad2d1639 MF |
394 | { |
395 | /* See if there is anything there */ | |
aa9fba53 | 396 | if (smc911x_detect_chip(dev)) |
ad2d1639 MF |
397 | return 1; |
398 | ||
c44efcf9 | 399 | smc911x_reset(dev); |
ad2d1639 MF |
400 | |
401 | /* Make sure we set EEDIO/EECLK to the EEPROM */ | |
c44efcf9 MF |
402 | if (smc911x_reg_read(dev, GPIO_CFG) & GPIO_CFG_EEPR_EN) { |
403 | while (smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) | |
ad2d1639 | 404 | if (smsc_ctrlc()) { |
069f4364 | 405 | printf("init: timeout (E2P_CMD = 0x%08x)\n", |
c44efcf9 | 406 | smc911x_reg_read(dev, E2P_CMD)); |
ad2d1639 MF |
407 | return 1; |
408 | } | |
c44efcf9 MF |
409 | smc911x_reg_write(dev, GPIO_CFG, |
410 | smc911x_reg_read(dev, GPIO_CFG) & ~GPIO_CFG_EEPR_EN); | |
ad2d1639 MF |
411 | } |
412 | ||
413 | return 0; | |
414 | } | |
415 | ||
416 | /** | |
417 | * getline - consume a line of input and handle some escape sequences | |
418 | */ | |
419 | static char *getline(void) | |
420 | { | |
421 | static char buffer[100]; | |
422 | char c; | |
423 | size_t i; | |
424 | ||
425 | i = 0; | |
426 | while (1) { | |
427 | buffer[i] = '\0'; | |
428 | while (!tstc()) | |
429 | continue; | |
430 | ||
431 | c = getc(); | |
432 | /* Convert to uppercase */ | |
433 | if (c >= 'a' && c <= 'z') | |
434 | c -= ('a' - 'A'); | |
435 | ||
436 | switch (c) { | |
437 | case '\r': /* Enter/Return key */ | |
438 | case '\n': | |
439 | puts("\n"); | |
440 | return buffer; | |
441 | ||
442 | case 0x03: /* ^C - break */ | |
443 | return NULL; | |
444 | ||
445 | case 0x5F: | |
446 | case 0x08: /* ^H - backspace */ | |
447 | case 0x7F: /* DEL - backspace */ | |
448 | if (i) { | |
449 | puts("\b \b"); | |
450 | i--; | |
451 | } | |
452 | break; | |
453 | ||
454 | default: | |
455 | /* Ignore control characters */ | |
456 | if (c < 0x20) | |
457 | break; | |
458 | /* Queue up all other characters */ | |
459 | buffer[i++] = c; | |
460 | printf("%c", c); | |
461 | break; | |
462 | } | |
463 | } | |
464 | } | |
465 | ||
466 | /** | |
467 | * smc911x_eeprom - our application's main() function | |
468 | */ | |
09140113 | 469 | int smc911x_eeprom(int argc, char *const argv[]) |
ad2d1639 | 470 | { |
c44efcf9 MF |
471 | /* Avoid initializing on stack as gcc likes to call memset() */ |
472 | struct eth_device dev; | |
c44efcf9 MF |
473 | dev.iobase = CONFIG_SMC911X_BASE; |
474 | ||
ad2d1639 MF |
475 | /* Print the ABI version */ |
476 | app_startup(argv); | |
477 | if (XF_VERSION != get_version()) { | |
478 | printf("Expects ABI version %d\n", XF_VERSION); | |
479 | printf("Actual U-Boot ABI version %lu\n", get_version()); | |
480 | printf("Can't run\n\n"); | |
481 | return 1; | |
482 | } | |
483 | ||
484 | /* Initialize the MAC/EEPROM somewhat */ | |
485 | puts("\n"); | |
c44efcf9 | 486 | if (smc911x_init(&dev)) |
ad2d1639 MF |
487 | return 1; |
488 | ||
489 | /* Dump helpful usage information */ | |
490 | puts("\n"); | |
491 | usage(); | |
492 | puts("\n"); | |
493 | ||
494 | while (1) { | |
495 | char *line; | |
496 | ||
497 | /* Send the prompt and wait for a line */ | |
498 | puts("eeprom> "); | |
499 | line = getline(); | |
500 | ||
501 | /* Got a ctrl+c */ | |
502 | if (!line) | |
503 | return 0; | |
504 | ||
505 | /* Eat leading space */ | |
506 | line = skip_space(line); | |
507 | ||
508 | /* Empty line, try again */ | |
509 | if (!line[0]) | |
510 | continue; | |
511 | ||
512 | /* Only accept 1 letter commands */ | |
4d91a6ec | 513 | if (line[0] && line[1] && !isblank(line[1])) |
ad2d1639 MF |
514 | goto unknown_cmd; |
515 | ||
516 | /* Now parse the command */ | |
517 | switch (line[0]) { | |
c44efcf9 MF |
518 | case 'W': write_stuff(&dev, line); break; |
519 | case 'D': dump_eeprom(&dev); break; | |
520 | case 'M': dump_regs(&dev); break; | |
521 | case 'C': copy_from_eeprom(&dev); break; | |
522 | case 'P': print_macaddr(&dev); break; | |
ad2d1639 MF |
523 | unknown_cmd: |
524 | default: puts("ERROR: Unknown command!\n\n"); | |
525 | case '?': | |
526 | case 'H': usage(); break; | |
527 | case 'Q': return 0; | |
528 | } | |
529 | } | |
530 | } |