1 /*
2  * smc911x_eeprom.c - EEPROM interface to SMC911x parts.
3  * Only tested on SMSC9118 though ...
4  *
5  * Copyright 2004-2008 Analog Devices Inc.
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
12  *       Das U-boot
13  *  - Ladislav Michl ladis@linux-mips.org
14  *       A rejected patch on the U-Boot mailing list
15  */
16 
17 #include <common.h>
18 #include <exports.h>
19 
20 #ifdef CONFIG_DRIVER_SMC911X
21 
22 #include "../drivers/net/smc911x.h"
23 
24 /**
25  *	smsc_ctrlc - detect press of CTRL+C (common ctrlc() isnt exported!?)
26  */
27 static int smsc_ctrlc(void)
28 {
29 	return (tstc() && getc() == 0x03);
30 }
31 
32 /**
33  *	usage - dump usage information
34  */
35 static void usage(void)
36 {
37 	puts(
38 		"MAC/EEPROM Commands:\n"
39 		" P : Print the MAC addresses\n"
40 		" D : Dump the EEPROM contents\n"
41 		" M : Dump the MAC contents\n"
42 		" C : Copy the MAC address from the EEPROM to the MAC\n"
43 		" W : Write a register in the EEPROM or in the MAC\n"
44 		" Q : Quit\n"
45 		"\n"
46 		"Some commands take arguments:\n"
47 		" W <E|M> <register> <value>\n"
48 		"    E: EEPROM   M: MAC\n"
49 	);
50 }
51 
52 /**
53  *	dump_regs - dump the MAC registers
54  *
55  * Registers 0x00 - 0x50 are FIFOs.  The 0x50+ are the control registers
56  * and they're all 32bits long.  0xB8+ are reserved, so don't bother.
57  */
58 static void dump_regs(void)
59 {
60 	u8 i, j = 0;
61 	for (i = 0x50; i < 0xB8; i += sizeof(u32))
62 		printf("%02x: 0x%08x %c", i,
63 			smc911x_reg_read(CONFIG_DRIVER_SMC911X_BASE + i),
64 			(j++ % 2 ? '\n' : ' '));
65 }
66 
67 /**
68  *	do_eeprom_cmd - handle eeprom communication
69  */
70 static int do_eeprom_cmd(int cmd, u8 reg)
71 {
72 	if (smc911x_reg_read(E2P_CMD) & E2P_CMD_EPC_BUSY) {
73 		printf("eeprom_cmd: busy at start (E2P_CMD = 0x%08x)\n",
74 			smc911x_reg_read(E2P_CMD));
75 		return -1;
76 	}
77 
78 	smc911x_reg_write(E2P_CMD, E2P_CMD_EPC_BUSY | cmd | reg);
79 
80 	while (smc911x_reg_read(E2P_CMD) & E2P_CMD_EPC_BUSY)
81 		if (smsc_ctrlc()) {
82 			printf("eeprom_cmd: timeout (E2P_CMD = 0x%08x)\n",
83 				smc911x_reg_read(E2P_CMD));
84 			return -1;
85 		}
86 
87 	return 0;
88 }
89 
90 /**
91  *	read_eeprom_reg - read specified register in EEPROM
92  */
93 static u8 read_eeprom_reg(u8 reg)
94 {
95 	int ret = do_eeprom_cmd(E2P_CMD_EPC_CMD_READ, reg);
96 	return (ret ? : smc911x_reg_read(E2P_DATA));
97 }
98 
99 /**
100  *	write_eeprom_reg - write specified value into specified register in EEPROM
101  */
102 static int write_eeprom_reg(u8 value, u8 reg)
103 {
104 	int ret;
105 
106 	/* enable erasing/writing */
107 	ret = do_eeprom_cmd(E2P_CMD_EPC_CMD_EWEN, reg);
108 	if (ret)
109 		goto done;
110 
111 	/* erase the eeprom reg */
112 	ret = do_eeprom_cmd(E2P_CMD_EPC_CMD_ERASE, reg);
113 	if (ret)
114 		goto done;
115 
116 	/* write the eeprom reg */
117 	smc911x_reg_write(E2P_DATA, value);
118 	ret = do_eeprom_cmd(E2P_CMD_EPC_CMD_WRITE, reg);
119 	if (ret)
120 		goto done;
121 
122 	/* disable erasing/writing */
123 	ret = do_eeprom_cmd(E2P_CMD_EPC_CMD_EWDS, reg);
124 
125  done:
126 	return ret;
127 }
128 
129 /**
130  *	skip_space - find first non-whitespace in given pointer
131  */
132 static char *skip_space(char *buf)
133 {
134 	while (buf[0] == ' ' || buf[0] == '\t')
135 		++buf;
136 	return buf;
137 }
138 
139 /**
140  *	write_stuff - handle writing of MAC registers / eeprom
141  */
142 static void write_stuff(char *line)
143 {
144 	char dest;
145 	char *endp;
146 	u8 reg;
147 	u32 value;
148 
149 	/* Skip over the "W " part of the command */
150 	line = skip_space(line + 1);
151 
152 	/* Figure out destination */
153 	switch (line[0]) {
154 	case 'E':
155 	case 'M':
156 		dest = line[0];
157 		break;
158 	default:
159 	invalid_usage:
160 		printf("ERROR: Invalid write usage\n");
161 		usage();
162 		return;
163 	}
164 
165 	/* Get the register to write */
166 	line = skip_space(line + 1);
167 	reg = simple_strtoul(line, &endp, 16);
168 	if (line == endp)
169 		goto invalid_usage;
170 
171 	/* Get the value to write */
172 	line = skip_space(endp);
173 	value = simple_strtoul(line, &endp, 16);
174 	if (line == endp)
175 		goto invalid_usage;
176 
177 	/* Check for trailing cruft */
178 	line = skip_space(endp);
179 	if (line[0])
180 		goto invalid_usage;
181 
182 	/* Finally, execute the command */
183 	if (dest == 'E') {
184 		printf("Writing EEPROM register %02x with %02x\n", reg, value);
185 		write_eeprom_reg(value, reg);
186 	} else {
187 		printf("Writing MAC register %02x with %08x\n", reg, value);
188 		smc911x_reg_write(CONFIG_DRIVER_SMC911X_BASE + reg, value);
189 	}
190 }
191 
192 /**
193  *	copy_from_eeprom - copy MAC address in eeprom to address registers
194  */
195 static void copy_from_eeprom(void)
196 {
197 	ulong addrl =
198 		read_eeprom_reg(0x01) |
199 		read_eeprom_reg(0x02) << 8 |
200 		read_eeprom_reg(0x03) << 16 |
201 		read_eeprom_reg(0x04) << 24;
202 	ulong addrh =
203 		read_eeprom_reg(0x05) |
204 		read_eeprom_reg(0x06) << 8;
205 	smc911x_set_mac_csr(ADDRL, addrl);
206 	smc911x_set_mac_csr(ADDRH, addrh);
207 	puts("EEPROM contents copied to MAC\n");
208 }
209 
210 /**
211  *	print_macaddr - print MAC address registers and MAC address in eeprom
212  */
213 static void print_macaddr(void)
214 {
215 	puts("Current MAC Address in MAC:     ");
216 	ulong addrl = smc911x_get_mac_csr(ADDRL);
217 	ulong addrh = smc911x_get_mac_csr(ADDRH);
218 	printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
219 		(u8)(addrl), (u8)(addrl >> 8), (u8)(addrl >> 16),
220 		(u8)(addrl >> 24), (u8)(addrh), (u8)(addrh >> 8));
221 
222 	puts("Current MAC Address in EEPROM:  ");
223 	int i;
224 	for (i = 1; i < 6; ++i)
225 		printf("%02x:", read_eeprom_reg(i));
226 	printf("%02x\n", read_eeprom_reg(i));
227 }
228 
229 /**
230  *	dump_eeprom - dump the whole content of the EEPROM
231  */
232 static void dump_eeprom(void)
233 {
234 	int i;
235 	puts("EEPROM:\n");
236 	for (i = 0; i < 7; ++i)
237 		printf("%02x: 0x%02x\n", i, read_eeprom_reg(i));
238 }
239 
240 /**
241  *	smc911x_init - get the MAC/EEPROM up and ready for use
242  */
243 static int smc911x_init(void)
244 {
245 	/* See if there is anything there */
246 	if (!smc911x_detect_chip())
247 		return 1;
248 
249 	smc911x_reset();
250 
251 	/* Make sure we set EEDIO/EECLK to the EEPROM */
252 	if (smc911x_reg_read(GPIO_CFG) & GPIO_CFG_EEPR_EN) {
253 		while (smc911x_reg_read(E2P_CMD) & E2P_CMD_EPC_BUSY)
254 			if (smsc_ctrlc()) {
255 				printf("init: timeout (E2P_CMD = 0x%08x)\n",
256 					smc911x_reg_read(E2P_CMD));
257 				return 1;
258 			}
259 		smc911x_reg_write(GPIO_CFG, smc911x_reg_read(GPIO_CFG) & ~GPIO_CFG_EEPR_EN);
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  */
318 int smc911x_eeprom(int argc, char *argv[])
319 {
320 	/* Print the ABI version */
321 	app_startup(argv);
322 	if (XF_VERSION != get_version()) {
323 		printf("Expects ABI version %d\n", XF_VERSION);
324 		printf("Actual U-Boot ABI version %lu\n", get_version());
325 		printf("Can't run\n\n");
326 		return 1;
327 	}
328 
329 	/* Initialize the MAC/EEPROM somewhat */
330 	puts("\n");
331 	if (smc911x_init())
332 		return 1;
333 
334 	/* Dump helpful usage information */
335 	puts("\n");
336 	usage();
337 	puts("\n");
338 
339 	while (1) {
340 		char *line;
341 
342 		/* Send the prompt and wait for a line */
343 		puts("eeprom> ");
344 		line = getline();
345 
346 		/* Got a ctrl+c */
347 		if (!line)
348 			return 0;
349 
350 		/* Eat leading space */
351 		line = skip_space(line);
352 
353 		/* Empty line, try again */
354 		if (!line[0])
355 			continue;
356 
357 		/* Only accept 1 letter commands */
358 		if (line[0] && line[1] && line[1] != ' ' && line[1] != '\t')
359 			goto unknown_cmd;
360 
361 		/* Now parse the command */
362 		switch (line[0]) {
363 		case 'W': write_stuff(line);  break;
364 		case 'D': dump_eeprom();      break;
365 		case 'M': dump_regs();        break;
366 		case 'C': copy_from_eeprom(); break;
367 		case 'P': print_macaddr();    break;
368 		unknown_cmd:
369 		default:  puts("ERROR: Unknown command!\n\n");
370 		case '?':
371 		case 'H': usage();            break;
372 		case 'Q': return 0;
373 		}
374 	}
375 }
376 
377 #else
378 int smc911x_eeprom(int argc, char *argv[])
379 {
380 	puts("Not supported for this board\n");
381 	return 1;
382 }
383 #endif
384