|  | /* | 
|  | * smc911x_eeprom.c - EEPROM interface to SMC911x parts. | 
|  | * Only tested on SMSC9118 though ... | 
|  | * | 
|  | * Copyright 2004-2009 Analog Devices Inc. | 
|  | * | 
|  | * Licensed under the GPL-2 or later. | 
|  | * | 
|  | * Based on smc91111_eeprom.c which: | 
|  | * Heavily borrowed from the following peoples GPL'ed software: | 
|  | *  - Wolfgang Denk, DENX Software Engineering, wd@denx.de | 
|  | *       Das U-boot | 
|  | *  - Ladislav Michl ladis@linux-mips.org | 
|  | *       A rejected patch on the U-Boot mailing list | 
|  | */ | 
|  |  | 
|  | #include <common.h> | 
|  | #include <exports.h> | 
|  |  | 
|  | /* the smc911x.h gets base addr through eth_device' iobase */ | 
|  | struct eth_device { | 
|  | const char *name; | 
|  | unsigned long iobase; | 
|  | void *priv; | 
|  | }; | 
|  | #include "../drivers/net/smc911x.h" | 
|  |  | 
|  | /** | 
|  | *	smsc_ctrlc - detect press of CTRL+C (common ctrlc() isnt exported!?) | 
|  | */ | 
|  | static int smsc_ctrlc(void) | 
|  | { | 
|  | return (tstc() && getc() == 0x03); | 
|  | } | 
|  |  | 
|  | /** | 
|  | *	usage - dump usage information | 
|  | */ | 
|  | static void usage(void) | 
|  | { | 
|  | puts( | 
|  | "MAC/EEPROM Commands:\n" | 
|  | " P : Print the MAC addresses\n" | 
|  | " D : Dump the EEPROM contents\n" | 
|  | " M : Dump the MAC contents\n" | 
|  | " C : Copy the MAC address from the EEPROM to the MAC\n" | 
|  | " W : Write a register in the EEPROM or in the MAC\n" | 
|  | " Q : Quit\n" | 
|  | "\n" | 
|  | "Some commands take arguments:\n" | 
|  | " W <E|M> <register> <value>\n" | 
|  | "    E: EEPROM   M: MAC\n" | 
|  | ); | 
|  | } | 
|  |  | 
|  | /** | 
|  | *	dump_regs - dump the MAC registers | 
|  | * | 
|  | * Registers 0x00 - 0x50 are FIFOs.  The 0x50+ are the control registers | 
|  | * and they're all 32bits long.  0xB8+ are reserved, so don't bother. | 
|  | */ | 
|  | static void dump_regs(struct eth_device *dev) | 
|  | { | 
|  | u8 i, j = 0; | 
|  | for (i = 0x50; i < 0xB8; i += sizeof(u32)) | 
|  | printf("%02x: 0x%08x %c", i, | 
|  | smc911x_reg_read(dev, i), | 
|  | (j++ % 2 ? '\n' : ' ')); | 
|  | } | 
|  |  | 
|  | /** | 
|  | *	do_eeprom_cmd - handle eeprom communication | 
|  | */ | 
|  | static int do_eeprom_cmd(struct eth_device *dev, int cmd, u8 reg) | 
|  | { | 
|  | if (smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) { | 
|  | printf("eeprom_cmd: busy at start (E2P_CMD = 0x%08x)\n", | 
|  | smc911x_reg_read(dev, E2P_CMD)); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | smc911x_reg_write(dev, E2P_CMD, E2P_CMD_EPC_BUSY | cmd | reg); | 
|  |  | 
|  | while (smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) | 
|  | if (smsc_ctrlc()) { | 
|  | printf("eeprom_cmd: timeout (E2P_CMD = 0x%08x)\n", | 
|  | smc911x_reg_read(dev, E2P_CMD)); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | *	read_eeprom_reg - read specified register in EEPROM | 
|  | */ | 
|  | static u8 read_eeprom_reg(struct eth_device *dev, u8 reg) | 
|  | { | 
|  | int ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_READ, reg); | 
|  | return (ret ? : smc911x_reg_read(dev, E2P_DATA)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | *	write_eeprom_reg - write specified value into specified register in EEPROM | 
|  | */ | 
|  | static int write_eeprom_reg(struct eth_device *dev, u8 value, u8 reg) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | /* enable erasing/writing */ | 
|  | ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_EWEN, reg); | 
|  | if (ret) | 
|  | goto done; | 
|  |  | 
|  | /* erase the eeprom reg */ | 
|  | ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_ERASE, reg); | 
|  | if (ret) | 
|  | goto done; | 
|  |  | 
|  | /* write the eeprom reg */ | 
|  | smc911x_reg_write(dev, E2P_DATA, value); | 
|  | ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_WRITE, reg); | 
|  | if (ret) | 
|  | goto done; | 
|  |  | 
|  | /* disable erasing/writing */ | 
|  | ret = do_eeprom_cmd(dev, E2P_CMD_EPC_CMD_EWDS, reg); | 
|  |  | 
|  | done: | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /** | 
|  | *	skip_space - find first non-whitespace in given pointer | 
|  | */ | 
|  | static char *skip_space(char *buf) | 
|  | { | 
|  | while (buf[0] == ' ' || buf[0] == '\t') | 
|  | ++buf; | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | /** | 
|  | *	write_stuff - handle writing of MAC registers / eeprom | 
|  | */ | 
|  | static void write_stuff(struct eth_device *dev, char *line) | 
|  | { | 
|  | char dest; | 
|  | char *endp; | 
|  | u8 reg; | 
|  | u32 value; | 
|  |  | 
|  | /* Skip over the "W " part of the command */ | 
|  | line = skip_space(line + 1); | 
|  |  | 
|  | /* Figure out destination */ | 
|  | switch (line[0]) { | 
|  | case 'E': | 
|  | case 'M': | 
|  | dest = line[0]; | 
|  | break; | 
|  | default: | 
|  | invalid_usage: | 
|  | printf("ERROR: Invalid write usage\n"); | 
|  | usage(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Get the register to write */ | 
|  | line = skip_space(line + 1); | 
|  | reg = simple_strtoul(line, &endp, 16); | 
|  | if (line == endp) | 
|  | goto invalid_usage; | 
|  |  | 
|  | /* Get the value to write */ | 
|  | line = skip_space(endp); | 
|  | value = simple_strtoul(line, &endp, 16); | 
|  | if (line == endp) | 
|  | goto invalid_usage; | 
|  |  | 
|  | /* Check for trailing cruft */ | 
|  | line = skip_space(endp); | 
|  | if (line[0]) | 
|  | goto invalid_usage; | 
|  |  | 
|  | /* Finally, execute the command */ | 
|  | if (dest == 'E') { | 
|  | printf("Writing EEPROM register %02x with %02x\n", reg, value); | 
|  | write_eeprom_reg(dev, value, reg); | 
|  | } else { | 
|  | printf("Writing MAC register %02x with %08x\n", reg, value); | 
|  | smc911x_reg_write(dev, reg, value); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | *	copy_from_eeprom - copy MAC address in eeprom to address registers | 
|  | */ | 
|  | static void copy_from_eeprom(struct eth_device *dev) | 
|  | { | 
|  | ulong addrl = | 
|  | read_eeprom_reg(dev, 0x01) | | 
|  | read_eeprom_reg(dev, 0x02) << 8 | | 
|  | read_eeprom_reg(dev, 0x03) << 16 | | 
|  | read_eeprom_reg(dev, 0x04) << 24; | 
|  | ulong addrh = | 
|  | read_eeprom_reg(dev, 0x05) | | 
|  | read_eeprom_reg(dev, 0x06) << 8; | 
|  | smc911x_set_mac_csr(dev, ADDRL, addrl); | 
|  | smc911x_set_mac_csr(dev, ADDRH, addrh); | 
|  | puts("EEPROM contents copied to MAC\n"); | 
|  | } | 
|  |  | 
|  | /** | 
|  | *	print_macaddr - print MAC address registers and MAC address in eeprom | 
|  | */ | 
|  | static void print_macaddr(struct eth_device *dev) | 
|  | { | 
|  | puts("Current MAC Address in MAC:     "); | 
|  | ulong addrl = smc911x_get_mac_csr(dev, ADDRL); | 
|  | ulong addrh = smc911x_get_mac_csr(dev, ADDRH); | 
|  | printf("%02x:%02x:%02x:%02x:%02x:%02x\n", | 
|  | (u8)(addrl), (u8)(addrl >> 8), (u8)(addrl >> 16), | 
|  | (u8)(addrl >> 24), (u8)(addrh), (u8)(addrh >> 8)); | 
|  |  | 
|  | puts("Current MAC Address in EEPROM:  "); | 
|  | int i; | 
|  | for (i = 1; i < 6; ++i) | 
|  | printf("%02x:", read_eeprom_reg(dev, i)); | 
|  | printf("%02x\n", read_eeprom_reg(dev, i)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | *	dump_eeprom - dump the whole content of the EEPROM | 
|  | */ | 
|  | static void dump_eeprom(struct eth_device *dev) | 
|  | { | 
|  | int i; | 
|  | puts("EEPROM:\n"); | 
|  | for (i = 0; i < 7; ++i) | 
|  | printf("%02x: 0x%02x\n", i, read_eeprom_reg(dev, i)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | *	smc911x_init - get the MAC/EEPROM up and ready for use | 
|  | */ | 
|  | static int smc911x_init(struct eth_device *dev) | 
|  | { | 
|  | /* See if there is anything there */ | 
|  | if (!smc911x_detect_chip(dev)) | 
|  | return 1; | 
|  |  | 
|  | smc911x_reset(dev); | 
|  |  | 
|  | /* Make sure we set EEDIO/EECLK to the EEPROM */ | 
|  | if (smc911x_reg_read(dev, GPIO_CFG) & GPIO_CFG_EEPR_EN) { | 
|  | while (smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) | 
|  | if (smsc_ctrlc()) { | 
|  | printf("init: timeout (E2P_CMD = 0x%08x)\n", | 
|  | smc911x_reg_read(dev, E2P_CMD)); | 
|  | return 1; | 
|  | } | 
|  | smc911x_reg_write(dev, GPIO_CFG, | 
|  | smc911x_reg_read(dev, GPIO_CFG) & ~GPIO_CFG_EEPR_EN); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | *	getline - consume a line of input and handle some escape sequences | 
|  | */ | 
|  | static char *getline(void) | 
|  | { | 
|  | static char buffer[100]; | 
|  | char c; | 
|  | size_t i; | 
|  |  | 
|  | i = 0; | 
|  | while (1) { | 
|  | buffer[i] = '\0'; | 
|  | while (!tstc()) | 
|  | continue; | 
|  |  | 
|  | c = getc(); | 
|  | /* Convert to uppercase */ | 
|  | if (c >= 'a' && c <= 'z') | 
|  | c -= ('a' - 'A'); | 
|  |  | 
|  | switch (c) { | 
|  | case '\r':	/* Enter/Return key */ | 
|  | case '\n': | 
|  | puts("\n"); | 
|  | return buffer; | 
|  |  | 
|  | case 0x03:	/* ^C - break */ | 
|  | return NULL; | 
|  |  | 
|  | case 0x5F: | 
|  | case 0x08:	/* ^H  - backspace */ | 
|  | case 0x7F:	/* DEL - backspace */ | 
|  | if (i) { | 
|  | puts("\b \b"); | 
|  | i--; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | /* Ignore control characters */ | 
|  | if (c < 0x20) | 
|  | break; | 
|  | /* Queue up all other characters */ | 
|  | buffer[i++] = c; | 
|  | printf("%c", c); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | *	smc911x_eeprom - our application's main() function | 
|  | */ | 
|  | int smc911x_eeprom(int argc, char *argv[]) | 
|  | { | 
|  | /* Avoid initializing on stack as gcc likes to call memset() */ | 
|  | struct eth_device dev; | 
|  | dev.name = __func__; | 
|  | dev.iobase = CONFIG_SMC911X_BASE; | 
|  |  | 
|  | /* Print the ABI version */ | 
|  | app_startup(argv); | 
|  | if (XF_VERSION != get_version()) { | 
|  | printf("Expects ABI version %d\n", XF_VERSION); | 
|  | printf("Actual U-Boot ABI version %lu\n", get_version()); | 
|  | printf("Can't run\n\n"); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* Initialize the MAC/EEPROM somewhat */ | 
|  | puts("\n"); | 
|  | if (smc911x_init(&dev)) | 
|  | return 1; | 
|  |  | 
|  | /* Dump helpful usage information */ | 
|  | puts("\n"); | 
|  | usage(); | 
|  | puts("\n"); | 
|  |  | 
|  | while (1) { | 
|  | char *line; | 
|  |  | 
|  | /* Send the prompt and wait for a line */ | 
|  | puts("eeprom> "); | 
|  | line = getline(); | 
|  |  | 
|  | /* Got a ctrl+c */ | 
|  | if (!line) | 
|  | return 0; | 
|  |  | 
|  | /* Eat leading space */ | 
|  | line = skip_space(line); | 
|  |  | 
|  | /* Empty line, try again */ | 
|  | if (!line[0]) | 
|  | continue; | 
|  |  | 
|  | /* Only accept 1 letter commands */ | 
|  | if (line[0] && line[1] && line[1] != ' ' && line[1] != '\t') | 
|  | goto unknown_cmd; | 
|  |  | 
|  | /* Now parse the command */ | 
|  | switch (line[0]) { | 
|  | case 'W': write_stuff(&dev, line); break; | 
|  | case 'D': dump_eeprom(&dev);       break; | 
|  | case 'M': dump_regs(&dev);         break; | 
|  | case 'C': copy_from_eeprom(&dev);  break; | 
|  | case 'P': print_macaddr(&dev);     break; | 
|  | unknown_cmd: | 
|  | default:  puts("ERROR: Unknown command!\n\n"); | 
|  | case '?': | 
|  | case 'H': usage();            break; | 
|  | case 'Q': return 0; | 
|  | } | 
|  | } | 
|  | } |