From patchwork Thu Mar 12 15:48:42 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Albert ARIBAUD (3ADEV)" X-Patchwork-Id: 449552 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 943901400DE for ; Fri, 13 Mar 2015 02:49:26 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 459BFA7448; Thu, 12 Mar 2015 16:49:20 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id XixXn8tPBFPu; Thu, 12 Mar 2015 16:49:20 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 9EF3EA743F; Thu, 12 Mar 2015 16:49:17 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 9BC9CA7424 for ; Thu, 12 Mar 2015 16:49:05 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id EsIzUN-EYslT for ; Thu, 12 Mar 2015 16:49:05 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from smtp2-g21.free.fr (smtp2-g21.free.fr [212.27.42.2]) by theia.denx.de (Postfix) with ESMTPS id 738AEA7422 for ; Thu, 12 Mar 2015 16:49:00 +0100 (CET) Received: from localhost.localdomain (unknown [82.235.144.2]) (Authenticated sender: aribaud.smtp) by smtp2-g21.free.fr (Postfix) with ESMTPSA id 27BC84B02A3; Thu, 12 Mar 2015 16:47:59 +0100 (CET) From: "Albert ARIBAUD (3ADEV)" To: u-boot@lists.denx.de Date: Thu, 12 Mar 2015 16:48:42 +0100 Message-Id: <1426175329-23720-2-git-send-email-albert.aribaud@3adev.fr> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1426175329-23720-1-git-send-email-albert.aribaud@3adev.fr> References: <1426175329-23720-1-git-send-email-albert.aribaud@3adev.fr> Cc: "Albert ARIBAUD \(3ADEV\)" Subject: [U-Boot] [PATCH v4 1/8] lpc32xx: add Ethernet support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Signed-off-by: Albert ARIBAUD (3ADEV) --- Changes in v4: None Changes in v3: None Changes in v2: None arch/arm/cpu/arm926ejs/lpc32xx/cpu.c | 9 + arch/arm/cpu/arm926ejs/lpc32xx/devices.c | 7 + arch/arm/include/asm/arch-lpc32xx/config.h | 3 + arch/arm/include/asm/arch-lpc32xx/sys_proto.h | 1 + drivers/net/Makefile | 1 + drivers/net/lpc32xx_eth.c | 636 ++++++++++++++++++++++++++ include/netdev.h | 1 + 7 files changed, 658 insertions(+) create mode 100644 drivers/net/lpc32xx_eth.c diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/cpu.c b/arch/arm/cpu/arm926ejs/lpc32xx/cpu.c index 35095a9..eec4d9e 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/cpu.c +++ b/arch/arm/cpu/arm926ejs/lpc32xx/cpu.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -55,3 +56,11 @@ int print_cpuinfo(void) return 0; } #endif + +#ifdef CONFIG_LPC32XX_ETH +int cpu_eth_init(bd_t *bis) +{ + lpc32xx_eth_initialize(bis); + return 0; +} +#endif diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c index b567657..062db8d 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c +++ b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c @@ -37,3 +37,10 @@ void lpc32xx_uart_init(unsigned int uart_id) writel(CLK_UART_X_DIV(1) | CLK_UART_Y_DIV(1), &clk->u3clk + (uart_id - 3)); } + +void lpc32xx_mac_init(void) +{ + /* Enable MAC interface */ + writel(CLK_MAC_REG | CLK_MAC_SLAVE | CLK_MAC_MASTER + | CLK_MAC_MII, &clk->macclk_ctrl); +} diff --git a/arch/arm/include/asm/arch-lpc32xx/config.h b/arch/arm/include/asm/arch-lpc32xx/config.h index 564441c..d57bc48 100644 --- a/arch/arm/include/asm/arch-lpc32xx/config.h +++ b/arch/arm/include/asm/arch-lpc32xx/config.h @@ -52,6 +52,9 @@ #define CONFIG_SYS_BAUDRATE_TABLE \ { 9600, 19200, 38400, 57600, 115200, 230400, 460800 } +/* Ethernet */ +#define LPC32XX_ETH_BASE ETHERNET_BASE + /* NOR Flash */ #if defined(CONFIG_SYS_FLASH_CFI) #define CONFIG_FLASH_CFI_DRIVER diff --git a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h index 28812be..a6b8826 100644 --- a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h +++ b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h @@ -8,5 +8,6 @@ #define _LPC32XX_SYS_PROTO_H void lpc32xx_uart_init(unsigned int uart_id); +void lpc32xx_mac_init(void); #endif /* _LPC32XX_SYS_PROTO_H */ diff --git a/drivers/net/Makefile b/drivers/net/Makefile index b8b0803..af8941f 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_GRETH) += greth.o obj-$(CONFIG_DRIVER_TI_KEYSTONE_NET) += keystone_net.o obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o obj-$(CONFIG_LAN91C96) += lan91c96.o +obj-$(CONFIG_LPC32XX_ETH) += lpc32xx_eth.o obj-$(CONFIG_MACB) += macb.o obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o obj-$(CONFIG_MPC5xxx_FEC) += mpc5xxx_fec.o diff --git a/drivers/net/lpc32xx_eth.c b/drivers/net/lpc32xx_eth.c new file mode 100644 index 0000000..16c8ef0 --- /dev/null +++ b/drivers/net/lpc32xx_eth.c @@ -0,0 +1,636 @@ +/* + * LPC32xx Ethernet MAC interface driver + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD - 3ADEV + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Notes: + * + * 1. Unless specified otherwise, all references to tables or paragraphs + * are to UM10326, "LPC32x0 and LPC32x0/01 User manual". + * + * 2. Only bitfield masks/values which are actually used by the driver + * are defined. + */ + +/* a single RX descriptor. The controller has an array of these */ +struct lpc32xx_eth_rxdesc { + u32 packet; /* Receive packet pointer */ + u32 control; /* Descriptor command status */ +}; + +#define LPC32XX_ETH_RX_DESC_SIZE (sizeof(struct lpc32xx_eth_rxdesc)) + +/* RX control bitfields/masks (see Table 330) */ +#define LPC32XX_ETH_RX_CTRL_SIZE_MASK 0x000007FF +#define LPC32XX_ETH_RX_CTRL_UNUSED 0x7FFFF800 +#define LPC32XX_ETH_RX_CTRL_INTERRUPT 0x80000000 + +/* a single RX status. The controller has an array of these */ +struct lpc32xx_eth_rxstat { + u32 statusinfo; /* Transmit Descriptor status */ + u32 statushashcrc; /* Transmit Descriptor CRCs */ +}; + +#define LPC32XX_ETH_RX_STAT_SIZE (sizeof(struct lpc32xx_eth_rxstat)) + +/* RX statusinfo bitfields/masks (see Table 333) */ +#define RX_STAT_RXSIZE 0x000007FF +/* Helper: OR of all errors except RANGE */ +#define RX_STAT_ERRORS 0x1B800000 + +/* a single TX descriptor. The controller has an array of these */ +struct lpc32xx_eth_txdesc { + u32 packet; /* Transmit packet pointer */ + u32 control; /* Descriptor control */ +}; + +#define LPC32XX_ETH_TX_DESC_SIZE (sizeof(struct lpc32xx_eth_txdesc)) + +/* TX control bitfields/masks (see Table 335) */ +#define TX_CTRL_TXSIZE 0x000007FF +#define TX_CTRL_LAST 0x40000000 + +/* a single TX status. The controller has an array of these */ +struct lpc32xx_eth_txstat { + u32 statusinfo; /* Transmit Descriptor status */ +}; + +#define LPC32XX_ETH_TX_STAT_SIZE (sizeof(struct lpc32xx_eth_txstat)) + +/* Ethernet MAC interface registers (see Table 283) */ +struct lpc32xx_eth_registers { + /* MAC registers - 0x3106_0000 to 0x3106_01FC */ + u32 mac1; /* MAC configuration register 1 */ + u32 mac2; /* MAC configuration register 2 */ + u32 ipgt; /* Back-to-back Inter-Packet Gap reg. */ + u32 ipgr; /* Non-back-to-back IPG register */ + u32 clrt; /* Collision Window / Retry register */ + u32 maxf; /* Maximum Frame register */ + u32 supp; /* Phy Support register */ + u32 test; + u32 mcfg; /* MII management configuration reg. */ + u32 mcmd; /* MII management command register */ + u32 madr; /* MII management address register */ + u32 mwtd; /* MII management wite data register */ + u32 mrdd; /* MII management read data register */ + u32 mind; /* MII management indicators register */ + u32 reserved1[2]; + u32 sa0; /* Station address register 0 */ + u32 sa1; /* Station address register 1 */ + u32 sa2; /* Station address register 2 */ + u32 reserved2[45]; + /* Control registers */ + u32 command; + u32 status; + u32 rxdescriptor; + u32 rxstatus; + u32 rxdescriptornumber; /* actually, number MINUS ONE */ + u32 rxproduceindex; /* head of rx desc fifo */ + u32 rxconsumeindex; /* tail of rx desc fifo */ + u32 txdescriptor; + u32 txstatus; + u32 txdescriptornumber; /* actually, number MINUS ONE */ + u32 txproduceindex; /* head of rx desc fifo */ + u32 txconsumeindex; /* tail of rx desc fifo */ + u32 reserved3[10]; + u32 tsv0; /* Transmit status vector register 0 */ + u32 tsv1; /* Transmit status vector register 1 */ + u32 rsv; /* Receive status vector register */ + u32 reserved4[3]; + u32 flowcontrolcounter; + u32 flowcontrolstatus; + u32 reserved5[34]; + /* RX filter registers - 0x3106_0200 to 0x3106_0FDC */ + u32 rxfilterctrl; + u32 rxfilterwolstatus; + u32 rxfilterwolclear; + u32 reserved6; + u32 hashfilterl; + u32 hashfilterh; + u32 reserved7[882]; + /* Module control registers - 0x3106_0FE0 to 0x3106_0FF8 */ + u32 intstatus; /* Interrupt status register */ + u32 intenable; + u32 intclear; + u32 intset; + u32 reserved8; + u32 powerdown; + u32 reserved9; +}; + +/* MAC1 register bitfields/masks and offsets (see Table 283) */ +#define MAC1_RECV_ENABLE 0x00000001 +#define MAC1_PASS_ALL_RX_FRAMES 0x00000002 +#define MAC1_SOFT_RESET 0x00008000 +/* Helper: general reset */ +#define MAC1_RESETS 0x0000CF00 + +/* MAC2 register bitfields/masks and offsets (see Table 284) */ +#define MAC2_FULL_DUPLEX 0x00000001 +#define MAC2_CRC_ENABLE 0x00000010 +#define MAC2_PAD_CRC_ENABLE 0x00000020 + +/* SUPP register bitfields/masks and offsets (see Table 290) */ +#define SUPP_SPEED 0x00000100 + +/* MCFG register bitfields/masks and offsets (see Table 292) */ +#define MCFG_CLOCK_SELECT_MASK 0x0000001C +/* divide clock by 28 (see Table 293) */ +#define MCFG_CLOCK_SELECT_DIV28 0x0000001C + +/* MADR register bitfields/masks and offsets (see Table 295) */ +#define MADR_REG_MASK 0x0000001F +#define MADR_PHY_MASK 0x00001F00 +#define MADR_REG_OFFSET 0 +#define MADR_PHY_OFFSET 8 + +/* MIND register bitfields/masks (see Table 298) */ +#define MIND_BUSY 0x00000001 + +/* COMMAND register bitfields/masks and offsets (see Table 283) */ +#define COMMAND_RXENABLE 0x00000001 +#define COMMAND_TXENABLE 0x00000002 +#define COMMAND_PASSRUNTFRAME 0x00000040 +#define COMMAND_FULL_DUPLEX 0x00000400 +/* Helper: general reset */ +#define COMMAND_RESETS 0x0000001C + +/* STATUS register bitfields/masks and offsets (see Table 283) */ +#define STATUS_RXSTATUS 0x00000001 +#define STATUS_TXSTATUS 0x00000002 + +/* RXFILTERCTRL register bitfields/masks (see Table 319) */ +#define RXFILTERCTRL_ACCEPTBROADCAST 0x00000002 +#define RXFILTERCTRL_ACCEPTPERFECT 0x00000020 + +/* port device data struct */ +struct lpc32xx_eth_device { + struct eth_device dev; + struct lpc32xx_eth_registers *regs; + struct lpc32xx_eth_txdesc *p_txdesc; + struct lpc32xx_eth_txstat *p_txstat; + struct lpc32xx_eth_rxdesc *p_rxdesc; + struct lpc32xx_eth_rxstat *p_rxstat; + u8 *p_rxbuf; + u8 *p_txbuf; +}; + +#define LPC32XX_ETH_DEVICE_SIZE (sizeof(struct lpc32xx_eth_device)) + +/* generic macros */ +#define to_lpc32xx_eth(_d) container_of(_d, struct lpc32xx_eth_device, dev) + +/* timeout for MII polling */ +#define MII_TIMEOUT 10000000 + +/* limits for PHY and register addresses */ +#define MII_MAX_REG (MADR_REG_MASK >> MADR_REG_OFFSET) + +#define MII_MAX_PHY (MADR_PHY_MASK >> MADR_PHY_OFFSET) + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_PHYLIB) || defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +/* + * mii_reg_read - miiphy_read callback function. + * + * Returns 16bit phy register value, or 0xffff on error + */ +static int mii_reg_read(const char *devname, u8 phy_adr, u8 reg_ofs, u16 *data) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct lpc32xx_eth_device *dlpc32xx_eth = to_lpc32xx_eth(dev); + struct lpc32xx_eth_registers *regs = dlpc32xx_eth->regs; + u32 mind_reg; + u32 timeout; + + /* check parameters */ + if (phy_adr > MII_MAX_PHY) { + printf("%s:%u: Invalid PHY address %d\n", + __func__, __LINE__, phy_adr); + return -EFAULT; + } + if (reg_ofs > MII_MAX_REG) { + printf("%s:%u: Invalid register offset %d\n", + __func__, __LINE__, reg_ofs); + return -EFAULT; + } + + /* write the phy and reg addressse into the MII address reg */ + writel((phy_adr << MADR_PHY_OFFSET) | (reg_ofs << MADR_REG_OFFSET), + ®s->madr); + + /* write 1 to the MII command register to cause a read */ + writel(1, ®s->mcmd); + + /* wait till the MII is not busy */ + timeout = MII_TIMEOUT; + do { + /* read MII indicators register */ + mind_reg = readl(®s->mind); + if (--timeout == 0) + break; + } while (mind_reg & MIND_BUSY); + + /* write 0 to the MII command register to finish the read */ + writel(0, ®s->mcmd); + + if (timeout == 0) { + printf("%s:%u: MII busy timeout\n", __func__, __LINE__); + return -EFAULT; + } + + *data = (u16) readl(®s->mrdd); + + debug("%s:(adr %d, off %d) => %04x\n", __func__, phy_adr, + reg_ofs, *data); + + return 0; +} + +/* + * mii_reg_write - imiiphy_write callback function. + * + * Returns 0 if write succeed, -EINVAL on bad parameters + * -ETIME on timeout + */ +static int mii_reg_write(const char *devname, u8 phy_adr, u8 reg_ofs, u16 data) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct lpc32xx_eth_device *dlpc32xx_eth = to_lpc32xx_eth(dev); + struct lpc32xx_eth_registers *regs = dlpc32xx_eth->regs; + u32 mind_reg; + u32 timeout; + + /* check parameters */ + if (phy_adr > MII_MAX_PHY) { + printf("%s:%u: Invalid PHY address %d\n", + __func__, __LINE__, phy_adr); + return -EFAULT; + } + if (reg_ofs > MII_MAX_REG) { + printf("%s:%u: Invalid register offset %d\n", + __func__, __LINE__, reg_ofs); + return -EFAULT; + } + + /* wait till the MII is not busy */ + timeout = MII_TIMEOUT; + do { + /* read MII indicators register */ + mind_reg = readl(®s->mind); + if (--timeout == 0) + break; + } while (mind_reg & MIND_BUSY); + + if (timeout == 0) { + printf("%s:%u: MII busy timeout\n", __func__, + __LINE__); + return -EFAULT; + } + + /* write the phy and reg addressse into the MII address reg */ + writel((phy_adr << MADR_PHY_OFFSET) | (reg_ofs << MADR_REG_OFFSET), + ®s->madr); + + /* write data to the MII write register */ + writel(data, ®s->mwtd); + + /*debug("%s:(adr %d, off %d) <= %04x\n", __func__, phy_adr, + reg_ofs, data);*/ + + return 0; +} +#endif + +#if defined(CONFIG_PHYLIB) +int lpc32xx_eth_phy_read(struct mii_dev *bus, int phy_addr, int dev_addr, + int reg_addr) +{ + u16 data; + int ret; + ret = mii_reg_read(bus->name, phy_addr, reg_addr, &data); + if (ret) + return ret; + return data; +} + +int lpc32xx_eth_phy_write(struct mii_dev *bus, int phy_addr, int dev_addr, + int reg_addr, u16 data) +{ + return mii_reg_write(bus->name, phy_addr, reg_addr, data); +} +#endif + +/* Use static aligned variables for the driver */ + +/* this driver needs at least 4 RX and 4 TX buffers */ + +#define TX_BUF_COUNT 4 +static __aligned(4) struct lpc32xx_eth_txdesc tx_desc[TX_BUF_COUNT]; +static __aligned(4) struct lpc32xx_eth_txstat tx_stat[TX_BUF_COUNT]; +static __aligned(PKTALIGN) u8 tx_buf[TX_BUF_COUNT*PKTSIZE_ALIGN]; + +#define RX_BUF_COUNT 4 +static __aligned(4) struct lpc32xx_eth_rxdesc rx_desc[RX_BUF_COUNT]; +static __aligned(8) struct lpc32xx_eth_rxstat rx_stat[RX_BUF_COUNT]; +static __aligned(PKTALIGN) u8 rx_buf[RX_BUF_COUNT*PKTSIZE_ALIGN]; + +static struct lpc32xx_eth_device lpc32xx_eth = { + .regs = (void *)LPC32XX_ETH_BASE, + .p_txdesc = tx_desc, + .p_txstat = tx_stat, + .p_rxdesc = rx_desc, + .p_rxstat = rx_stat, + .p_txbuf = tx_buf, + .p_rxbuf = rx_buf, +}; + +#define TX_TIMEOUT 10000 + +static int lpc32xx_eth_send(struct eth_device *dev, void *dataptr, int datasize) +{ + struct lpc32xx_eth_device *lpc32xx_eth_device = + container_of(dev, struct lpc32xx_eth_device, dev); + struct lpc32xx_eth_registers *regs = lpc32xx_eth_device->regs; + int timeout, tx_index; + + /* time out if transmit descriptor array remains full too long */ + timeout = TX_TIMEOUT; + while ((regs->status & STATUS_TXSTATUS) && + (regs->txconsumeindex == regs->txproduceindex)) { + if (timeout-- == 0) { + debug("%s:%u: time out\n", __func__, + __LINE__); + return -1; + } + } + + /* determine next transmit packet index to use */ + tx_index = regs->txproduceindex; + + /* set up transmit packet */ + writel((u32)dataptr, &tx_desc[tx_index].packet); + writel(TX_CTRL_LAST | ((datasize - 1) & TX_CTRL_TXSIZE), + &tx_desc[tx_index].control); + writel(0, &tx_stat[tx_index].statusinfo); + + /* pass transmit packet to DMA engine */ + tx_index = (tx_index + 1) % TX_BUF_COUNT; + writel(tx_index, ®s->txproduceindex); + + /* transmission succeeded */ + return 0; +} + +#define RX_TIMEOUT 1000000 + +static int lpc32xx_eth_recv(struct eth_device *dev) +{ + struct lpc32xx_eth_device *lpc32xx_eth_device = + container_of(dev, struct lpc32xx_eth_device, dev); + struct lpc32xx_eth_registers *regs = lpc32xx_eth_device->regs; + int timeout, rx_index; + + /* time out if receive descriptor array remains empty too long */ + timeout = RX_TIMEOUT; + while (regs->rxproduceindex == regs->rxconsumeindex) { + if (timeout-- == 0) + return -1; + } + + /* determine next receive packet index to use */ + rx_index = regs->rxconsumeindex; + + /* if data was valid, pass it on */ + if (!(rx_stat[rx_index].statusinfo & RX_STAT_ERRORS)) + NetReceive(&(rx_buf[rx_index*PKTSIZE_ALIGN]), + (rx_stat[rx_index].statusinfo & RX_STAT_RXSIZE) + + 1); + + /* pass receive slot back to DMA engine */ + rx_index = (rx_index + 1) % RX_BUF_COUNT; + writel(rx_index, ®s->rxconsumeindex); + + /* reception successful */ + return 0; +} + +static int lpc32xx_eth_write_hwaddr(struct eth_device *dev) +{ + struct lpc32xx_eth_device *lpc32xx_eth_device = + container_of(dev, struct lpc32xx_eth_device, dev); + struct lpc32xx_eth_registers *regs = lpc32xx_eth_device->regs; + + /* Save station address */ + writel((unsigned long) (dev->enetaddr[0] | + (dev->enetaddr[1] << 8)), ®s->sa2); + writel((unsigned long) (dev->enetaddr[2] | + (dev->enetaddr[3] << 8)), ®s->sa1); + writel((unsigned long) (dev->enetaddr[4] | + (dev->enetaddr[5] << 8)), ®s->sa0); + + return 0; +} + +static int lpc32xx_eth_init(struct eth_device *dev) +{ + struct lpc32xx_eth_device *lpc32xx_eth_device = + container_of(dev, struct lpc32xx_eth_device, dev); + struct lpc32xx_eth_registers *regs = lpc32xx_eth_device->regs; + int index; + + /* Release SOFT reset to let MII talk to PHY */ + clrbits_le32(®s->mac1, MAC1_SOFT_RESET); + + /* Configure Full/Half Duplex mode */ + if (miiphy_duplex(dev->name, CONFIG_PHY_ADDR) == FULL) { + setbits_le32(®s->mac2, MAC2_FULL_DUPLEX); + setbits_le32(®s->command, COMMAND_FULL_DUPLEX); + writel(0x15, ®s->ipgt); + } else { + writel(0x12, ®s->ipgt); + } + + /* Configure 100MBit/10MBit mode */ + if (miiphy_speed(dev->name, CONFIG_PHY_ADDR) == _100BASET) + writel(SUPP_SPEED, ®s->supp); + else + writel(0, ®s->supp); + + /* Initial MAC initialization */ + writel(MAC1_PASS_ALL_RX_FRAMES, ®s->mac1); + writel(MAC2_PAD_CRC_ENABLE | MAC2_CRC_ENABLE, ®s->mac2); + writel(PKTSIZE_ALIGN, ®s->maxf); + + /* Retries: 15 (0xF). Collision window: 57 (0x37). */ + writel(0x370F, ®s->clrt); + + /* Set IP gap pt 2 to default 0x12 but pt 1 to non-default 0 */ + writel(0x0012, ®s->ipgr); + + /* pass runt (smaller than 64 bytes) frames */ + writel(COMMAND_PASSRUNTFRAME, ®s->command); + + /* Save station address */ + writel((unsigned long) (dev->enetaddr[0] | + (dev->enetaddr[1] << 8)), ®s->sa2); + writel((unsigned long) (dev->enetaddr[2] | + (dev->enetaddr[3] << 8)), ®s->sa1); + writel((unsigned long) (dev->enetaddr[4] | + (dev->enetaddr[5] << 8)), ®s->sa0); + + /* set up transmit buffers */ + for (index = 0; index < TX_BUF_COUNT; index++) { + tx_desc[index].control = 0; + tx_stat[index].statusinfo = 0; + } + writel((u32)tx_desc, (u32 *)®s->txdescriptor); + writel((u32)tx_stat, ®s->txstatus); + writel(TX_BUF_COUNT-1, ®s->txdescriptornumber); + + /* set up receive buffers */ + for (index = 0; index < RX_BUF_COUNT; index++) { + rx_desc[index].packet = (u32) (rx_buf+index*PKTSIZE_ALIGN); + rx_desc[index].control = PKTSIZE_ALIGN - 1; + rx_stat[index].statusinfo = 0; + rx_stat[index].statushashcrc = 0; + } + writel((u32)rx_desc, ®s->rxdescriptor); + writel((u32)rx_stat, ®s->rxstatus); + writel(RX_BUF_COUNT-1, ®s->rxdescriptornumber); + + /* Enable broadcast and matching address packets */ + writel(RXFILTERCTRL_ACCEPTBROADCAST | + RXFILTERCTRL_ACCEPTPERFECT, ®s->rxfilterctrl); + + /* Clear and disable interrupts */ + writel(0xFFFF, ®s->intclear); + writel(0, ®s->intenable); + + /* Enable receive and transmit mode of MAC ethernet core */ + setbits_le32(®s->command, COMMAND_RXENABLE | COMMAND_TXENABLE); + setbits_le32(®s->mac1, MAC1_RECV_ENABLE); + + /* + * Perform a 'dummy' first send to work around Ethernet.1 + * erratum (see ES_LPC3250 rev. 9 dated 1 June 2011). + * Use zeroed "index" variable as the dummy. + */ + + index = 0; + lpc32xx_eth_send(dev, &index, 4); + + return 0; +} + +static int lpc32xx_eth_halt(struct eth_device *dev) +{ + struct lpc32xx_eth_device *lpc32xx_eth_device = + container_of(dev, struct lpc32xx_eth_device, dev); + struct lpc32xx_eth_registers *regs = lpc32xx_eth_device->regs; + + /* Reset all MAC logic */ + writel(MAC1_RESETS, ®s->mac1); + writel(COMMAND_RESETS, ®s->command); + /* Let reset condition settle */ + udelay(2000); + + return 0; +} + +#if defined(CONFIG_PHYLIB) +int lpc32xx_eth_phylib_init(struct eth_device *dev, int phyid) +{ + struct mii_dev *bus; + struct phy_device *phydev; + int ret; + + bus = mdio_alloc(); + if (!bus) { + printf("mdio_alloc failed\n"); + return -ENOMEM; + } + bus->read = lpc32xx_eth_phy_read; + bus->write = lpc32xx_eth_phy_write; + sprintf(bus->name, dev->name); + + ret = mdio_register(bus); + if (ret) { + printf("mdio_register failed\n"); + free(bus); + return -ENOMEM; + } + + phydev = phy_connect(bus, phyid, dev, PHY_INTERFACE_MODE_MII); + if (!phydev) { + printf("phy_connect failed\n"); + return -ENODEV; + } + + phy_config(phydev); + phy_startup(phydev); + + return 0; +} +#endif + +int lpc32xx_eth_initialize(bd_t *bis) +{ + struct eth_device *dev = &lpc32xx_eth.dev; + struct lpc32xx_eth_registers *regs = lpc32xx_eth.regs; + + /* + * Set RMII management clock rate. With HCLK at 104 MHz and + * a divider of 28, this will be 3.72 MHz. + */ + + writel(MCFG_CLOCK_SELECT_DIV28, ®s->mcfg); + + /* Reset all MAC logic */ + writel(MAC1_RESETS, ®s->mac1); + writel(COMMAND_RESETS, ®s->command); + + /* wait 10 ms for the whole I/F to reset */ + udelay(10000); + + /* must be less than sizeof(dev->name) */ + strcpy(dev->name, "eth0"); + + dev->init = (void *)lpc32xx_eth_init; + dev->halt = (void *)lpc32xx_eth_halt; + dev->send = (void *)lpc32xx_eth_send; + dev->recv = (void *)lpc32xx_eth_recv; + dev->write_hwaddr = (void *)lpc32xx_eth_write_hwaddr; + + /* Release SOFT reset to let MII talk to PHY */ + clrbits_le32(®s->mac1, MAC1_SOFT_RESET); + + /* register driver before talking to phy */ + eth_register(dev); + +#if defined(CONFIG_PHYLIB) + lpc32xx_eth_phylib_init(dev, 0); +#elif defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, mii_reg_read, mii_reg_write); +#endif + + return 0; +} diff --git a/include/netdev.h b/include/netdev.h index 90140bd..453c940 100644 --- a/include/netdev.h +++ b/include/netdev.h @@ -57,6 +57,7 @@ int greth_initialize(bd_t *bis); void gt6426x_eth_initialize(bd_t *bis); int ks8851_mll_initialize(u8 dev_num, int base_addr); int lan91c96_initialize(u8 dev_num, int base_addr); +int lpc32xx_eth_initialize(bd_t *bis); int macb_eth_initialize(int id, void *regs, unsigned int phy_addr); int mcdmafec_initialize(bd_t *bis); int mcffec_initialize(bd_t *bis);