diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/designware.c | 128 | ||||
-rw-r--r-- | drivers/net/designware.h | 3 | ||||
-rw-r--r-- | drivers/net/mvgbe.c | 2 | ||||
-rw-r--r-- | drivers/net/xilinx_ll_temac.c | 399 | ||||
-rw-r--r-- | drivers/net/xilinx_ll_temac.h | 310 | ||||
-rw-r--r-- | drivers/net/xilinx_ll_temac_fifo.c | 143 | ||||
-rw-r--r-- | drivers/net/xilinx_ll_temac_fifo.h | 122 | ||||
-rw-r--r-- | drivers/net/xilinx_ll_temac_mdio.c | 180 | ||||
-rw-r--r-- | drivers/net/xilinx_ll_temac_mdio.h | 53 | ||||
-rw-r--r-- | drivers/net/xilinx_ll_temac_sdma.c | 370 | ||||
-rw-r--r-- | drivers/net/xilinx_ll_temac_sdma.h | 281 |
12 files changed, 1954 insertions, 39 deletions
diff --git a/drivers/net/Makefile b/drivers/net/Makefile index f4f7ea3..430f90c 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -77,6 +77,8 @@ COBJS-$(CONFIG_ULI526X) += uli526x.o COBJS-$(CONFIG_VSC7385_ENET) += vsc7385.o COBJS-$(CONFIG_XILINX_AXIEMAC) += xilinx_axi_emac.o COBJS-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o +COBJS-$(CONFIG_XILINX_LL_TEMAC) += xilinx_ll_temac.o xilinx_ll_temac_mdio.o \ + xilinx_ll_temac_fifo.o xilinx_ll_temac_sdma.o COBJS := $(sort $(COBJS-y)) SRCS := $(COBJS:.o=.c) diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 1e34436..e8e669b 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -32,6 +32,8 @@ #include <asm/io.h> #include "designware.h" +static int configure_phy(struct eth_device *dev); + static void tx_descs_init(struct eth_device *dev) { struct dw_eth_dev *priv = dev->priv; @@ -106,16 +108,20 @@ static int mac_reset(struct eth_device *dev) struct eth_mac_regs *mac_p = priv->mac_regs_p; struct eth_dma_regs *dma_p = priv->dma_regs_p; + ulong start; int timeout = CONFIG_MACRESET_TIMEOUT; writel(DMAMAC_SRST, &dma_p->busmode); writel(MII_PORTSELECT, &mac_p->conf); - do { + start = get_timer(0); + while (get_timer(start) < timeout) { if (!(readl(&dma_p->busmode) & DMAMAC_SRST)) return 0; - udelay(1000); - } while (timeout--); + + /* Try again after 10usec */ + udelay(10); + }; return -1; } @@ -144,10 +150,16 @@ static int dw_eth_init(struct eth_device *dev, bd_t *bis) struct eth_dma_regs *dma_p = priv->dma_regs_p; u32 conf; + if (priv->phy_configured != 1) + configure_phy(dev); + /* Reset ethernet hardware */ if (mac_reset(dev) < 0) return -1; + /* Resore the HW MAC address as it has been lost during MAC reset */ + dw_write_hwaddr(dev); + writel(FIXEDBURST | PRIORXTX_41 | BURST_16, &dma_p->busmode); @@ -172,8 +184,7 @@ static int dw_eth_init(struct eth_device *dev, bd_t *bis) writel(readl(&dma_p->opmode) | RXSTART, &dma_p->opmode); writel(readl(&dma_p->opmode) | TXSTART, &dma_p->opmode); - writel(readl(&mac_p->conf) | RXENABLE, &mac_p->conf); - writel(readl(&mac_p->conf) | TXENABLE, &mac_p->conf); + writel(readl(&mac_p->conf) | RXENABLE | TXENABLE, &mac_p->conf); return 0; } @@ -266,6 +277,7 @@ static int eth_mdio_read(struct eth_device *dev, u8 addr, u8 reg, u16 *val) { struct dw_eth_dev *priv = dev->priv; struct eth_mac_regs *mac_p = priv->mac_regs_p; + ulong start; u32 miiaddr; int timeout = CONFIG_MDIO_TIMEOUT; @@ -274,13 +286,16 @@ static int eth_mdio_read(struct eth_device *dev, u8 addr, u8 reg, u16 *val) writel(miiaddr | MII_CLKRANGE_150_250M | MII_BUSY, &mac_p->miiaddr); - do { + start = get_timer(0); + while (get_timer(start) < timeout) { if (!(readl(&mac_p->miiaddr) & MII_BUSY)) { *val = readl(&mac_p->miidata); return 0; } - udelay(1000); - } while (timeout--); + + /* Try again after 10usec */ + udelay(10); + }; return -1; } @@ -289,6 +304,7 @@ static int eth_mdio_write(struct eth_device *dev, u8 addr, u8 reg, u16 val) { struct dw_eth_dev *priv = dev->priv; struct eth_mac_regs *mac_p = priv->mac_regs_p; + ulong start; u32 miiaddr; int ret = -1, timeout = CONFIG_MDIO_TIMEOUT; u16 value; @@ -299,11 +315,16 @@ static int eth_mdio_write(struct eth_device *dev, u8 addr, u8 reg, u16 val) writel(miiaddr | MII_CLKRANGE_150_250M | MII_BUSY, &mac_p->miiaddr); - do { - if (!(readl(&mac_p->miiaddr) & MII_BUSY)) + start = get_timer(0); + while (get_timer(start) < timeout) { + if (!(readl(&mac_p->miiaddr) & MII_BUSY)) { ret = 0; - udelay(1000); - } while (timeout--); + break; + } + + /* Try again after 10usec */ + udelay(10); + }; /* Needed as a fix for ST-Phy */ eth_mdio_read(dev, addr, reg, &value); @@ -344,18 +365,23 @@ static int dw_reset_phy(struct eth_device *dev) { struct dw_eth_dev *priv = dev->priv; u16 ctrl; + ulong start; int timeout = CONFIG_PHYRESET_TIMEOUT; u32 phy_addr = priv->address; eth_mdio_write(dev, phy_addr, MII_BMCR, BMCR_RESET); - do { + + start = get_timer(0); + while (get_timer(start) < timeout) { eth_mdio_read(dev, phy_addr, MII_BMCR, &ctrl); if (!(ctrl & BMCR_RESET)) break; - udelay(1000); - } while (timeout--); - if (timeout < 0) + /* Try again after 10usec */ + udelay(10); + }; + + if (get_timer(start) >= CONFIG_PHYRESET_TIMEOUT) return -1; #ifdef CONFIG_PHY_RESET_DELAY @@ -372,6 +398,7 @@ static int configure_phy(struct eth_device *dev) #if defined(CONFIG_DW_AUTONEG) u16 bmsr; u32 timeout; + ulong start; u16 anlpar, btsr; #else u16 ctrl; @@ -379,7 +406,7 @@ static int configure_phy(struct eth_device *dev) #if defined(CONFIG_DW_SEARCH_PHY) phy_addr = find_phy(dev); - if (phy_addr > 0) + if (phy_addr >= 0) priv->address = phy_addr; else return -1; @@ -390,8 +417,10 @@ static int configure_phy(struct eth_device *dev) return -1; #if defined(CONFIG_DW_AUTONEG) - bmcr = BMCR_ANENABLE | BMCR_ANRESTART | BMCR_SPEED100 | \ - BMCR_FULLDPLX | BMCR_SPEED1000; + /* Set Auto-Neg Advertisement capabilities to 10/100 half/full */ + eth_mdio_write(dev, phy_addr, MII_ADVERTISE, 0x1E1); + + bmcr = BMCR_ANENABLE | BMCR_ANRESTART; #else bmcr = BMCR_SPEED100 | BMCR_FULLDPLX; @@ -408,33 +437,56 @@ static int configure_phy(struct eth_device *dev) /* Read the phy status register and populate priv structure */ #if defined(CONFIG_DW_AUTONEG) timeout = CONFIG_AUTONEG_TIMEOUT; - do { + start = get_timer(0); + + while (get_timer(start) < timeout) { eth_mdio_read(dev, phy_addr, MII_BMSR, &bmsr); if (bmsr & BMSR_ANEGCOMPLETE) break; - udelay(1000); - } while (timeout--); + + /* Try again after 10usec */ + udelay(10); + }; eth_mdio_read(dev, phy_addr, MII_LPA, &anlpar); eth_mdio_read(dev, phy_addr, MII_STAT1000, &btsr); - if (btsr & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) { - priv->speed = SPEED_1000M; - if (btsr & PHY_1000BTSR_1000FD) + if (bmsr & BMSR_ANEGCOMPLETE) { + if (btsr & PHY_1000BTSR_1000FD) { + priv->speed = SPEED_1000M; + bmcr |= BMCR_SPEED1000; priv->duplex = FULL_DUPLEX; - else + bmcr |= BMCR_FULLDPLX; + } else if (btsr & PHY_1000BTSR_1000HD) { + priv->speed = SPEED_1000M; + bmcr |= BMCR_SPEED1000; priv->duplex = HALF_DUPLEX; - } else { - if (anlpar & LPA_100) + bmcr &= ~BMCR_FULLDPLX; + } else if (anlpar & LPA_100FULL) { priv->speed = SPEED_100M; - else - priv->speed = SPEED_10M; - - if (anlpar & (LPA_10FULL | LPA_100FULL)) + bmcr |= BMCR_SPEED100; priv->duplex = FULL_DUPLEX; - else + bmcr |= BMCR_FULLDPLX; + } else if (anlpar & LPA_100HALF) { + priv->speed = SPEED_100M; + bmcr |= BMCR_SPEED100; priv->duplex = HALF_DUPLEX; - } + bmcr &= ~BMCR_FULLDPLX; + } else if (anlpar & LPA_10FULL) { + priv->speed = SPEED_10M; + bmcr &= ~BMCR_SPEED100; + priv->duplex = FULL_DUPLEX; + bmcr |= BMCR_FULLDPLX; + } else { + priv->speed = SPEED_10M; + bmcr &= ~BMCR_SPEED100; + priv->duplex = HALF_DUPLEX; + bmcr &= ~BMCR_FULLDPLX; + } + if (eth_mdio_write(dev, phy_addr, MII_BMCR, bmcr) < 0) + return -1; + } else + return -1; #else if (eth_mdio_read(dev, phy_addr, MII_BMCR, &ctrl) < 0) return -1; @@ -451,6 +503,8 @@ static int configure_phy(struct eth_device *dev) else priv->speed = SPEED_10M; #endif + priv->phy_configured = 1; + return 0; } @@ -511,14 +565,12 @@ int designware_initialize(u32 id, ulong base_addr, u32 phy_addr) priv->dma_regs_p = (struct eth_dma_regs *)(base_addr + DW_DMA_BASE_OFFSET); priv->address = phy_addr; + priv->phy_configured = 0; if (mac_reset(dev) < 0) return -1; - if (configure_phy(dev) < 0) { - printf("Phy could not be configured\n"); - return -1; - } + configure_phy(dev); dev->init = dw_eth_init; dev->send = dw_eth_send; diff --git a/drivers/net/designware.h b/drivers/net/designware.h index e5828a6..abf729d 100644 --- a/drivers/net/designware.h +++ b/drivers/net/designware.h @@ -121,7 +121,7 @@ struct eth_dma_regs { #define RXSTART (1 << 1) /* Descriptior related definitions */ -#define MAC_MAX_FRAME_SZ (2048) +#define MAC_MAX_FRAME_SZ (1600) struct dmamacdescr { u32 txrx_status; @@ -238,6 +238,7 @@ struct dw_eth_dev { u32 duplex; u32 tx_currdescnum; u32 rx_currdescnum; + u32 phy_configured; u32 padding; struct dmamacdescr tx_mac_descrtable[CONFIG_TX_DESCR_NUM]; diff --git a/drivers/net/mvgbe.c b/drivers/net/mvgbe.c index 477bf5c..dcc1547 100644 --- a/drivers/net/mvgbe.c +++ b/drivers/net/mvgbe.c @@ -52,6 +52,7 @@ DECLARE_GLOBAL_DATA_PTR; #define MV_PHY_ADR_REQUEST 0xee #define MVGBE_SMI_REG (((struct mvgbe_registers *)MVGBE0_BASE)->smi) +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) /* * smi_reg_read - miiphy_read callback function. * @@ -181,6 +182,7 @@ static int smi_reg_write(const char *devname, u8 phy_adr, u8 reg_ofs, u16 data) return 0; } +#endif /* Stop and checks all queues */ static void stop_queue(u32 * qreg) diff --git a/drivers/net/xilinx_ll_temac.c b/drivers/net/xilinx_ll_temac.c new file mode 100644 index 0000000..85660c0 --- /dev/null +++ b/drivers/net/xilinx_ll_temac.c @@ -0,0 +1,399 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * supports SDMA or FIFO access and MDIO bus communication + * + * Copyright (C) 2011 - 2012 Stephan Linz <linz@li-pro.net> + * Copyright (C) 2008 - 2011 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ + +#include <config.h> +#include <common.h> +#include <net.h> +#include <netdev.h> +#include <malloc.h> +#include <asm/io.h> +#include <miiphy.h> + +#include "xilinx_ll_temac.h" +#include "xilinx_ll_temac_fifo.h" +#include "xilinx_ll_temac_sdma.h" +#include "xilinx_ll_temac_mdio.h" + +#if !defined(CONFIG_MII) +# error "LL_TEMAC requires MII -- missing CONFIG_MII" +#endif + +#if !defined(CONFIG_PHYLIB) +# error "LL_TEMAC requires PHYLIB -- missing CONFIG_PHYLIB" +#endif + +struct ll_temac_info { + int flags; + unsigned long base_addr; + unsigned long ctrl_addr; + char *devname; + unsigned int phyaddr; + char *mdio_busname; +}; + +/* Ethernet interface ready status */ +int ll_temac_check_status(struct temac_reg *regs, u32 mask) +{ + unsigned timeout = 50; /* 1usec * 50 = 50usec */ + + /* + * Quote from LL TEMAC documentation: The bits in the RDY + * register are asserted when there is no access in progress. + * When an access is in progress, a bit corresponding to the + * type of access is automatically de-asserted. The bit is + * automatically re-asserted when the access is complete. + */ + while (timeout && (!(in_be32(®s->rdy) & mask))) { + timeout--; + udelay(1); + } + + if (!timeout) { + printf("%s: Timeout on 0x%08x @%p\n", __func__, + mask, ®s->rdy); + return 1; + } + + return 0; +} + +/* + * Indirect write to ll_temac. + * + * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf + * page 23, second paragraph, The use of CTL0 register or CTL1 register + */ +int ll_temac_indirect_set(struct temac_reg *regs, u16 regn, u32 reg_data) +{ + out_be32(®s->lsw, (reg_data & MLSW_MASK)); + out_be32(®s->ctl, CTL_WEN | (regn & CTL_ADDR_MASK)); + + if (ll_temac_check_status(regs, RSE_CFG_WR)) + return 0; + + return 1; +} + +/* + * Indirect read from ll_temac. + * + * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf + * page 23, second paragraph, The use of CTL0 register or CTL1 register + */ +int ll_temac_indirect_get(struct temac_reg *regs, u16 regn, u32* reg_data) +{ + out_be32(®s->ctl, (regn & CTL_ADDR_MASK)); + + if (ll_temac_check_status(regs, RSE_CFG_RR)) + return 0; + + *reg_data = in_be32(®s->lsw) & MLSW_MASK; + return 1; +} + +/* setting sub-controller and ll_temac to proper setting */ +static int ll_temac_setup_ctrl(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + struct temac_reg *regs = (struct temac_reg *)dev->iobase; + + if (ll_temac->ctrlreset && ll_temac->ctrlreset(dev)) + return 0; + + if (ll_temac->ctrlinit && ll_temac->ctrlinit(dev)) + return 0; + + /* Promiscuous mode disable */ + if (!ll_temac_indirect_set(regs, TEMAC_AFM, 0)) + return 0; + + /* Enable Receiver - RX bit */ + if (!ll_temac_indirect_set(regs, TEMAC_RCW1, RCW1_RX)) + return 0; + + /* Enable Transmitter - TX bit */ + if (!ll_temac_indirect_set(regs, TEMAC_TC, TC_TX)) + return 0; + + return 1; +} + +/* + * Configure ll_temac based on negotiated speed and duplex + * reported by PHY handling code + */ +static int ll_temac_adjust_link(struct eth_device *dev) +{ + unsigned int speed, emmc_reg; + struct temac_reg *regs = (struct temac_reg *)dev->iobase; + struct ll_temac *ll_temac = dev->priv; + struct phy_device *phydev = ll_temac->phydev; + + if (!phydev->link) { + printf("%s: No link.\n", phydev->dev->name); + return 0; + } + + switch (phydev->speed) { + case 1000: + speed = EMMC_LSPD_1000; + break; + case 100: + speed = EMMC_LSPD_100; + break; + case 10: + speed = EMMC_LSPD_10; + break; + default: + return 0; + } + + if (!ll_temac_indirect_get(regs, TEMAC_EMMC, &emmc_reg)) + return 0; + + emmc_reg &= ~EMMC_LSPD_MASK; + emmc_reg |= speed; + + if (!ll_temac_indirect_set(regs, TEMAC_EMMC, emmc_reg)) + return 0; + + printf("%s: PHY is %s with %dbase%s, %s%s\n", + dev->name, phydev->drv->name, + phydev->speed, (phydev->port == PORT_TP) ? "T" : "X", + (phydev->duplex) ? "FDX" : "HDX", + (phydev->port == PORT_OTHER) ? ", unkown mode" : ""); + + return 1; +} + +/* setup mac addr */ +static int ll_temac_setup_mac_addr(struct eth_device *dev) +{ + struct temac_reg *regs = (struct temac_reg *)dev->iobase; + u32 val; + + /* set up unicast MAC address filter */ + val = ((dev->enetaddr[3] << 24) | (dev->enetaddr[2] << 16) | + (dev->enetaddr[1] << 8) | (dev->enetaddr[0])); + val &= UAW0_UADDR_MASK; + + if (!ll_temac_indirect_set(regs, TEMAC_UAW0, val)) + return 1; + + val = ((dev->enetaddr[5] << 8) | dev->enetaddr[4]); + val &= UAW1_UADDR_MASK; + + if (!ll_temac_indirect_set(regs, TEMAC_UAW1, val)) + return 1; + + return 0; +} + +/* halt device */ +static void ll_temac_halt(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + struct temac_reg *regs = (struct temac_reg *)dev->iobase; + + /* Disable Receiver */ + ll_temac_indirect_set(regs, TEMAC_RCW0, 0); + + /* Disable Transmitter */ + ll_temac_indirect_set(regs, TEMAC_TC, 0); + + if (ll_temac->ctrlhalt) + ll_temac->ctrlhalt(dev); + + /* Shut down the PHY, as needed */ + phy_shutdown(ll_temac->phydev); +} + +static int ll_temac_init(struct eth_device *dev, bd_t *bis) +{ + struct ll_temac *ll_temac = dev->priv; + + printf("%s: Xilinx XPS LocalLink Tri-Mode Ether MAC #%d at 0x%08X.\n", + dev->name, dev->index, dev->iobase); + + if (!ll_temac_setup_ctrl(dev)) + return -1; + + /* Start up the PHY */ + phy_startup(ll_temac->phydev); + + if (!ll_temac_adjust_link(dev)) { + ll_temac_halt(dev); + return -1; + } + + /* If there's no link, fail */ + return ll_temac->phydev->link ? 0 : -1; +} + +/* + * Discover which PHY is attached to the device, and configure it + * properly. If the PHY is not recognized, then return 0 + * (failure). Otherwise, return 1 + */ +static int ll_temac_phy_init(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + struct phy_device *phydev; + unsigned int supported = PHY_GBIT_FEATURES; + + /* interface - look at driver/net/tsec.c */ + phydev = phy_connect(ll_temac->bus, ll_temac->phyaddr, + dev, PHY_INTERFACE_MODE_NONE); + + phydev->supported &= supported; + phydev->advertising = phydev->supported; + + ll_temac->phydev = phydev; + + phy_config(phydev); + + return 1; +} + +/* + * Initialize a single ll_temac devices + * + * Returns the result of ll_temac phy interface that were initialized + */ +int xilinx_ll_temac_initialize(bd_t *bis, struct ll_temac_info *devinf) +{ + struct eth_device *dev; + struct ll_temac *ll_temac; + + dev = calloc(1, sizeof(*dev)); + if (dev == NULL) + return 0; + + ll_temac = calloc(1, sizeof(struct ll_temac)); + if (ll_temac == NULL) { + free(dev); + return 0; + } + + /* use given name or generate its own unique name */ + if (devinf->devname) { + strncpy(dev->name, devinf->devname, NAMESIZE); + } else { + snprintf(dev->name, NAMESIZE, "lltemac.%lx", devinf->base_addr); + devinf->devname = dev->name; + } + + dev->iobase = devinf->base_addr; + + dev->priv = ll_temac; + dev->init = ll_temac_init; + dev->halt = ll_temac_halt; + dev->write_hwaddr = ll_temac_setup_mac_addr; + + ll_temac->ctrladdr = devinf->ctrl_addr; + if (devinf->flags & XILINX_LL_TEMAC_M_SDMA_PLB) { +#if defined(CONFIG_XILINX_440) || defined(CONFIG_XILINX_405) + if (devinf->flags & XILINX_LL_TEMAC_M_SDMA_DCR) { + ll_temac_collect_xldcr_sdma_reg_addr(dev); + ll_temac->in32 = ll_temac_xldcr_in32; + ll_temac->out32 = ll_temac_xldcr_out32; + } else +#endif + { + ll_temac_collect_xlplb_sdma_reg_addr(dev); + ll_temac->in32 = ll_temac_xlplb_in32; + ll_temac->out32 = ll_temac_xlplb_out32; + } + ll_temac->ctrlinit = ll_temac_init_sdma; + ll_temac->ctrlhalt = ll_temac_halt_sdma; + ll_temac->ctrlreset = ll_temac_reset_sdma; + dev->recv = ll_temac_recv_sdma; + dev->send = ll_temac_send_sdma; + } else { + ll_temac->in32 = NULL; + ll_temac->out32 = NULL; + ll_temac->ctrlinit = NULL; + ll_temac->ctrlhalt = NULL; + ll_temac->ctrlreset = ll_temac_reset_fifo; + dev->recv = ll_temac_recv_fifo; + dev->send = ll_temac_send_fifo; + } + + /* Link to specified MDIO bus */ + strncpy(ll_temac->mdio_busname, devinf->mdio_busname, MDIO_NAME_LEN); + ll_temac->bus = miiphy_get_dev_by_name(ll_temac->mdio_busname); + + /* Looking for a valid PHY address if it is not yet set */ + if (devinf->phyaddr == -1) + ll_temac->phyaddr = ll_temac_phy_addr(ll_temac->bus); + else + ll_temac->phyaddr = devinf->phyaddr; + + eth_register(dev); + + /* Try to initialize PHY here, and return */ + return ll_temac_phy_init(dev); +} + +/* + * Initialize a single ll_temac device with its mdio bus behind ll_temac + * + * Returns 1 if the ll_temac device and the mdio bus were initialized + * otherwise returns 0 + */ +int xilinx_ll_temac_eth_init(bd_t *bis, unsigned long base_addr, int flags, + unsigned long ctrl_addr) +{ + struct ll_temac_info devinf; + struct ll_temac_mdio_info mdioinf; + int ret; + + /* prepare the internal driver informations */ + devinf.flags = flags; + devinf.base_addr = base_addr; + devinf.ctrl_addr = ctrl_addr; + devinf.devname = NULL; + devinf.phyaddr = -1; + + mdioinf.name = devinf.mdio_busname = NULL; + mdioinf.regs = (struct temac_reg *)devinf.base_addr; + + ret = xilinx_ll_temac_mdio_initialize(bis, &mdioinf); + if (ret >= 0) { + + /* + * If there was no MDIO bus name then take over the + * new automaticaly generated by the MDIO init code. + */ + if (mdioinf.name != devinf.mdio_busname) + devinf.mdio_busname = mdioinf.name; + + ret = xilinx_ll_temac_initialize(bis, &devinf); + if (ret > 0) + return 1; + + } + + return 0; +} diff --git a/drivers/net/xilinx_ll_temac.h b/drivers/net/xilinx_ll_temac.h new file mode 100644 index 0000000..ece3b60 --- /dev/null +++ b/drivers/net/xilinx_ll_temac.h @@ -0,0 +1,310 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * LL_TEMAC interface + * + * Copyright (C) 2011 - 2012 Stephan Linz <linz@li-pro.net> + * Copyright (C) 2008 - 2011 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ +#ifndef _XILINX_LL_TEMAC_ +#define _XILINX_LL_TEMAC_ + +#include <config.h> +#include <net.h> +#include <phy.h> +#include <miiphy.h> + +#include <asm/types.h> +#include <asm/byteorder.h> + +#include "xilinx_ll_temac_sdma.h" + +#if !defined(__BIG_ENDIAN) +# error LL_TEMAC requires big endianess +#endif + +/* + * TEMAC Memory and Register Definition + * + * [1]: [0]/ip_documentation/xps_ll_temac.pdf + * page 19, Memory and Register Descriptions + */ +struct temac_reg { + /* direct soft registers (low part) */ + u32 raf; /* Reset and Address Filter */ + u32 tpf; /* Transmit Pause Frame */ + u32 ifgp; /* Transmit Inter Frame Gap Adjustment */ + u32 is; /* Interrupt Status */ + u32 ip; /* Interrupt Pending */ + u32 ie; /* Interrupt Enable */ + u32 ttag; /* Transmit VLAN Tag */ + u32 rtag; /* Receive VLAN Tag */ + /* hard TEMAC registers */ + u32 msw; /* Most Significant Word Data */ + u32 lsw; /* Least Significant Word Data */ + u32 ctl; /* Control */ + u32 rdy; /* Ready Status */ + /* direct soft registers (high part) */ + u32 uawl; /* Unicast Address Word Lower */ + u32 uawu; /* Unicast Address Word Upper */ + u32 tpid0; /* VLAN TPID Word 0 */ + u32 tpid1; /* VLAN TPID Word 1 */ +}; + +/* Reset and Address Filter Registers (raf), [1] p25 */ +#define RAF_SR (1 << 13) +#define RAF_EMFE (1 << 12) +#define RAF_NFE (1 << 11) +#define RAF_RVSTM_POS 9 +#define RAF_RVSTM_MASK (3 << RAF_RVSTM_POS) +#define RAF_TVSTM_POS 7 +#define RAF_TVSTM_MASK (3 << RAF_TVSTM_POS) +#define RAF_RVTM_POS 5 +#define RAF_RVTM_MASK (3 << RAF_RVTM_POS) +#define RAF_TVTM_POS 3 +#define RAF_TVTM_MASK (3 << RAF_TVTM_POS) +#define RAF_BCREJ (1 << 2) +#define RAF_MCREJ (1 << 1) +#define RAF_HTRST (1 << 0) + +/* Transmit Pause Frame Registers (tpf), [1] p28 */ +#define TPF_TPFV_POS 0 +#define TPF_TPFV_MASK (0xFFFF << TPF_TPFV_POS) + +/* Transmit Inter Frame Gap Adjustment Registers (ifgp), [1] p28 */ +#define IFGP_POS 0 +#define IFGP_MASK (0xFF << IFGP_POS) + +/* Interrupt Status, Pending, Enable Registers (is, ip, ie), [1] p29-33 */ +#define ISPE_MR (1 << 7) +#define ISPE_RDL (1 << 6) +#define ISPE_TC (1 << 5) +#define ISPE_RFO (1 << 4) +#define ISPE_RR (1 << 3) +#define ISPE_RC (1 << 2) +#define ISPE_AN (1 << 1) +#define ISPE_HAC (1 << 0) + +/* Transmit, Receive VLAN Tag Registers (ttag, rtag), [1] p34-35 */ +#define TRTAG_TPID_POS 16 +#define TRTAG_TPID_MASK (0xFFFF << TRTAG_TPID_POS) +#define TRTAG_PRIO_POS 13 +#define TRTAG_PRIO_MASK (7 << TRTAG_PRIO_POS) +#define TRTAG_CFI (1 << 12) +#define TRTAG_VID_POS 0 +#define TRTAG_VID_MASK (0xFFF << TRTAG_VID_POS) + +/* Most, Least Significant Word Data Register (msw, lsw), [1] p46 */ +#define MLSW_POS 0 +#define MLSW_MASK (~0UL << MLSW_POS) + +/* LSW Data Register for PHY addresses (lsw), [1] p66 */ +#define LSW_REGAD_POS 0 +#define LSW_REGAD_MASK (0x1F << LSW_REGAD_POS) +#define LSW_PHYAD_POS 5 +#define LSW_PHYAD_MASK (0x1F << LSW_PHYAD_POS) + +/* LSW Data Register for PHY data (lsw), [1] p66 */ +#define LSW_REGDAT_POS 0 +#define LSW_REGDAT_MASK (0xFFFF << LSW_REGDAT_POS) + +/* Control Register (ctl), [1] p47 */ +#define CTL_WEN (1 << 15) +#define CTL_ADDR_POS 0 +#define CTL_ADDR_MASK (0x3FF << CTL_ADDR_POS) + +/* Ready Status Register Ethernet (rdy), [1] p48 */ +#define RSE_HACS_RDY (1 << 14) +#define RSE_CFG_WR (1 << 6) +#define RSE_CFG_RR (1 << 5) +#define RSE_AF_WR (1 << 4) +#define RSE_AF_RR (1 << 3) +#define RSE_MIIM_WR (1 << 2) +#define RSE_MIIM_RR (1 << 1) +#define RSE_FABR_RR (1 << 0) + +/* Unicast Address Word Lower, Upper Registers (uawl, uawu), [1] p35-36 */ +#define UAWL_UADDR_POS 0 +#define UAWL_UADDR_MASK (~0UL << UAWL_UADDR_POS) +#define UAWU_UADDR_POS 0 +#define UAWU_UADDR_MASK (0xFFFF << UAWU_UADDR_POS) + +/* VLAN TPID Word 0, 1 Registers (tpid0, tpid1), [1] p37 */ +#define TPID0_V0_POS 0 +#define TPID0_V0_MASK (0xFFFF << TPID0_V0_POS) +#define TPID0_V1_POS 16 +#define TPID0_V1_MASK (0xFFFF << TPID0_V1_POS) +#define TPID1_V2_POS 0 +#define TPID1_V2_MASK (0xFFFF << TPID1_V2_POS) +#define TPID1_V3_POS 16 +#define TPID1_V3_MASK (0xFFFF << TPID1_V3_POS) + +/* + * TEMAC Indirectly Addressable Register Index Enumeration + * + * [0]: http://www.xilinx.com/support/documentation + * + * [1]: [0]/ip_documentation/xps_ll_temac.pdf + * page 23, PLB Indirectly Addressable TEMAC Registers + */ +enum temac_ctrl { + TEMAC_RCW0 = 0x200, + TEMAC_RCW1 = 0x240, + TEMAC_TC = 0x280, + TEMAC_FCC = 0x2C0, + TEMAC_EMMC = 0x300, + TEMAC_PHYC = 0x320, + TEMAC_MC = 0x340, + TEMAC_UAW0 = 0x380, + TEMAC_UAW1 = 0x384, + TEMAC_MAW0 = 0x388, + TEMAC_MAW1 = 0x38C, + TEMAC_AFM = 0x390, + TEMAC_TIS = 0x3A0, + TEMAC_TIE = 0x3A4, + TEMAC_MIIMWD = 0x3B0, + TEMAC_MIIMAI = 0x3B4 +}; + +/* Receive Configuration Word 0, 1 Registers (RCW0, RCW1), [1] p50-51 */ +#define RCW0_PADDR_POS 0 +#define RCW0_PADDR_MASK (~0UL << RCW_PADDR_POS) +#define RCW1_RST (1 << 31) +#define RCW1_JUM (1 << 30) +#define RCW1_FCS (1 << 29) +#define RCW1_RX (1 << 28) +#define RCW1_VLAN (1 << 27) +#define RCW1_HD (1 << 26) +#define RCW1_LT_DIS (1 << 25) +#define RCW1_PADDR_POS 0 +#define RCW1_PADDR_MASK (0xFFFF << RCW_PADDR_POS) + +/* Transmit Configuration Registers (TC), [1] p52 */ +#define TC_RST (1 << 31) +#define TC_JUM (1 << 30) +#define TC_FCS (1 << 29) +#define TC_TX (1 << 28) +#define TC_VLAN (1 << 27) +#define TC_HD (1 << 26) +#define TC_IFG (1 << 25) + +/* Flow Control Configuration Registers (FCC), [1] p54 */ +#define FCC_FCTX (1 << 30) +#define FCC_FCRX (1 << 29) + +/* Ethernet MAC Mode Configuration Registers (EMMC), [1] p54 */ +#define EMMC_LSPD_POS 30 +#define EMMC_LSPD_MASK (3 << EMMC_LSPD_POS) +#define EMMC_LSPD_1000 (2 << EMMC_LSPD_POS) +#define EMMC_LSPD_100 (1 << EMMC_LSPD_POS) +#define EMMC_LSPD_10 0 +#define EMMC_RGMII (1 << 29) +#define EMMC_SGMII (1 << 28) +#define EMMC_GPCS (1 << 27) +#define EMMC_HOST (1 << 26) +#define EMMC_TX16 (1 << 25) +#define EMMC_RX16 (1 << 24) + +/* RGMII/SGMII Configuration Registers (PHYC), [1] p56 */ +#define PHYC_SLSPD_POS 30 +#define PHYC_SLSPD_MASK (3 << EMMC_SLSPD_POS) +#define PHYC_SLSPD_1000 (2 << EMMC_SLSPD_POS) +#define PHYC_SLSPD_100 (1 << EMMC_SLSPD_POS) +#define PHYC_SLSPD_10 0 +#define PHYC_RLSPD_POS 2 +#define PHYC_RLSPD_MASK (3 << EMMC_RLSPD_POS) +#define PHYC_RLSPD_1000 (2 << EMMC_RLSPD_POS) +#define PHYC_RLSPD_100 (1 << EMMC_RLSPD_POS) +#define PHYC_RLSPD_10 0 +#define PHYC_RGMII_HD (1 << 1) +#define PHYC_RGMII_LINK (1 << 0) + +/* Management Configuration Registers (MC), [1] p57 */ +#define MC_MDIOEN (1 << 6) +#define MC_CLKDIV_POS 0 +#define MC_CLKDIV_MASK (0x3F << MC_CLKDIV_POS) + +/* + * fHOSTCLK fMDC = fHOSTCLK + * fMDC = ------------------- ---------> MC_CLKDIV = -------- - 1 + * (1 + MC_CLKDIV) * 2 2.5 MHz 5MHz + */ +#define MC_CLKDIV(f, m) ((f / (2 * m)) - 1) +#define MC_CLKDIV_25(f) MC_CLKDIV(f, 2500000) +#define MC_CLKDIV_20(f) MC_CLKDIV(f, 2000000) +#define MC_CLKDIV_15(f) MC_CLKDIV(f, 1500000) +#define MC_CLKDIV_10(f) MC_CLKDIV(f, 1000000) + +/* Unicast Address Word 0, 1 Registers (UAW0, UAW1), [1] p58-59 */ +#define UAW0_UADDR_POS 0 +#define UAW0_UADDR_MASK (~0UL << UAW0_UADDR_POS) +#define UAW1_UADDR_POS 0 +#define UAW1_UADDR_MASK (0xFFFF << UAW1_UADDR_POS) + +/* Multicast Address Word 0, 1 Registers (MAW0, MAW1), [1] p60 */ +#define MAW0_MADDR_POS 0 +#define MAW0_MADDR_MASK (~0UL << MAW0_MADDR_POS) +#define MAW1_RNW (1 << 23) +#define MAW1_MAIDX_POS 16 +#define MAW1_MAIDX_MASK (3 << MAW1_MAIDX_POS) +#define MAW1_MADDR_POS 0 +#define MAW1_MADDR_MASK (0xFFFF << MAW1_MADDR_POS) + +/* Address Filter Mode Registers (AFM), [1] p63 */ +#define AFM_PM (1 << 31) + +/* Interrupt Status, Enable Registers (TIS, TIE), [1] p63-65 */ +#define TISE_CFG_W (1 << 6) +#define TISE_CFG_R (1 << 5) +#define TISE_AF_W (1 << 4) +#define TISE_AF_R (1 << 3) +#define TISE_MIIM_W (1 << 2) +#define TISE_MIIM_R (1 << 1) +#define TISE_FABR_R (1 << 0) + +/* MII Management Write Data Registers (MIIMWD), [1] p66 */ +#define MIIMWD_DATA_POS 0 +#define MIIMWD_DATA_MASK (0xFFFF << MIIMWD_DATA_POS) + +/* Ethernet interface ready status */ +int ll_temac_check_status(struct temac_reg *regs, u32 mask); + +/* Indirect write to ll_temac. */ +int ll_temac_indirect_set(struct temac_reg *regs, u16 regn, u32 reg_data); + +/* Indirect read from ll_temac. */ +int ll_temac_indirect_get(struct temac_reg *regs, u16 regn, u32* reg_data); + +struct ll_temac { + phys_addr_t ctrladdr; + phys_addr_t sdma_reg_addr[SDMA_CTRL_REGNUMS]; + + unsigned (*in32)(phys_addr_t); + void (*out32)(phys_addr_t, unsigned); + + int (*ctrlinit) (struct eth_device *); + int (*ctrlhalt) (struct eth_device *); + int (*ctrlreset) (struct eth_device *); + + int phyaddr; + struct phy_device *phydev; + struct mii_dev *bus; + char mdio_busname[MDIO_NAME_LEN]; +}; + +#endif /* _XILINX_LL_TEMAC_ */ diff --git a/drivers/net/xilinx_ll_temac_fifo.c b/drivers/net/xilinx_ll_temac_fifo.c new file mode 100644 index 0000000..3ff0f34 --- /dev/null +++ b/drivers/net/xilinx_ll_temac_fifo.c @@ -0,0 +1,143 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * FIFO sub-controller + * + * Copyright (C) 2011 - 2012 Stephan Linz <linz@li-pro.net> + * Copyright (C) 2008 - 2011 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * CREDITS: tsec driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * [0]: http://www.xilinx.com/support/documentation + * + * [F]: [0]/ip_documentation/xps_ll_fifo.pdf + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ + +#include <config.h> +#include <common.h> +#include <net.h> + +#include <asm/types.h> +#include <asm/io.h> + +#include "xilinx_ll_temac.h" +#include "xilinx_ll_temac_fifo.h" + +int ll_temac_reset_fifo(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + struct fifo_ctrl *fifo_ctrl = (void *)ll_temac->ctrladdr; + + out_be32(&fifo_ctrl->tdfr, LL_FIFO_TDFR_KEY); + out_be32(&fifo_ctrl->rdfr, LL_FIFO_RDFR_KEY); + out_be32(&fifo_ctrl->isr, ~0UL); + out_be32(&fifo_ctrl->ier, 0); + + return 0; +} + +int ll_temac_recv_fifo(struct eth_device *dev) +{ + int i, length = 0; + u32 *buf = (u32 *)NetRxPackets[0]; + struct ll_temac *ll_temac = dev->priv; + struct fifo_ctrl *fifo_ctrl = (void *)ll_temac->ctrladdr; + + if (in_be32(&fifo_ctrl->isr) & LL_FIFO_ISR_RC) { + + /* reset isr */ + out_be32(&fifo_ctrl->isr, ~0UL); + + /* + * MAYBE here: + * while (fifo_ctrl->isr); + */ + + /* + * The length is written (into RLR) by the XPS LL FIFO + * when the packet is received across the RX LocalLink + * interface and the receive data FIFO had enough + * locations that all of the packet data has been saved. + * The RLR should only be read when a receive packet is + * available for processing (the receive occupancy is + * not zero). Once the RLR is read, the receive packet + * data should be read from the receive data FIFO before + * the RLR is read again. + * + * [F] page 17, Receive Length Register (RLR) + */ + if (in_be32(&fifo_ctrl->rdfo) & LL_FIFO_RDFO_MASK) { + length = in_be32(&fifo_ctrl->rlf) & LL_FIFO_RLF_MASK; + } else { + printf("%s: Got error, no receive occupancy\n", + __func__); + return -1; + } + + if (length > PKTSIZE_ALIGN) { + printf("%s: Got error, receive package too big (%i)\n", + __func__, length); + ll_temac_reset_fifo(dev); + return -1; + } + + for (i = 0; i < length; i += 4) + *buf++ = in_be32(&fifo_ctrl->rdfd); + + NetReceive(NetRxPackets[0], length); + } + + return 0; +} + +int ll_temac_send_fifo(struct eth_device *dev, volatile void *packet, + int length) +{ + int i; + u32 *buf = (u32 *)packet; + struct ll_temac *ll_temac = dev->priv; + struct fifo_ctrl *fifo_ctrl = (void *)ll_temac->ctrladdr; + + if (length < LL_FIFO_TLF_MIN) { + printf("%s: Got error, transmit package too small (%i)\n", + __func__, length); + return -1; + } + + if (length > LL_FIFO_TLF_MAX) { + printf("%s: Got error, transmit package too big (%i)\n", + __func__, length); + return -1; + } + + for (i = 0; i < length; i += 4) + out_be32(&fifo_ctrl->tdfd, *buf++); + + /* + * Once the packet length is written to the TLR it is + * automatically moved to the transmit data FIFO with + * the packet data freeing up the TLR for another value. + * The packet length must be written to the TLR after + * the packet data is written to the transmit data FIFO. + * It is not valid to write data for multiple packets + * to the transmit data FIFO before writing the packet + * length values. + * + * [F] page 17, Transmit Length Register (TLR) + */ + out_be32(&fifo_ctrl->tlf, length); + + return 0; +} diff --git a/drivers/net/xilinx_ll_temac_fifo.h b/drivers/net/xilinx_ll_temac_fifo.h new file mode 100644 index 0000000..f0d6e68 --- /dev/null +++ b/drivers/net/xilinx_ll_temac_fifo.h @@ -0,0 +1,122 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * FIFO sub-controller interface + * + * Copyright (C) 2011 - 2012 Stephan Linz <linz@li-pro.net> + * Copyright (C) 2008 - 2011 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ +#ifndef _XILINX_LL_TEMAC_FIFO_ +#define _XILINX_LL_TEMAC_FIFO_ + +#include <net.h> + +#include <asm/types.h> +#include <asm/byteorder.h> + +#if !defined(__BIG_ENDIAN) +# error LL_TEMAC requires big endianess +#endif + +/* + * FIFO Register Definition + * + * Used for memory mapped access from and to (Rd/Td) the LocalLink (LL) + * Tri-Mode Ether MAC (TEMAC) via the 2 kb full duplex FIFO Controller, + * one for each. + * + * [1]: [0]/ip_documentation/xps_ll_fifo.pdf + * page 10, Registers Definition + */ +struct fifo_ctrl { + u32 isr; /* Interrupt Status Register (RW) */ + u32 ier; /* Interrupt Enable Register (RW) */ + u32 tdfr; /* Transmit Data FIFO Reset (WO) */ + u32 tdfv; /* Transmit Data FIFO Vacancy (RO) */ + u32 tdfd; /* Transmit Data FIFO 32bit wide Data write port (WO) */ + u32 tlf; /* Transmit Length FIFO (WO) */ + u32 rdfr; /* Receive Data FIFO Reset (WO) */ + u32 rdfo; /* Receive Data FIFO Occupancy (RO) */ + u32 rdfd; /* Receive Data FIFO 32bit wide Data read port (RO) */ + u32 rlf; /* Receive Length FIFO (RO) */ + u32 llr; /* LocalLink Reset (WO) */ +}; + +/* Interrupt Status Register (ISR), [1] p11 */ +#define LL_FIFO_ISR_RPURE (1 << 31) /* Receive Packet Underrun Read Err */ +#define LL_FIFO_ISR_RPORE (1 << 30) /* Receive Packet Overrun Read Err */ +#define LL_FIFO_ISR_RPUE (1 << 29) /* Receive Packet Underrun Error */ +#define LL_FIFO_ISR_TPOE (1 << 28) /* Transmit Packet Overrun Error */ +#define LL_FIFO_ISR_TC (1 << 27) /* Transmit Complete */ +#define LL_FIFO_ISR_RC (1 << 26) /* Receive Complete */ +#define LL_FIFO_ISR_TSE (1 << 25) /* Transmit Size Error */ +#define LL_FIFO_ISR_TRC (1 << 24) /* Transmit Reset Complete */ +#define LL_FIFO_ISR_RRC (1 << 23) /* Receive Reset Complete */ + +/* Interrupt Enable Register (IER), [1] p12/p13 */ +#define LL_FIFO_IER_RPURE (1 << 31) /* Receive Packet Underrun Read Err */ +#define LL_FIFO_IER_RPORE (1 << 30) /* Receive Packet Overrun Read Err */ +#define LL_FIFO_IER_RPUE (1 << 29) /* Receive Packet Underrun Error */ +#define LL_FIFO_IER_TPOE (1 << 28) /* Transmit Packet Overrun Error */ +#define LL_FIFO_IER_TC (1 << 27) /* Transmit Complete */ +#define LL_FIFO_IER_RC (1 << 26) /* Receive Complete */ +#define LL_FIFO_IER_TSE (1 << 25) /* Transmit Size Error */ +#define LL_FIFO_IER_TRC (1 << 24) /* Transmit Reset Complete */ +#define LL_FIFO_IER_RRC (1 << 23) /* Receive Reset Complete */ + +/* Transmit Data FIFO Reset (TDFR), [1] p13/p14 */ +#define LL_FIFO_TDFR_KEY 0x000000A5UL + +/* Transmit Data FIFO Vacancy (TDFV), [1] p14 */ +#define LL_FIFO_TDFV_POS 0 +#define LL_FIFO_TDFV_MASK (0x000001FFUL << LL_FIFO_TDFV_POS) + +/* Transmit Length FIFO (TLF), [1] p16/p17 */ +#define LL_FIFO_TLF_POS 0 +#define LL_FIFO_TLF_MASK (0x000007FFUL << LL_FIFO_TLF_POS) +#define LL_FIFO_TLF_MIN ((4 * sizeof(u32)) & LL_FIFO_TLF_MASK) +#define LL_FIFO_TLF_MAX ((510 * sizeof(u32)) & LL_FIFO_TLF_MASK) + +/* Receive Data FIFO Reset (RDFR), [1] p15 */ +#define LL_FIFO_RDFR_KEY 0x000000A5UL + +/* Receive Data FIFO Occupancy (RDFO), [1] p16 */ +#define LL_FIFO_RDFO_POS 0 +#define LL_FIFO_RDFO_MASK (0x000001FFUL << LL_FIFO_RDFO_POS) + +/* Receive Length FIFO (RLF), [1] p17/p18 */ +#define LL_FIFO_RLF_POS 0 +#define LL_FIFO_RLF_MASK (0x000007FFUL << LL_FIFO_RLF_POS) +#define LL_FIFO_RLF_MIN ((4 * sizeof(uint32)) & LL_FIFO_RLF_MASK) +#define LL_FIFO_RLF_MAX ((510 * sizeof(uint32)) & LL_FIFO_RLF_MASK) + +/* LocalLink Reset (LLR), [1] p18 */ +#define LL_FIFO_LLR_KEY 0x000000A5UL + + +/* reset FIFO and IRQ, disable interrupts */ +int ll_temac_reset_fifo(struct eth_device *dev); + +/* receive buffered data from FIFO (polling ISR) */ +int ll_temac_recv_fifo(struct eth_device *dev); + +/* send buffered data to FIFO */ +int ll_temac_send_fifo(struct eth_device *dev, volatile void *packet, + int length); + +#endif /* _XILINX_LL_TEMAC_FIFO_ */ diff --git a/drivers/net/xilinx_ll_temac_mdio.c b/drivers/net/xilinx_ll_temac_mdio.c new file mode 100644 index 0000000..c56ff48 --- /dev/null +++ b/drivers/net/xilinx_ll_temac_mdio.c @@ -0,0 +1,180 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * MDIO bus access + * + * Copyright (C) 2011 - 2012 Stephan Linz <linz@li-pro.net> + * Copyright (C) 2008 - 2011 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * CREDITS: tsec driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ + +#include <config.h> +#include <common.h> +#include <miiphy.h> +#include <phy.h> +#include <malloc.h> +#include <asm/io.h> + +#include "xilinx_ll_temac.h" +#include "xilinx_ll_temac_mdio.h" + +#if !defined(CONFIG_MII) +# error "LL_TEMAC requires MII -- missing CONFIG_MII" +#endif + +#if !defined(CONFIG_PHYLIB) +# error "LL_TEMAC requires PHYLIB -- missing CONFIG_PHYLIB" +#endif + +/* + * Prior to PHY access, the MDIO clock must be setup. This driver will set a + * safe default that should work with PLB bus speeds of up to 150 MHz and keep + * the MDIO clock below 2.5 MHz. If the user wishes faster access to the PHY + * then the clock divisor can be set to a different value by setting the + * correct bus speed value with CONFIG_XILINX_LL_TEMAC_CLK. + */ +#if !defined(CONFIG_XILINX_LL_TEMAC_CLK) +#define MDIO_CLOCK_DIV MC_CLKDIV_10(150000000) +#else +#define MDIO_CLOCK_DIV MC_CLKDIV_25(CONFIG_XILINX_LL_TEMAC_CLK) +#endif + +static int ll_temac_mdio_setup(struct mii_dev *bus) +{ + struct temac_reg *regs = (struct temac_reg *)bus->priv; + + /* setup MDIO clock */ + ll_temac_indirect_set(regs, TEMAC_MC, + MC_MDIOEN | (MDIO_CLOCK_DIV & MC_CLKDIV_MASK)); + + return 0; +} + +/* + * Indirect MII PHY read via ll_temac. + * + * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf + * page 67, Using the MII Management to Access PHY Registers + */ +int ll_temac_local_mdio_read(struct temac_reg *regs, int addr, int devad, + int regnum) +{ + out_be32(®s->lsw, + ((addr << LSW_PHYAD_POS) & LSW_PHYAD_MASK) | + (regnum & LSW_REGAD_MASK)); + out_be32(®s->ctl, TEMAC_MIIMAI); + + ll_temac_check_status(regs, RSE_MIIM_RR); + + return in_be32(®s->lsw) & LSW_REGDAT_MASK; +} + +/* + * Indirect MII PHY write via ll_temac. + * + * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf + * page 67, Using the MII Management to Access PHY Registers + */ +void ll_temac_local_mdio_write(struct temac_reg *regs, int addr, int devad, + int regnum, u16 value) +{ + out_be32(®s->lsw, (value & LSW_REGDAT_MASK)); + out_be32(®s->ctl, CTL_WEN | TEMAC_MIIMWD); + + out_be32(®s->lsw, + ((addr << LSW_PHYAD_POS) & LSW_PHYAD_MASK) | + (regnum & LSW_REGAD_MASK)); + out_be32(®s->ctl, CTL_WEN | TEMAC_MIIMAI); + + ll_temac_check_status(regs, RSE_MIIM_WR); +} + +int ll_temac_phy_read(struct mii_dev *bus, int addr, int devad, int regnum) +{ + struct temac_reg *regs = (struct temac_reg *)bus->priv; + + return ll_temac_local_mdio_read(regs, addr, devad, regnum); +} + +int ll_temac_phy_write(struct mii_dev *bus, int addr, int devad, int regnum, + u16 value) +{ + struct temac_reg *regs = (struct temac_reg *)bus->priv; + + ll_temac_local_mdio_write(regs, addr, devad, regnum, value); + + return 0; +} + +/* + * Use MII register 1 (MII status register) to detect PHY + * + * A Mask used to verify certain PHY features (register content) + * in the PHY detection register: + * Auto-negotiation support, 10Mbps half/full duplex support + */ +#define PHY_DETECT_REG MII_BMSR +#define PHY_DETECT_MASK (BMSR_10FULL | BMSR_10HALF | BMSR_ANEGCAPABLE) + +/* Looking for a valid PHY address */ +int ll_temac_phy_addr(struct mii_dev *bus) +{ + struct temac_reg *regs = (struct temac_reg *)bus->priv; + unsigned short val; + unsigned int phy; + + for (phy = PHY_MAX_ADDR; phy >= 0; phy--) { + val = ll_temac_local_mdio_read(regs, phy, 0, PHY_DETECT_REG); + if ((val != 0xFFFF) && + ((val & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { + /* Found a valid PHY address */ + return phy; + } + } + + return -1; +} + +int xilinx_ll_temac_mdio_initialize(bd_t *bis, struct ll_temac_mdio_info *info) +{ + struct mii_dev *bus = mdio_alloc(); + + if (!bus) { + printf("Failed to allocate LL_TEMAC MDIO bus: %s\n", + info->name); + return -1; + } + + bus->read = ll_temac_phy_read; + bus->write = ll_temac_phy_write; + bus->reset = NULL; + + /* use given name or generate its own unique name */ + if (info->name) { + strncpy(bus->name, info->name, MDIO_NAME_LEN); + } else { + snprintf(bus->name, MDIO_NAME_LEN, "lltemii.%p", info->regs); + info->name = bus->name; + } + + bus->priv = info->regs; + + ll_temac_mdio_setup(bus); + return mdio_register(bus); +} diff --git a/drivers/net/xilinx_ll_temac_mdio.h b/drivers/net/xilinx_ll_temac_mdio.h new file mode 100644 index 0000000..8d8fabd --- /dev/null +++ b/drivers/net/xilinx_ll_temac_mdio.h @@ -0,0 +1,53 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * MDIO bus access interface + * + * Copyright (C) 2011 - 2012 Stephan Linz <linz@li-pro.net> + * Copyright (C) 2008 - 2011 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ +#ifndef _XILINX_LL_TEMAC_MDIO_ +#define _XILINX_LL_TEMAC_MDIO_ + +#include <net.h> +#include <miiphy.h> + +#include <asm/types.h> +#include <asm/byteorder.h> + +#include "xilinx_ll_temac.h" + +int ll_temac_local_mdio_read(struct temac_reg *regs, int addr, int devad, + int regnum); +void ll_temac_local_mdio_write(struct temac_reg *regs, int addr, int devad, + int regnum, u16 value); + +int ll_temac_phy_read(struct mii_dev *bus, int addr, int devad, int regnum); +int ll_temac_phy_write(struct mii_dev *bus, int addr, int devad, int regnum, + u16 value); + +int ll_temac_phy_addr(struct mii_dev *bus); + +struct ll_temac_mdio_info { + struct temac_reg *regs; + char *name; +}; + +int xilinx_ll_temac_mdio_initialize(bd_t *bis, struct ll_temac_mdio_info *info); + +#endif /* _XILINX_LL_TEMAC_MDIO_ */ diff --git a/drivers/net/xilinx_ll_temac_sdma.c b/drivers/net/xilinx_ll_temac_sdma.c new file mode 100644 index 0000000..621d100 --- /dev/null +++ b/drivers/net/xilinx_ll_temac_sdma.c @@ -0,0 +1,370 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * SDMA sub-controller + * + * Copyright (C) 2011 - 2012 Stephan Linz <linz@li-pro.net> + * Copyright (C) 2008 - 2011 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * CREDITS: tsec driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * [0]: http://www.xilinx.com/support/documentation + * + * [M]: [0]/ip_documentation/mpmc.pdf + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ + +#include <config.h> +#include <common.h> +#include <net.h> + +#include <asm/types.h> +#include <asm/io.h> + +#include "xilinx_ll_temac.h" +#include "xilinx_ll_temac_sdma.h" + +#define TX_BUF_CNT 2 + +static unsigned int rx_idx; /* index of the current RX buffer */ +static unsigned int tx_idx; /* index of the current TX buffer */ + +struct rtx_cdmac_bd { + struct cdmac_bd rx[PKTBUFSRX]; + struct cdmac_bd tx[TX_BUF_CNT]; +}; + +/* + * DMA Buffer Descriptor alignment + * + * If the address contained in the Next Descriptor Pointer register is not + * 8-word aligned or reaches beyond the range of available memory, the SDMA + * halts processing and sets the CDMAC_BD_STCTRL_ERROR bit in the respective + * status register (tx_chnl_sts or rx_chnl_sts). + * + * [1]: [0]/ip_documentation/mpmc.pdf + * page 161, Next Descriptor Pointer + */ +static struct rtx_cdmac_bd cdmac_bd __aligned(32); + +#if defined(CONFIG_XILINX_440) || defined(CONFIG_XILINX_405) + +/* + * Indirect DCR access operations mi{ft}dcr_xilinx() espacialy + * for Xilinx PowerPC implementations on FPGA. + * + * FIXME: This part should go up to arch/powerpc -- but where? + */ +#include <asm/processor.h> +#define XILINX_INDIRECT_DCR_ADDRESS_REG 0 +#define XILINX_INDIRECT_DCR_ACCESS_REG 1 +inline unsigned mifdcr_xilinx(const unsigned dcrn) +{ + mtdcr(XILINX_INDIRECT_DCR_ADDRESS_REG, dcrn); + return mfdcr(XILINX_INDIRECT_DCR_ACCESS_REG); +} +inline void mitdcr_xilinx(const unsigned dcrn, int val) +{ + mtdcr(XILINX_INDIRECT_DCR_ADDRESS_REG, dcrn); + mtdcr(XILINX_INDIRECT_DCR_ACCESS_REG, val); +} + +/* Xilinx Device Control Register (DCR) in/out accessors */ +inline unsigned ll_temac_xldcr_in32(phys_addr_t addr) +{ + return mifdcr_xilinx((const unsigned)addr); +} +inline void ll_temac_xldcr_out32(phys_addr_t addr, unsigned value) +{ + mitdcr_xilinx((const unsigned)addr, value); +} + +void ll_temac_collect_xldcr_sdma_reg_addr(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + phys_addr_t dmac_ctrl = ll_temac->ctrladdr; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + ra[TX_NXTDESC_PTR] = dmac_ctrl + TX_NXTDESC_PTR; + ra[TX_CURBUF_ADDR] = dmac_ctrl + TX_CURBUF_ADDR; + ra[TX_CURBUF_LENGTH] = dmac_ctrl + TX_CURBUF_LENGTH; + ra[TX_CURDESC_PTR] = dmac_ctrl + TX_CURDESC_PTR; + ra[TX_TAILDESC_PTR] = dmac_ctrl + TX_TAILDESC_PTR; + ra[TX_CHNL_CTRL] = dmac_ctrl + TX_CHNL_CTRL; + ra[TX_IRQ_REG] = dmac_ctrl + TX_IRQ_REG; + ra[TX_CHNL_STS] = dmac_ctrl + TX_CHNL_STS; + ra[RX_NXTDESC_PTR] = dmac_ctrl + RX_NXTDESC_PTR; + ra[RX_CURBUF_ADDR] = dmac_ctrl + RX_CURBUF_ADDR; + ra[RX_CURBUF_LENGTH] = dmac_ctrl + RX_CURBUF_LENGTH; + ra[RX_CURDESC_PTR] = dmac_ctrl + RX_CURDESC_PTR; + ra[RX_TAILDESC_PTR] = dmac_ctrl + RX_TAILDESC_PTR; + ra[RX_CHNL_CTRL] = dmac_ctrl + RX_CHNL_CTRL; + ra[RX_IRQ_REG] = dmac_ctrl + RX_IRQ_REG; + ra[RX_CHNL_STS] = dmac_ctrl + RX_CHNL_STS; + ra[DMA_CONTROL_REG] = dmac_ctrl + DMA_CONTROL_REG; +} + +#endif /* CONFIG_XILINX_440 || ONFIG_XILINX_405 */ + +/* Xilinx Processor Local Bus (PLB) in/out accessors */ +inline unsigned ll_temac_xlplb_in32(phys_addr_t addr) +{ + return in_be32((void *)addr); +} +inline void ll_temac_xlplb_out32(phys_addr_t addr, unsigned value) +{ + out_be32((void *)addr, value); +} + +/* collect all register addresses for Xilinx PLB in/out accessors */ +void ll_temac_collect_xlplb_sdma_reg_addr(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + struct sdma_ctrl *sdma_ctrl = (void *)ll_temac->ctrladdr; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + ra[TX_NXTDESC_PTR] = (phys_addr_t)&sdma_ctrl->tx_nxtdesc_ptr; + ra[TX_CURBUF_ADDR] = (phys_addr_t)&sdma_ctrl->tx_curbuf_addr; + ra[TX_CURBUF_LENGTH] = (phys_addr_t)&sdma_ctrl->tx_curbuf_length; + ra[TX_CURDESC_PTR] = (phys_addr_t)&sdma_ctrl->tx_curdesc_ptr; + ra[TX_TAILDESC_PTR] = (phys_addr_t)&sdma_ctrl->tx_taildesc_ptr; + ra[TX_CHNL_CTRL] = (phys_addr_t)&sdma_ctrl->tx_chnl_ctrl; + ra[TX_IRQ_REG] = (phys_addr_t)&sdma_ctrl->tx_irq_reg; + ra[TX_CHNL_STS] = (phys_addr_t)&sdma_ctrl->tx_chnl_sts; + ra[RX_NXTDESC_PTR] = (phys_addr_t)&sdma_ctrl->rx_nxtdesc_ptr; + ra[RX_CURBUF_ADDR] = (phys_addr_t)&sdma_ctrl->rx_curbuf_addr; + ra[RX_CURBUF_LENGTH] = (phys_addr_t)&sdma_ctrl->rx_curbuf_length; + ra[RX_CURDESC_PTR] = (phys_addr_t)&sdma_ctrl->rx_curdesc_ptr; + ra[RX_TAILDESC_PTR] = (phys_addr_t)&sdma_ctrl->rx_taildesc_ptr; + ra[RX_CHNL_CTRL] = (phys_addr_t)&sdma_ctrl->rx_chnl_ctrl; + ra[RX_IRQ_REG] = (phys_addr_t)&sdma_ctrl->rx_irq_reg; + ra[RX_CHNL_STS] = (phys_addr_t)&sdma_ctrl->rx_chnl_sts; + ra[DMA_CONTROL_REG] = (phys_addr_t)&sdma_ctrl->dma_control_reg; +} + +/* Check for TX and RX channel errors. */ +static inline int ll_temac_sdma_error(struct eth_device *dev) +{ + int err; + struct ll_temac *ll_temac = dev->priv; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + err = ll_temac->in32(ra[TX_CHNL_STS]) & CHNL_STS_ERROR; + err |= ll_temac->in32(ra[RX_CHNL_STS]) & CHNL_STS_ERROR; + + return err; +} + +int ll_temac_init_sdma(struct eth_device *dev) +{ + struct ll_temac *ll_temac = dev->priv; + struct cdmac_bd *rx_dp; + struct cdmac_bd *tx_dp; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + int i; + + printf("%s: SDMA: %d Rx buffers, %d Tx buffers\n", + dev->name, PKTBUFSRX, TX_BUF_CNT); + + /* Initialize the Rx Buffer descriptors */ + for (i = 0; i < PKTBUFSRX; i++) { + rx_dp = &cdmac_bd.rx[i]; + memset(rx_dp, 0, sizeof(*rx_dp)); + rx_dp->next_p = rx_dp; + rx_dp->buf_len = PKTSIZE_ALIGN; + rx_dp->phys_buf_p = (u8 *)NetRxPackets[i]; + flush_cache((u32)rx_dp->phys_buf_p, PKTSIZE_ALIGN); + } + flush_cache((u32)cdmac_bd.rx, sizeof(cdmac_bd.rx)); + + /* Initialize the TX Buffer Descriptors */ + for (i = 0; i < TX_BUF_CNT; i++) { + tx_dp = &cdmac_bd.tx[i]; + memset(tx_dp, 0, sizeof(*tx_dp)); + tx_dp->next_p = tx_dp; + } + flush_cache((u32)cdmac_bd.tx, sizeof(cdmac_bd.tx)); + + /* Reset index counter to the Rx and Tx Buffer descriptors */ + rx_idx = tx_idx = 0; + + /* initial Rx DMA start by writing to respective TAILDESC_PTR */ + ll_temac->out32(ra[RX_CURDESC_PTR], (int)&cdmac_bd.rx[rx_idx]); + ll_temac->out32(ra[RX_TAILDESC_PTR], (int)&cdmac_bd.rx[rx_idx]); + + return 0; +} + +int ll_temac_halt_sdma(struct eth_device *dev) +{ + unsigned timeout = 50; /* 1usec * 50 = 50usec */ + struct ll_temac *ll_temac = dev->priv; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + /* + * Soft reset the DMA + * + * Quote from MPMC documentation: Writing a 1 to this field + * forces the DMA engine to shutdown and reset itself. After + * setting this bit, software must poll it until the bit is + * cleared by the DMA. This indicates that the reset process + * is done and the pipeline has been flushed. + */ + ll_temac->out32(ra[DMA_CONTROL_REG], DMA_CONTROL_RESET); + while (timeout && (ll_temac->in32(ra[DMA_CONTROL_REG]) + & DMA_CONTROL_RESET)) { + timeout--; + udelay(1); + } + + if (!timeout) { + printf("%s: Timeout\n", __func__); + return -1; + } + + return 0; +} + +int ll_temac_reset_sdma(struct eth_device *dev) +{ + u32 r; + struct ll_temac *ll_temac = dev->priv; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + /* Soft reset the DMA. */ + if (ll_temac_halt_sdma(dev)) + return -1; + + /* Now clear the interrupts. */ + r = ll_temac->in32(ra[TX_CHNL_CTRL]); + r &= ~CHNL_CTRL_IRQ_MASK; + ll_temac->out32(ra[TX_CHNL_CTRL], r); + + r = ll_temac->in32(ra[RX_CHNL_CTRL]); + r &= ~CHNL_CTRL_IRQ_MASK; + ll_temac->out32(ra[RX_CHNL_CTRL], r); + + /* Now ACK pending IRQs. */ + ll_temac->out32(ra[TX_IRQ_REG], IRQ_REG_IRQ_MASK); + ll_temac->out32(ra[RX_IRQ_REG], IRQ_REG_IRQ_MASK); + + /* Set tail-ptr mode, disable errors for both channels. */ + ll_temac->out32(ra[DMA_CONTROL_REG], + /* Enable use of tail pointer register */ + DMA_CONTROL_TPE | + /* Disable error when 2 or 4 bit coalesce cnt overfl */ + DMA_CONTROL_RXOCEID | + /* Disable error when 2 or 4 bit coalesce cnt overfl */ + DMA_CONTROL_TXOCEID); + + return 0; +} + +int ll_temac_recv_sdma(struct eth_device *dev) +{ + int length, pb_idx; + struct cdmac_bd *rx_dp = &cdmac_bd.rx[rx_idx]; + struct ll_temac *ll_temac = dev->priv; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + if (ll_temac_sdma_error(dev)) { + + if (ll_temac_reset_sdma(dev)) + return -1; + + ll_temac_init_sdma(dev); + } + + flush_cache((u32)rx_dp, sizeof(*rx_dp)); + + if (!(rx_dp->sca.stctrl & CDMAC_BD_STCTRL_COMPLETED)) + return 0; + + if (rx_dp->sca.stctrl & (CDMAC_BD_STCTRL_SOP | CDMAC_BD_STCTRL_EOP)) { + pb_idx = rx_idx; + length = rx_dp->sca.app[4] & CDMAC_BD_APP4_RXBYTECNT_MASK; + } else { + pb_idx = -1; + length = 0; + printf("%s: Got part of package, unsupported (%x)\n", + __func__, rx_dp->sca.stctrl); + } + + /* flip the buffer */ + flush_cache((u32)rx_dp->phys_buf_p, length); + + /* reset the current descriptor */ + rx_dp->sca.stctrl = 0; + rx_dp->sca.app[4] = 0; + flush_cache((u32)rx_dp, sizeof(*rx_dp)); + + /* Find next empty buffer descriptor, preparation for next iteration */ + rx_idx = (rx_idx + 1) % PKTBUFSRX; + rx_dp = &cdmac_bd.rx[rx_idx]; + flush_cache((u32)rx_dp, sizeof(*rx_dp)); + + /* DMA start by writing to respective TAILDESC_PTR */ + ll_temac->out32(ra[RX_CURDESC_PTR], (int)&cdmac_bd.rx[rx_idx]); + ll_temac->out32(ra[RX_TAILDESC_PTR], (int)&cdmac_bd.rx[rx_idx]); + + if (length > 0 && pb_idx != -1) + NetReceive(NetRxPackets[pb_idx], length); + + return 0; +} + +int ll_temac_send_sdma(struct eth_device *dev, volatile void *packet, + int length) +{ + unsigned timeout = 50; /* 1usec * 50 = 50usec */ + struct cdmac_bd *tx_dp = &cdmac_bd.tx[tx_idx]; + struct ll_temac *ll_temac = dev->priv; + phys_addr_t *ra = ll_temac->sdma_reg_addr; + + if (ll_temac_sdma_error(dev)) { + + if (ll_temac_reset_sdma(dev)) + return -1; + + ll_temac_init_sdma(dev); + } + + tx_dp->phys_buf_p = (u8 *)packet; + tx_dp->buf_len = length; + tx_dp->sca.stctrl = CDMAC_BD_STCTRL_SOP | CDMAC_BD_STCTRL_EOP | + CDMAC_BD_STCTRL_STOP_ON_END; + + flush_cache((u32)packet, length); + flush_cache((u32)tx_dp, sizeof(*tx_dp)); + + /* DMA start by writing to respective TAILDESC_PTR */ + ll_temac->out32(ra[TX_CURDESC_PTR], (int)tx_dp); + ll_temac->out32(ra[TX_TAILDESC_PTR], (int)tx_dp); + + /* Find next empty buffer descriptor, preparation for next iteration */ + tx_idx = (tx_idx + 1) % TX_BUF_CNT; + tx_dp = &cdmac_bd.tx[tx_idx]; + + do { + flush_cache((u32)tx_dp, sizeof(*tx_dp)); + udelay(1); + } while (timeout-- && !(tx_dp->sca.stctrl & CDMAC_BD_STCTRL_COMPLETED)); + + if (!timeout) { + printf("%s: Timeout\n", __func__); + return -1; + } + + return 0; +} diff --git a/drivers/net/xilinx_ll_temac_sdma.h b/drivers/net/xilinx_ll_temac_sdma.h new file mode 100644 index 0000000..51e258d --- /dev/null +++ b/drivers/net/xilinx_ll_temac_sdma.h @@ -0,0 +1,281 @@ +/* + * Xilinx xps_ll_temac ethernet driver for u-boot + * + * SDMA sub-controller interface + * + * Copyright (C) 2011 - 2012 Stephan Linz <linz@li-pro.net> + * Copyright (C) 2008 - 2011 Michal Simek <monstr@monstr.eu> + * Copyright (C) 2008 - 2011 PetaLogix + * + * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver + * Copyright (C) 2008 Nissin Systems Co.,Ltd. + * March 2008 created + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * [0]: http://www.xilinx.com/support/documentation + * + * [S]: [0]/ip_documentation/xps_ll_temac.pdf + * [A]: [0]/application_notes/xapp1041.pdf + */ +#ifndef _XILINX_LL_TEMAC_SDMA_ +#define _XILINX_LL_TEMAC_SDMA_ + +#include <net.h> + +#include <asm/types.h> +#include <asm/byteorder.h> + +#include <linux/compiler.h> + +#if !defined(__BIG_ENDIAN) +# error LL_TEMAC requires big endianess +#endif + +/* + * DMA Buffer Descriptor for CDMAC + * + * Used for data connection from and to (Rx/Tx) the LocalLink (LL) TEMAC via + * the Communications Direct Memory Access Controller (CDMAC) -- one for each. + * + * overview: + * ftp://ftp.xilinx.com/pub/documentation/misc/mpmc_getting_started.pdf + * + * [1]: [0]/ip_documentation/mpmc.pdf + * page 140, DMA Operation Descriptors + * + * [2]: [0]/user_guides/ug200.pdf + * page 229, DMA Controller -- Descriptor Format + * + * [3]: [0]/ip_documentation/xps_ll_temac.pdf + * page 72, Transmit LocalLink Frame Format + * page 73, Receive LocalLink Frame Format + */ +struct cdmac_bd { + struct cdmac_bd *next_p; /* Next Descriptor Pointer */ + u8 *phys_buf_p; /* Buffer Address */ + u32 buf_len; /* Buffer Length */ + union { + u8 stctrl; /* Status/Control the DMA transfer */ + u32 app[5]; /* application specific data */ + } __packed __aligned(1) sca; +}; + +/* CDMAC Descriptor Status and Control (stctrl), [1] p140, [2] p230 */ +#define CDMAC_BD_STCTRL_ERROR (1 << 7) +#define CDMAC_BD_STCTRL_IRQ_ON_END (1 << 6) +#define CDMAC_BD_STCTRL_STOP_ON_END (1 << 5) +#define CDMAC_BD_STCTRL_COMPLETED (1 << 4) +#define CDMAC_BD_STCTRL_SOP (1 << 3) +#define CDMAC_BD_STCTRL_EOP (1 << 2) +#define CDMAC_BD_STCTRL_DMACHBUSY (1 << 1) + +/* CDMAC Descriptor APP0: Transmit LocalLink Footer Word 3, [3] p72 */ +#define CDMAC_BD_APP0_TXCSCNTRL (1 << 0) + +/* CDMAC Descriptor APP1: Transmit LocalLink Footer Word 4, [3] p73 */ +#define CDMAC_BD_APP1_TXCSBEGIN_POS 16 +#define CDMAC_BD_APP1_TXCSBEGIN_MASK (0xFFFF << CDMAC_BD_APP1_TXCSBEGIN_POS) +#define CDMAC_BD_APP1_TXCSINSERT_POS 0 +#define CDMAC_BD_APP1_TXCSINSERT_MASK (0xFFFF << CDMAC_BD_APP1_TXCSINSERT_POS) + +/* CDMAC Descriptor APP2: Transmit LocalLink Footer Word 5, [3] p73 */ +#define CDMAC_BD_APP2_TXCSINIT_POS 0 +#define CDMAC_BD_APP2_TXCSINIT_MASK (0xFFFF << CDMAC_BD_APP2_TXCSINIT_POS) + +/* CDMAC Descriptor APP0: Receive LocalLink Footer Word 3, [3] p73 */ +#define CDMAC_BD_APP0_MADDRU_POS 0 +#define CDMAC_BD_APP0_MADDRU_MASK (0xFFFF << CDMAC_BD_APP0_MADDRU_POS) + +/* CDMAC Descriptor APP1: Receive LocalLink Footer Word 4, [3] p74 */ +#define CDMAC_BD_APP1_MADDRL_POS 0 +#define CDMAC_BD_APP1_MADDRL_MASK (~0UL << CDMAC_BD_APP1_MADDRL_POS) + +/* CDMAC Descriptor APP2: Receive LocalLink Footer Word 5, [3] p74 */ +#define CDMAC_BD_APP2_BCAST_FRAME (1 << 2) +#define CDMAC_BD_APP2_IPC_MCAST_FRAME (1 << 1) +#define CDMAC_BD_APP2_MAC_MCAST_FRAME (1 << 0) + +/* CDMAC Descriptor APP3: Receive LocalLink Footer Word 6, [3] p74 */ +#define CDMAC_BD_APP3_TLTPID_POS 16 +#define CDMAC_BD_APP3_TLTPID_MASK (0xFFFF << CDMAC_BD_APP3_TLTPID_POS) +#define CDMAC_BD_APP3_RXCSRAW_POS 0 +#define CDMAC_BD_APP3_RXCSRAW_MASK (0xFFFF << CDMAC_BD_APP3_RXCSRAW_POS) + +/* CDMAC Descriptor APP4: Receive LocalLink Footer Word 7, [3] p74 */ +#define CDMAC_BD_APP4_VLANTAG_POS 16 +#define CDMAC_BD_APP4_VLANTAG_MASK (0xFFFF << CDMAC_BD_APP4_VLANTAG_POS) +#define CDMAC_BD_APP4_RXBYTECNT_POS 0 +#define CDMAC_BD_APP4_RXBYTECNT_MASK (0x3FFF << CDMAC_BD_APP4_RXBYTECNT_POS) + +/* + * SDMA Register Definition + * + * [0]: http://www.xilinx.com/support/documentation + * + * [1]: [0]/ip_documentation/mpmc.pdf + * page 54, SDMA Register Summary + * page 160, SDMA Registers + * + * [2]: [0]/user_guides/ug200.pdf + * page 244, DMA Controller -- Programming Interface and Registers + */ +#define SDMA_CTRL_REGTYPE u32 +#define SDMA_CTRL_REGSIZE sizeof(SDMA_CTRL_REGTYPE) +struct sdma_ctrl { + /* Transmit Registers */ + SDMA_CTRL_REGTYPE tx_nxtdesc_ptr; /* TX Next Description Pointer */ + SDMA_CTRL_REGTYPE tx_curbuf_addr; /* TX Current Buffer Address */ + SDMA_CTRL_REGTYPE tx_curbuf_length; /* TX Current Buffer Length */ + SDMA_CTRL_REGTYPE tx_curdesc_ptr; /* TX Current Descriptor Pointer */ + SDMA_CTRL_REGTYPE tx_taildesc_ptr; /* TX Tail Descriptor Pointer */ + SDMA_CTRL_REGTYPE tx_chnl_ctrl; /* TX Channel Control */ + SDMA_CTRL_REGTYPE tx_irq_reg; /* TX Interrupt Register */ + SDMA_CTRL_REGTYPE tx_chnl_sts; /* TX Status Register */ + /* Receive Registers */ + SDMA_CTRL_REGTYPE rx_nxtdesc_ptr; /* RX Next Descriptor Pointer */ + SDMA_CTRL_REGTYPE rx_curbuf_addr; /* RX Current Buffer Address */ + SDMA_CTRL_REGTYPE rx_curbuf_length; /* RX Current Buffer Length */ + SDMA_CTRL_REGTYPE rx_curdesc_ptr; /* RX Current Descriptor Pointer */ + SDMA_CTRL_REGTYPE rx_taildesc_ptr; /* RX Tail Descriptor Pointer */ + SDMA_CTRL_REGTYPE rx_chnl_ctrl; /* RX Channel Control */ + SDMA_CTRL_REGTYPE rx_irq_reg; /* RX Interrupt Register */ + SDMA_CTRL_REGTYPE rx_chnl_sts; /* RX Status Register */ + /* Control Registers */ + SDMA_CTRL_REGTYPE dma_control_reg; /* DMA Control Register */ +}; + +#define SDMA_CTRL_REGNUMS sizeof(struct sdma_ctrl)/SDMA_CTRL_REGSIZE + +/* + * DMAC Register Index Enumeration + * + * [2]: http://www.xilinx.com/support/documentation/user_guides/ug200.pdf + * page 244, DMA Controller -- Programming Interface and Registers + */ +enum dmac_ctrl { + /* Transmit Registers */ + TX_NXTDESC_PTR = 0, /* TX Next Description Pointer */ + TX_CURBUF_ADDR, /* TX Current Buffer Address */ + TX_CURBUF_LENGTH, /* TX Current Buffer Length */ + TX_CURDESC_PTR, /* TX Current Descriptor Pointer */ + TX_TAILDESC_PTR, /* TX Tail Descriptor Pointer */ + TX_CHNL_CTRL, /* TX Channel Control */ + TX_IRQ_REG, /* TX Interrupt Register */ + TX_CHNL_STS, /* TX Status Register */ + /* Receive Registers */ + RX_NXTDESC_PTR, /* RX Next Descriptor Pointer */ + RX_CURBUF_ADDR, /* RX Current Buffer Address */ + RX_CURBUF_LENGTH, /* RX Current Buffer Length */ + RX_CURDESC_PTR, /* RX Current Descriptor Pointer */ + RX_TAILDESC_PTR, /* RX Tail Descriptor Pointer */ + RX_CHNL_CTRL, /* RX Channel Control */ + RX_IRQ_REG, /* RX Interrupt Register */ + RX_CHNL_STS, /* RX Status Register */ + /* Control Registers */ + DMA_CONTROL_REG /* DMA Control Register */ +}; + +/* Rx/Tx Channel Control Register (*_chnl_ctrl), [1] p163, [2] p246/p252 */ +#define CHNL_CTRL_ITO_POS 24 +#define CHNL_CTRL_ITO_MASK (0xFF << CHNL_CTRL_ITO_POS) +#define CHNL_CTRL_IC_POS 16 +#define CHNL_CTRL_IC_MASK (0xFF << CHNL_CTRL_IC_POS) +#define CHNL_CTRL_MSBADDR_POS 12 +#define CHNL_CTRL_MSBADDR_MASK (0xF << CHNL_CTRL_MSBADDR_POS) +#define CHNL_CTRL_AME (1 << 11) +#define CHNL_CTRL_OBWC (1 << 10) +#define CHNL_CTRL_IOE (1 << 9) +#define CHNL_CTRL_LIC (1 << 8) +#define CHNL_CTRL_IE (1 << 7) +#define CHNL_CTRL_IEE (1 << 2) +#define CHNL_CTRL_IDE (1 << 1) +#define CHNL_CTRL_ICE (1 << 0) + +/* All interrupt enable bits */ +#define CHNL_CTRL_IRQ_MASK (CHNL_CTRL_IE | \ + CHNL_CTRL_IEE | \ + CHNL_CTRL_IDE | \ + CHNL_CTRL_ICE) + +/* Rx/Tx Interrupt Status Register (*_irq_reg), [1] p164, [2] p247/p253 */ +#define IRQ_REG_DTV_POS 24 +#define IRQ_REG_DTV_MASK (0xFF << IRQ_REG_DTV_POS) +#define IRQ_REG_CCV_POS 16 +#define IRQ_REG_CCV_MASK (0xFF << IRQ_REG_CCV_POS) +#define IRQ_REG_WRCQ_EMPTY (1 << 14) +#define IRQ_REG_CIC_POS 10 +#define IRQ_REG_CIC_MASK (0xF << IRQ_REG_CIC_POS) +#define IRQ_REG_DIC_POS 8 +#define IRQ_REG_DIC_MASK (3 << 8) +#define IRQ_REG_PLB_RD_NMI (1 << 4) +#define IRQ_REG_PLB_WR_NMI (1 << 3) +#define IRQ_REG_EI (1 << 2) +#define IRQ_REG_DI (1 << 1) +#define IRQ_REG_CI (1 << 0) + +/* All interrupt bits */ +#define IRQ_REG_IRQ_MASK (IRQ_REG_PLB_RD_NMI | \ + IRQ_REG_PLB_WR_NMI | \ + IRQ_REG_EI | IRQ_REG_DI | IRQ_REG_CI) + +/* Rx/Tx Channel Status Register (*_chnl_sts), [1] p165, [2] p249/p255 */ +#define CHNL_STS_ERROR_TAIL (1 << 21) +#define CHNL_STS_ERROR_CMP (1 << 20) +#define CHNL_STS_ERROR_ADDR (1 << 19) +#define CHNL_STS_ERROR_NXTP (1 << 18) +#define CHNL_STS_ERROR_CURP (1 << 17) +#define CHNL_STS_ERROR_BSYWR (1 << 16) +#define CHNL_STS_ERROR (1 << 7) +#define CHNL_STS_IOE (1 << 6) +#define CHNL_STS_SOE (1 << 5) +#define CHNL_STS_CMPLT (1 << 4) +#define CHNL_STS_SOP (1 << 3) +#define CHNL_STS_EOP (1 << 2) +#define CHNL_STS_EBUSY (1 << 1) + +/* DMA Control Register (dma_control_reg), [1] p166, [2] p256 */ +#define DMA_CONTROL_PLBED (1 << 5) +#define DMA_CONTROL_RXOCEID (1 << 4) +#define DMA_CONTROL_TXOCEID (1 << 3) +#define DMA_CONTROL_TPE (1 << 2) +#define DMA_CONTROL_RESET (1 << 0) + +#if defined(CONFIG_XILINX_440) || defined(CONFIG_XILINX_405) + +/* Xilinx Device Control Register (DCR) in/out accessors */ +unsigned ll_temac_xldcr_in32(phys_addr_t addr); +void ll_temac_xldcr_out32(phys_addr_t addr, unsigned value); + +/* collect all register addresses for Xilinx DCR in/out accessors */ +void ll_temac_collect_xldcr_sdma_reg_addr(struct eth_device *dev); + +#endif /* CONFIG_XILINX_440 || CONFIG_XILINX_405 */ + +/* Xilinx Processor Local Bus (PLB) in/out accessors */ +unsigned ll_temac_xlplb_in32(phys_addr_t base); +void ll_temac_xlplb_out32(phys_addr_t base, unsigned value); + +/* collect all register addresses for Xilinx PLB in/out accessors */ +void ll_temac_collect_xlplb_sdma_reg_addr(struct eth_device *dev); + +/* initialize both Rx/Tx buffer descriptors */ +int ll_temac_init_sdma(struct eth_device *dev); + +/* halt both Rx/Tx transfers */ +int ll_temac_halt_sdma(struct eth_device *dev); + +/* reset SDMA and IRQ, disable interrupts and errors */ +int ll_temac_reset_sdma(struct eth_device *dev); + +/* receive buffered data from SDMA (polling ISR) */ +int ll_temac_recv_sdma(struct eth_device *dev); + +/* send buffered data to SDMA */ +int ll_temac_send_sdma(struct eth_device *dev, volatile void *packet, + int length); + +#endif /* _XILINX_LL_TEMAC_SDMA_ */ |