]>
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, [email protected] | |
12 | * Das U-boot | |
13 | * - Ladislav Michl [email protected] | |
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> |
4d91a6ec | 20 | #include <linux/ctype.h> |
ad2d1639 MF |
21 | #include "../drivers/net/smc911x.h" |
22 | ||
23 | /** | |
24 | * smsc_ctrlc - detect press of CTRL+C (common ctrlc() isnt exported!?) | |
25 | */ | |
26 | static int smsc_ctrlc(void) | |
27 | { | |
28 | return (tstc() && getc() == 0x03); | |
29 | } | |
30 | ||
31 | /** | |
32 | * usage - dump usage information | |
33 | */ | |
34 | static void usage(void) | |
35 | { | |
36 | puts( | |
37 | "MAC/EEPROM Commands:\n" | |
38 | " P : Print the MAC addresses\n" | |
39 | " D : Dump the EEPROM contents\n" | |
40 | " M : Dump the MAC contents\n" | |
41 | " C : Copy the MAC address from the EEPROM to the MAC\n" | |
42 | " W : Write a register in the EEPROM or in the MAC\n" | |
43 | " Q : Quit\n" | |
44 | "\n" | |
45 | "Some commands take arguments:\n" | |
46 | " W <E|M> <register> <value>\n" | |
47 | " E: EEPROM M: MAC\n" | |
48 | ); | |
49 | } | |
50 | ||
51 | /** | |
52 | * dump_regs - dump the MAC registers | |
53 | * | |
54 | * Registers 0x00 - 0x50 are FIFOs. The 0x50+ are the control registers | |
55 | * and they're all 32bits long. 0xB8+ are reserved, so don't bother. | |
56 | */ | |
c44efcf9 | 57 | static void dump_regs(struct eth_device *dev) |
ad2d1639 MF |
58 | { |
59 | u8 i, j = 0; | |
60 | for (i = 0x50; i < 0xB8; i += sizeof(u32)) | |
069f4364 | 61 | printf("%02x: 0x%08x %c", i, |
c44efcf9 | 62 | smc911x_reg_read(dev, i), |
ad2d1639 MF |
63 | (j++ % 2 ? '\n' : ' ')); |
64 | } | |
65 | ||
66 | /** | |
67 | * do_eeprom_cmd - handle eeprom communication | |
68 | */ | |
c44efcf9 | 69 | static int do_eeprom_cmd(struct eth_device *dev, int cmd, u8 reg) |
ad2d1639 | 70 | { |
c44efcf9 | 71 | if (smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) { |
ad2d1639 | 72 | printf("eeprom_cmd: busy at start (E2P_CMD = 0x%08x)\n", |
c44efcf9 | 73 | smc911x_reg_read(dev, E2P_CMD)); |
ad2d1639 MF |
74 | return -1; |
75 | } | |
76 | ||
c44efcf9 | 77 | smc911x_reg_write(dev, E2P_CMD, E2P_CMD_EPC_BUSY | cmd | reg); |
ad2d1639 | 78 | |
c44efcf9 | 79 | while (smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) |
ad2d1639 MF |
80 | if (smsc_ctrlc()) { |
81 | printf("eeprom_cmd: timeout (E2P_CMD = 0x%08x)\n", | |
c44efcf9 | 82 | smc911x_reg_read(dev, E2P_CMD)); |
ad2d1639 MF |
83 | return -1; |
84 | } | |
85 | ||
86 | return 0; | |
87 | } | |
88 | ||
89 | /** | |
90 | * read_eeprom_reg - read specified register in EEPROM | |
91 | */ | |
c44efcf9 | 92 | static u8 read_eeprom_reg(struct eth_device *dev, u8 reg) |
ad2d1639 | 93 | { |
c44efcf9 MF |
94 | int ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_READ, reg); |
95 | return (ret ? : smc911x_reg_read(dev, E2P_DATA)); | |
ad2d1639 MF |
96 | } |
97 | ||
98 | /** | |
99 | * write_eeprom_reg - write specified value into specified register in EEPROM | |
100 | */ | |
c44efcf9 | 101 | static int write_eeprom_reg(struct eth_device *dev, u8 value, u8 reg) |
ad2d1639 MF |
102 | { |
103 | int ret; | |
104 | ||
105 | /* enable erasing/writing */ | |
c44efcf9 | 106 | ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_EWEN, reg); |
ad2d1639 MF |
107 | if (ret) |
108 | goto done; | |
109 | ||
110 | /* erase the eeprom reg */ | |
c44efcf9 | 111 | ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_ERASE, reg); |
ad2d1639 MF |
112 | if (ret) |
113 | goto done; | |
114 | ||
115 | /* write the eeprom reg */ | |
c44efcf9 MF |
116 | smc911x_reg_write(dev, E2P_DATA, value); |
117 | ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_WRITE, reg); | |
ad2d1639 MF |
118 | if (ret) |
119 | goto done; | |
120 | ||
121 | /* disable erasing/writing */ | |
c44efcf9 | 122 | ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_EWDS, reg); |
ad2d1639 MF |
123 | |
124 | done: | |
125 | return ret; | |
126 | } | |
127 | ||
128 | /** | |
129 | * skip_space - find first non-whitespace in given pointer | |
130 | */ | |
131 | static char *skip_space(char *buf) | |
132 | { | |
4d91a6ec | 133 | while (isblank(buf[0])) |
ad2d1639 MF |
134 | ++buf; |
135 | return buf; | |
136 | } | |
137 | ||
138 | /** | |
139 | * write_stuff - handle writing of MAC registers / eeprom | |
140 | */ | |
c44efcf9 | 141 | static void write_stuff(struct eth_device *dev, char *line) |
ad2d1639 MF |
142 | { |
143 | char dest; | |
144 | char *endp; | |
145 | u8 reg; | |
146 | u32 value; | |
147 | ||
148 | /* Skip over the "W " part of the command */ | |
149 | line = skip_space(line + 1); | |
150 | ||
151 | /* Figure out destination */ | |
152 | switch (line[0]) { | |
153 | case 'E': | |
154 | case 'M': | |
155 | dest = line[0]; | |
156 | break; | |
157 | default: | |
158 | invalid_usage: | |
159 | printf("ERROR: Invalid write usage\n"); | |
160 | usage(); | |
161 | return; | |
162 | } | |
163 | ||
164 | /* Get the register to write */ | |
165 | line = skip_space(line + 1); | |
166 | reg = simple_strtoul(line, &endp, 16); | |
167 | if (line == endp) | |
168 | goto invalid_usage; | |
169 | ||
170 | /* Get the value to write */ | |
171 | line = skip_space(endp); | |
172 | value = simple_strtoul(line, &endp, 16); | |
173 | if (line == endp) | |
174 | goto invalid_usage; | |
175 | ||
176 | /* Check for trailing cruft */ | |
177 | line = skip_space(endp); | |
178 | if (line[0]) | |
179 | goto invalid_usage; | |
180 | ||
181 | /* Finally, execute the command */ | |
182 | if (dest == 'E') { | |
183 | printf("Writing EEPROM register %02x with %02x\n", reg, value); | |
c44efcf9 | 184 | write_eeprom_reg(dev, value, reg); |
ad2d1639 MF |
185 | } else { |
186 | printf("Writing MAC register %02x with %08x\n", reg, value); | |
c44efcf9 | 187 | smc911x_reg_write(dev, reg, value); |
ad2d1639 MF |
188 | } |
189 | } | |
190 | ||
191 | /** | |
192 | * copy_from_eeprom - copy MAC address in eeprom to address registers | |
193 | */ | |
c44efcf9 | 194 | static void copy_from_eeprom(struct eth_device *dev) |
ad2d1639 MF |
195 | { |
196 | ulong addrl = | |
c44efcf9 MF |
197 | read_eeprom_reg(dev, 0x01) | |
198 | read_eeprom_reg(dev, 0x02) << 8 | | |
199 | read_eeprom_reg(dev, 0x03) << 16 | | |
200 | read_eeprom_reg(dev, 0x04) << 24; | |
ad2d1639 | 201 | ulong addrh = |
c44efcf9 MF |
202 | read_eeprom_reg(dev, 0x05) | |
203 | read_eeprom_reg(dev, 0x06) << 8; | |
204 | smc911x_set_mac_csr(dev, ADDRL, addrl); | |
205 | smc911x_set_mac_csr(dev, ADDRH, addrh); | |
ad2d1639 MF |
206 | puts("EEPROM contents copied to MAC\n"); |
207 | } | |
208 | ||
209 | /** | |
210 | * print_macaddr - print MAC address registers and MAC address in eeprom | |
211 | */ | |
c44efcf9 | 212 | static void print_macaddr(struct eth_device *dev) |
ad2d1639 MF |
213 | { |
214 | puts("Current MAC Address in MAC: "); | |
c44efcf9 MF |
215 | ulong addrl = smc911x_get_mac_csr(dev, ADDRL); |
216 | ulong addrh = smc911x_get_mac_csr(dev, ADDRH); | |
ad2d1639 MF |
217 | printf("%02x:%02x:%02x:%02x:%02x:%02x\n", |
218 | (u8)(addrl), (u8)(addrl >> 8), (u8)(addrl >> 16), | |
219 | (u8)(addrl >> 24), (u8)(addrh), (u8)(addrh >> 8)); | |
220 | ||
221 | puts("Current MAC Address in EEPROM: "); | |
222 | int i; | |
223 | for (i = 1; i < 6; ++i) | |
c44efcf9 MF |
224 | printf("%02x:", read_eeprom_reg(dev, i)); |
225 | printf("%02x\n", read_eeprom_reg(dev, i)); | |
ad2d1639 MF |
226 | } |
227 | ||
228 | /** | |
229 | * dump_eeprom - dump the whole content of the EEPROM | |
230 | */ | |
c44efcf9 | 231 | static void dump_eeprom(struct eth_device *dev) |
ad2d1639 MF |
232 | { |
233 | int i; | |
234 | puts("EEPROM:\n"); | |
235 | for (i = 0; i < 7; ++i) | |
c44efcf9 | 236 | printf("%02x: 0x%02x\n", i, read_eeprom_reg(dev, i)); |
ad2d1639 MF |
237 | } |
238 | ||
239 | /** | |
240 | * smc911x_init - get the MAC/EEPROM up and ready for use | |
241 | */ | |
c44efcf9 | 242 | static int smc911x_init(struct eth_device *dev) |
ad2d1639 MF |
243 | { |
244 | /* See if there is anything there */ | |
aa9fba53 | 245 | if (smc911x_detect_chip(dev)) |
ad2d1639 MF |
246 | return 1; |
247 | ||
c44efcf9 | 248 | smc911x_reset(dev); |
ad2d1639 MF |
249 | |
250 | /* Make sure we set EEDIO/EECLK to the EEPROM */ | |
c44efcf9 MF |
251 | if (smc911x_reg_read(dev, GPIO_CFG) & GPIO_CFG_EEPR_EN) { |
252 | while (smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) | |
ad2d1639 | 253 | if (smsc_ctrlc()) { |
069f4364 | 254 | printf("init: timeout (E2P_CMD = 0x%08x)\n", |
c44efcf9 | 255 | smc911x_reg_read(dev, E2P_CMD)); |
ad2d1639 MF |
256 | return 1; |
257 | } | |
c44efcf9 MF |
258 | smc911x_reg_write(dev, GPIO_CFG, |
259 | smc911x_reg_read(dev, GPIO_CFG) & ~GPIO_CFG_EEPR_EN); | |
ad2d1639 MF |
260 | } |
261 | ||
262 | return 0; | |
263 | } | |
264 | ||
265 | /** | |
266 | * getline - consume a line of input and handle some escape sequences | |
267 | */ | |
268 | static char *getline(void) | |
269 | { | |
270 | static char buffer[100]; | |
271 | char c; | |
272 | size_t i; | |
273 | ||
274 | i = 0; | |
275 | while (1) { | |
276 | buffer[i] = '\0'; | |
277 | while (!tstc()) | |
278 | continue; | |
279 | ||
280 | c = getc(); | |
281 | /* Convert to uppercase */ | |
282 | if (c >= 'a' && c <= 'z') | |
283 | c -= ('a' - 'A'); | |
284 | ||
285 | switch (c) { | |
286 | case '\r': /* Enter/Return key */ | |
287 | case '\n': | |
288 | puts("\n"); | |
289 | return buffer; | |
290 | ||
291 | case 0x03: /* ^C - break */ | |
292 | return NULL; | |
293 | ||
294 | case 0x5F: | |
295 | case 0x08: /* ^H - backspace */ | |
296 | case 0x7F: /* DEL - backspace */ | |
297 | if (i) { | |
298 | puts("\b \b"); | |
299 | i--; | |
300 | } | |
301 | break; | |
302 | ||
303 | default: | |
304 | /* Ignore control characters */ | |
305 | if (c < 0x20) | |
306 | break; | |
307 | /* Queue up all other characters */ | |
308 | buffer[i++] = c; | |
309 | printf("%c", c); | |
310 | break; | |
311 | } | |
312 | } | |
313 | } | |
314 | ||
315 | /** | |
316 | * smc911x_eeprom - our application's main() function | |
317 | */ | |
54841ab5 | 318 | int smc911x_eeprom(int argc, char * const argv[]) |
ad2d1639 | 319 | { |
c44efcf9 MF |
320 | /* Avoid initializing on stack as gcc likes to call memset() */ |
321 | struct eth_device dev; | |
c44efcf9 MF |
322 | dev.iobase = CONFIG_SMC911X_BASE; |
323 | ||
ad2d1639 MF |
324 | /* Print the ABI version */ |
325 | app_startup(argv); | |
326 | if (XF_VERSION != get_version()) { | |
327 | printf("Expects ABI version %d\n", XF_VERSION); | |
328 | printf("Actual U-Boot ABI version %lu\n", get_version()); | |
329 | printf("Can't run\n\n"); | |
330 | return 1; | |
331 | } | |
332 | ||
333 | /* Initialize the MAC/EEPROM somewhat */ | |
334 | puts("\n"); | |
c44efcf9 | 335 | if (smc911x_init(&dev)) |
ad2d1639 MF |
336 | return 1; |
337 | ||
338 | /* Dump helpful usage information */ | |
339 | puts("\n"); | |
340 | usage(); | |
341 | puts("\n"); | |
342 | ||
343 | while (1) { | |
344 | char *line; | |
345 | ||
346 | /* Send the prompt and wait for a line */ | |
347 | puts("eeprom> "); | |
348 | line = getline(); | |
349 | ||
350 | /* Got a ctrl+c */ | |
351 | if (!line) | |
352 | return 0; | |
353 | ||
354 | /* Eat leading space */ | |
355 | line = skip_space(line); | |
356 | ||
357 | /* Empty line, try again */ | |
358 | if (!line[0]) | |
359 | continue; | |
360 | ||
361 | /* Only accept 1 letter commands */ | |
4d91a6ec | 362 | if (line[0] && line[1] && !isblank(line[1])) |
ad2d1639 MF |
363 | goto unknown_cmd; |
364 | ||
365 | /* Now parse the command */ | |
366 | switch (line[0]) { | |
c44efcf9 MF |
367 | case 'W': write_stuff(&dev, line); break; |
368 | case 'D': dump_eeprom(&dev); break; | |
369 | case 'M': dump_regs(&dev); break; | |
370 | case 'C': copy_from_eeprom(&dev); break; | |
371 | case 'P': print_macaddr(&dev); break; | |
ad2d1639 MF |
372 | unknown_cmd: |
373 | default: puts("ERROR: Unknown command!\n\n"); | |
374 | case '?': | |
375 | case 'H': usage(); break; | |
376 | case 'Q': return 0; | |
377 | } | |
378 | } | |
379 | } |