diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index 65c25f3bfd..ff285f14b4 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -10,9 +10,165 @@ #include #include #include +#include #include "smc911x.h" +struct chip_id { + u16 id; + char *name; +}; + +static const struct chip_id chip_ids[] = { + { CHIP_89218, "LAN89218" }, + { CHIP_9115, "LAN9115" }, + { CHIP_9116, "LAN9116" }, + { CHIP_9117, "LAN9117" }, + { CHIP_9118, "LAN9118" }, + { CHIP_9211, "LAN9211" }, + { CHIP_9215, "LAN9215" }, + { CHIP_9216, "LAN9216" }, + { CHIP_9217, "LAN9217" }, + { CHIP_9218, "LAN9218" }, + { CHIP_9220, "LAN9220" }, + { CHIP_9221, "LAN9221" }, + { 0, NULL }, +}; + +#define DRIVERNAME "smc911x" + +#if defined (CONFIG_SMC911X_32_BIT) && \ + defined (CONFIG_SMC911X_16_BIT) +#error "SMC911X: Only one of CONFIG_SMC911X_32_BIT and \ + CONFIG_SMC911X_16_BIT shall be set" +#endif + +#if defined (CONFIG_SMC911X_32_BIT) +static inline u32 __smc911x_reg_read(struct eth_device *dev, u32 offset) +{ + return *(volatile u32*)(dev->iobase + offset); +} +u32 smc911x_reg_read(struct eth_device *dev, u32 offset) + __attribute__((weak, alias("__smc911x_reg_read"))); + +static inline void __smc911x_reg_write(struct eth_device *dev, + u32 offset, u32 val) +{ + *(volatile u32*)(dev->iobase + offset) = val; +} +void smc911x_reg_write(struct eth_device *dev, u32 offset, u32 val) + __attribute__((weak, alias("__smc911x_reg_write"))); +#elif defined (CONFIG_SMC911X_16_BIT) +static inline u32 smc911x_reg_read(struct eth_device *dev, u32 offset) +{ + volatile u16 *addr_16 = (u16 *)(dev->iobase + offset); + return ((*addr_16 & 0x0000ffff) | (*(addr_16 + 1) << 16)); +} +static inline void smc911x_reg_write(struct eth_device *dev, + u32 offset, u32 val) +{ + *(volatile u16 *)(dev->iobase + offset) = (u16)val; + *(volatile u16 *)(dev->iobase + offset + 2) = (u16)(val >> 16); +} +#else +#error "SMC911X: undefined bus width" +#endif /* CONFIG_SMC911X_16_BIT */ + +static u32 smc911x_get_mac_csr(struct eth_device *dev, u8 reg) +{ + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; + smc911x_reg_write(dev, MAC_CSR_CMD, + MAC_CSR_CMD_CSR_BUSY | MAC_CSR_CMD_R_NOT_W | reg); + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; + + return smc911x_reg_read(dev, MAC_CSR_DATA); +} + +static void smc911x_set_mac_csr(struct eth_device *dev, u8 reg, u32 data) +{ + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; + smc911x_reg_write(dev, MAC_CSR_DATA, data); + smc911x_reg_write(dev, MAC_CSR_CMD, MAC_CSR_CMD_CSR_BUSY | reg); + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; +} + +static int smc911x_detect_chip(struct eth_device *dev) +{ + unsigned long val, i; + + val = smc911x_reg_read(dev, BYTE_TEST); + if (val == 0xffffffff) { + /* Special case -- no chip present */ + return -1; + } else if (val != 0x87654321) { + printf(DRIVERNAME ": Invalid chip endian 0x%08lx\n", val); + return -1; + } + + val = smc911x_reg_read(dev, ID_REV) >> 16; + for (i = 0; chip_ids[i].id != 0; i++) { + if (chip_ids[i].id == val) break; + } + if (!chip_ids[i].id) { + printf(DRIVERNAME ": Unknown chip ID %04lx\n", val); + return -1; + } + + dev->priv = (void *)&chip_ids[i]; + + return 0; +} + +static void smc911x_reset(struct eth_device *dev) +{ + int timeout; + + /* + * Take out of PM setting first + * Device is already wake up if PMT_CTRL_READY bit is set + */ + if ((smc911x_reg_read(dev, PMT_CTRL) & PMT_CTRL_READY) == 0) { + /* Write to the bytetest will take out of powerdown */ + smc911x_reg_write(dev, BYTE_TEST, 0x0); + + timeout = 10; + + while (timeout-- && + !(smc911x_reg_read(dev, PMT_CTRL) & PMT_CTRL_READY)) + udelay(10); + if (timeout < 0) { + printf(DRIVERNAME + ": timeout waiting for PM restore\n"); + return; + } + } + + /* Disable interrupts */ + smc911x_reg_write(dev, INT_EN, 0); + + smc911x_reg_write(dev, HW_CFG, HW_CFG_SRST); + + timeout = 1000; + while (timeout-- && smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) + udelay(10); + + if (timeout < 0) { + printf(DRIVERNAME ": reset timeout\n"); + return; + } + + /* Reset the FIFO level and flow control settings */ + smc911x_set_mac_csr(dev, FLOW, FLOW_FCPT | FLOW_FCEN); + smc911x_reg_write(dev, AFC_CFG, 0x0050287F); + + /* Set to LED outputs */ + smc911x_reg_write(dev, GPIO_CFG, 0x70070000); +} + static void smc911x_handle_mac_address(struct eth_device *dev) { unsigned long addrh, addrl; @@ -117,7 +273,6 @@ static void smc911x_enable(struct eth_device *dev) smc911x_set_mac_csr(dev, MAC_CR, MAC_CR_TXEN | MAC_CR_RXEN | MAC_CR_HBDIS); - } static int smc911x_init(struct eth_device *dev, bd_t * bd) diff --git a/drivers/net/smc911x.h b/drivers/net/smc911x.h index 3145fbde2b..ce66900f4c 100644 --- a/drivers/net/smc911x.h +++ b/drivers/net/smc911x.h @@ -8,47 +8,6 @@ #ifndef _SMC911X_H_ #define _SMC911X_H_ -#include - -#define DRIVERNAME "smc911x" - -#if defined (CONFIG_SMC911X_32_BIT) && \ - defined (CONFIG_SMC911X_16_BIT) -#error "SMC911X: Only one of CONFIG_SMC911X_32_BIT and \ - CONFIG_SMC911X_16_BIT shall be set" -#endif - -#if defined (CONFIG_SMC911X_32_BIT) -static inline u32 __smc911x_reg_read(struct eth_device *dev, u32 offset) -{ - return *(volatile u32*)(dev->iobase + offset); -} -u32 smc911x_reg_read(struct eth_device *dev, u32 offset) - __attribute__((weak, alias("__smc911x_reg_read"))); - -static inline void __smc911x_reg_write(struct eth_device *dev, - u32 offset, u32 val) -{ - *(volatile u32*)(dev->iobase + offset) = val; -} -void smc911x_reg_write(struct eth_device *dev, u32 offset, u32 val) - __attribute__((weak, alias("__smc911x_reg_write"))); -#elif defined (CONFIG_SMC911X_16_BIT) -static inline u32 smc911x_reg_read(struct eth_device *dev, u32 offset) -{ - volatile u16 *addr_16 = (u16 *)(dev->iobase + offset); - return ((*addr_16 & 0x0000ffff) | (*(addr_16 + 1) << 16)); -} -static inline void smc911x_reg_write(struct eth_device *dev, - u32 offset, u32 val) -{ - *(volatile u16 *)(dev->iobase + offset) = (u16)val; - *(volatile u16 *)(dev->iobase + offset + 2) = (u16)(val >> 16); -} -#else -#error "SMC911X: undefined bus width" -#endif /* CONFIG_SMC911X_16_BIT */ - /* Below are the register offsets and bit definitions * of the Lan911x memory space */ @@ -380,120 +339,4 @@ static inline void smc911x_reg_write(struct eth_device *dev, #define CHIP_9220 0x9220 #define CHIP_9221 0x9221 -struct chip_id { - u16 id; - char *name; -}; - -static const struct chip_id chip_ids[] = { - { CHIP_89218, "LAN89218" }, - { CHIP_9115, "LAN9115" }, - { CHIP_9116, "LAN9116" }, - { CHIP_9117, "LAN9117" }, - { CHIP_9118, "LAN9118" }, - { CHIP_9211, "LAN9211" }, - { CHIP_9215, "LAN9215" }, - { CHIP_9216, "LAN9216" }, - { CHIP_9217, "LAN9217" }, - { CHIP_9218, "LAN9218" }, - { CHIP_9220, "LAN9220" }, - { CHIP_9221, "LAN9221" }, - { 0, NULL }, -}; - -static u32 smc911x_get_mac_csr(struct eth_device *dev, u8 reg) -{ - while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) - ; - smc911x_reg_write(dev, MAC_CSR_CMD, - MAC_CSR_CMD_CSR_BUSY | MAC_CSR_CMD_R_NOT_W | reg); - while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) - ; - - return smc911x_reg_read(dev, MAC_CSR_DATA); -} - -static void smc911x_set_mac_csr(struct eth_device *dev, u8 reg, u32 data) -{ - while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) - ; - smc911x_reg_write(dev, MAC_CSR_DATA, data); - smc911x_reg_write(dev, MAC_CSR_CMD, MAC_CSR_CMD_CSR_BUSY | reg); - while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) - ; -} - -static int smc911x_detect_chip(struct eth_device *dev) -{ - unsigned long val, i; - - val = smc911x_reg_read(dev, BYTE_TEST); - if (val == 0xffffffff) { - /* Special case -- no chip present */ - return -1; - } else if (val != 0x87654321) { - printf(DRIVERNAME ": Invalid chip endian 0x%08lx\n", val); - return -1; - } - - val = smc911x_reg_read(dev, ID_REV) >> 16; - for (i = 0; chip_ids[i].id != 0; i++) { - if (chip_ids[i].id == val) break; - } - if (!chip_ids[i].id) { - printf(DRIVERNAME ": Unknown chip ID %04lx\n", val); - return -1; - } - - dev->priv = (void *)&chip_ids[i]; - - return 0; -} - -static void smc911x_reset(struct eth_device *dev) -{ - int timeout; - - /* - * Take out of PM setting first - * Device is already wake up if PMT_CTRL_READY bit is set - */ - if ((smc911x_reg_read(dev, PMT_CTRL) & PMT_CTRL_READY) == 0) { - /* Write to the bytetest will take out of powerdown */ - smc911x_reg_write(dev, BYTE_TEST, 0x0); - - timeout = 10; - - while (timeout-- && - !(smc911x_reg_read(dev, PMT_CTRL) & PMT_CTRL_READY)) - udelay(10); - if (timeout < 0) { - printf(DRIVERNAME - ": timeout waiting for PM restore\n"); - return; - } - } - - /* Disable interrupts */ - smc911x_reg_write(dev, INT_EN, 0); - - smc911x_reg_write(dev, HW_CFG, HW_CFG_SRST); - - timeout = 1000; - while (timeout-- && smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) - udelay(10); - - if (timeout < 0) { - printf(DRIVERNAME ": reset timeout\n"); - return; - } - - /* Reset the FIFO level and flow control settings */ - smc911x_set_mac_csr(dev, FLOW, FLOW_FCPT | FLOW_FCEN); - smc911x_reg_write(dev, AFC_CFG, 0x0050287F); - - /* Set to LED outputs */ - smc911x_reg_write(dev, GPIO_CFG, 0x70070000); -} - #endif diff --git a/examples/standalone/smc911x_eeprom.c b/examples/standalone/smc911x_eeprom.c index 2c05ed902d..19ad9e6297 100644 --- a/examples/standalone/smc911x_eeprom.c +++ b/examples/standalone/smc911x_eeprom.c @@ -18,8 +18,164 @@ #include #include #include +#include #include "../drivers/net/smc911x.h" +#define DRIVERNAME "smc911x" + +#if defined (CONFIG_SMC911X_32_BIT) && \ + defined (CONFIG_SMC911X_16_BIT) +#error "SMC911X: Only one of CONFIG_SMC911X_32_BIT and \ + CONFIG_SMC911X_16_BIT shall be set" +#endif + +struct chip_id { + u16 id; + char *name; +}; + +static const struct chip_id chip_ids[] = { + { CHIP_89218, "LAN89218" }, + { CHIP_9115, "LAN9115" }, + { CHIP_9116, "LAN9116" }, + { CHIP_9117, "LAN9117" }, + { CHIP_9118, "LAN9118" }, + { CHIP_9211, "LAN9211" }, + { CHIP_9215, "LAN9215" }, + { CHIP_9216, "LAN9216" }, + { CHIP_9217, "LAN9217" }, + { CHIP_9218, "LAN9218" }, + { CHIP_9220, "LAN9220" }, + { CHIP_9221, "LAN9221" }, + { 0, NULL }, +}; + +#if defined (CONFIG_SMC911X_32_BIT) +static inline u32 __smc911x_reg_read(struct eth_device *dev, u32 offset) +{ + return *(volatile u32*)(dev->iobase + offset); +} +u32 smc911x_reg_read(struct eth_device *dev, u32 offset) + __attribute__((weak, alias("__smc911x_reg_read"))); + +static inline void __smc911x_reg_write(struct eth_device *dev, + u32 offset, u32 val) +{ + *(volatile u32*)(dev->iobase + offset) = val; +} +void smc911x_reg_write(struct eth_device *dev, u32 offset, u32 val) + __attribute__((weak, alias("__smc911x_reg_write"))); +#elif defined (CONFIG_SMC911X_16_BIT) +static inline u32 smc911x_reg_read(struct eth_device *dev, u32 offset) +{ + volatile u16 *addr_16 = (u16 *)(dev->iobase + offset); + return ((*addr_16 & 0x0000ffff) | (*(addr_16 + 1) << 16)); +} +static inline void smc911x_reg_write(struct eth_device *dev, + u32 offset, u32 val) +{ + *(volatile u16 *)(dev->iobase + offset) = (u16)val; + *(volatile u16 *)(dev->iobase + offset + 2) = (u16)(val >> 16); +} +#else +#error "SMC911X: undefined bus width" +#endif /* CONFIG_SMC911X_16_BIT */ + +static u32 smc911x_get_mac_csr(struct eth_device *dev, u8 reg) +{ + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; + smc911x_reg_write(dev, MAC_CSR_CMD, + MAC_CSR_CMD_CSR_BUSY | MAC_CSR_CMD_R_NOT_W | reg); + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; + + return smc911x_reg_read(dev, MAC_CSR_DATA); +} + +static void smc911x_set_mac_csr(struct eth_device *dev, u8 reg, u32 data) +{ + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; + smc911x_reg_write(dev, MAC_CSR_DATA, data); + smc911x_reg_write(dev, MAC_CSR_CMD, MAC_CSR_CMD_CSR_BUSY | reg); + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; +} + +static int smc911x_detect_chip(struct eth_device *dev) +{ + unsigned long val, i; + + val = smc911x_reg_read(dev, BYTE_TEST); + if (val == 0xffffffff) { + /* Special case -- no chip present */ + return -1; + } else if (val != 0x87654321) { + printf(DRIVERNAME ": Invalid chip endian 0x%08lx\n", val); + return -1; + } + + val = smc911x_reg_read(dev, ID_REV) >> 16; + for (i = 0; chip_ids[i].id != 0; i++) { + if (chip_ids[i].id == val) break; + } + if (!chip_ids[i].id) { + printf(DRIVERNAME ": Unknown chip ID %04lx\n", val); + return -1; + } + + dev->priv = (void *)&chip_ids[i]; + + return 0; +} + +static void smc911x_reset(struct eth_device *dev) +{ + int timeout; + + /* + * Take out of PM setting first + * Device is already wake up if PMT_CTRL_READY bit is set + */ + if ((smc911x_reg_read(dev, PMT_CTRL) & PMT_CTRL_READY) == 0) { + /* Write to the bytetest will take out of powerdown */ + smc911x_reg_write(dev, BYTE_TEST, 0x0); + + timeout = 10; + + while (timeout-- && + !(smc911x_reg_read(dev, PMT_CTRL) & PMT_CTRL_READY)) + udelay(10); + if (timeout < 0) { + printf(DRIVERNAME + ": timeout waiting for PM restore\n"); + return; + } + } + + /* Disable interrupts */ + smc911x_reg_write(dev, INT_EN, 0); + + smc911x_reg_write(dev, HW_CFG, HW_CFG_SRST); + + timeout = 1000; + while (timeout-- && smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) + udelay(10); + + if (timeout < 0) { + printf(DRIVERNAME ": reset timeout\n"); + return; + } + + /* Reset the FIFO level and flow control settings */ + smc911x_set_mac_csr(dev, FLOW, FLOW_FCPT | FLOW_FCEN); + smc911x_reg_write(dev, AFC_CFG, 0x0050287F); + + /* Set to LED outputs */ + smc911x_reg_write(dev, GPIO_CFG, 0x70070000); +} + /** * smsc_ctrlc - detect press of CTRL+C (common ctrlc() isnt exported!?) */