diff options
Diffstat (limited to 'qemu/roms/u-boot/drivers/net/designware.c')
-rw-r--r-- | qemu/roms/u-boot/drivers/net/designware.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/qemu/roms/u-boot/drivers/net/designware.c b/qemu/roms/u-boot/drivers/net/designware.c new file mode 100644 index 000000000..78751b260 --- /dev/null +++ b/qemu/roms/u-boot/drivers/net/designware.c @@ -0,0 +1,449 @@ +/* + * (C) Copyright 2010 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Designware ethernet IP driver for u-boot + */ + +#include <common.h> +#include <miiphy.h> +#include <malloc.h> +#include <linux/compiler.h> +#include <linux/err.h> +#include <asm/io.h> +#include "designware.h" + +#if !defined(CONFIG_PHYLIB) +# error "DesignWare Ether MAC requires PHYLIB - missing CONFIG_PHYLIB" +#endif + +static int dw_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct eth_mac_regs *mac_p = bus->priv; + ulong start; + u16 miiaddr; + int timeout = CONFIG_MDIO_TIMEOUT; + + miiaddr = ((addr << MIIADDRSHIFT) & MII_ADDRMSK) | + ((reg << MIIREGSHIFT) & MII_REGMSK); + + writel(miiaddr | MII_CLKRANGE_150_250M | MII_BUSY, &mac_p->miiaddr); + + start = get_timer(0); + while (get_timer(start) < timeout) { + if (!(readl(&mac_p->miiaddr) & MII_BUSY)) + return readl(&mac_p->miidata); + udelay(10); + }; + + return -1; +} + +static int dw_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct eth_mac_regs *mac_p = bus->priv; + ulong start; + u16 miiaddr; + int ret = -1, timeout = CONFIG_MDIO_TIMEOUT; + + writel(val, &mac_p->miidata); + miiaddr = ((addr << MIIADDRSHIFT) & MII_ADDRMSK) | + ((reg << MIIREGSHIFT) & MII_REGMSK) | MII_WRITE; + + writel(miiaddr | MII_CLKRANGE_150_250M | MII_BUSY, &mac_p->miiaddr); + + start = get_timer(0); + while (get_timer(start) < timeout) { + if (!(readl(&mac_p->miiaddr) & MII_BUSY)) { + ret = 0; + break; + } + udelay(10); + }; + + return ret; +} + +static int dw_mdio_init(char *name, struct eth_mac_regs *mac_regs_p) +{ + struct mii_dev *bus = mdio_alloc(); + + if (!bus) { + printf("Failed to allocate MDIO bus\n"); + return -1; + } + + bus->read = dw_mdio_read; + bus->write = dw_mdio_write; + sprintf(bus->name, name); + + bus->priv = (void *)mac_regs_p; + + return mdio_register(bus); +} + +static void tx_descs_init(struct eth_device *dev) +{ + struct dw_eth_dev *priv = dev->priv; + struct eth_dma_regs *dma_p = priv->dma_regs_p; + struct dmamacdescr *desc_table_p = &priv->tx_mac_descrtable[0]; + char *txbuffs = &priv->txbuffs[0]; + struct dmamacdescr *desc_p; + u32 idx; + + for (idx = 0; idx < CONFIG_TX_DESCR_NUM; idx++) { + desc_p = &desc_table_p[idx]; + desc_p->dmamac_addr = &txbuffs[idx * CONFIG_ETH_BUFSIZE]; + desc_p->dmamac_next = &desc_table_p[idx + 1]; + +#if defined(CONFIG_DW_ALTDESCRIPTOR) + desc_p->txrx_status &= ~(DESC_TXSTS_TXINT | DESC_TXSTS_TXLAST | + DESC_TXSTS_TXFIRST | DESC_TXSTS_TXCRCDIS | \ + DESC_TXSTS_TXCHECKINSCTRL | \ + DESC_TXSTS_TXRINGEND | DESC_TXSTS_TXPADDIS); + + desc_p->txrx_status |= DESC_TXSTS_TXCHAIN; + desc_p->dmamac_cntl = 0; + desc_p->txrx_status &= ~(DESC_TXSTS_MSK | DESC_TXSTS_OWNBYDMA); +#else + desc_p->dmamac_cntl = DESC_TXCTRL_TXCHAIN; + desc_p->txrx_status = 0; +#endif + } + + /* Correcting the last pointer of the chain */ + desc_p->dmamac_next = &desc_table_p[0]; + + /* Flush all Tx buffer descriptors at once */ + flush_dcache_range((unsigned int)priv->tx_mac_descrtable, + (unsigned int)priv->tx_mac_descrtable + + sizeof(priv->tx_mac_descrtable)); + + writel((ulong)&desc_table_p[0], &dma_p->txdesclistaddr); + priv->tx_currdescnum = 0; +} + +static void rx_descs_init(struct eth_device *dev) +{ + struct dw_eth_dev *priv = dev->priv; + struct eth_dma_regs *dma_p = priv->dma_regs_p; + struct dmamacdescr *desc_table_p = &priv->rx_mac_descrtable[0]; + char *rxbuffs = &priv->rxbuffs[0]; + struct dmamacdescr *desc_p; + u32 idx; + + /* Before passing buffers to GMAC we need to make sure zeros + * written there right after "priv" structure allocation were + * flushed into RAM. + * Otherwise there's a chance to get some of them flushed in RAM when + * GMAC is already pushing data to RAM via DMA. This way incoming from + * GMAC data will be corrupted. */ + flush_dcache_range((unsigned int)rxbuffs, (unsigned int)rxbuffs + + RX_TOTAL_BUFSIZE); + + for (idx = 0; idx < CONFIG_RX_DESCR_NUM; idx++) { + desc_p = &desc_table_p[idx]; + desc_p->dmamac_addr = &rxbuffs[idx * CONFIG_ETH_BUFSIZE]; + desc_p->dmamac_next = &desc_table_p[idx + 1]; + + desc_p->dmamac_cntl = + (MAC_MAX_FRAME_SZ & DESC_RXCTRL_SIZE1MASK) | \ + DESC_RXCTRL_RXCHAIN; + + desc_p->txrx_status = DESC_RXSTS_OWNBYDMA; + } + + /* Correcting the last pointer of the chain */ + desc_p->dmamac_next = &desc_table_p[0]; + + /* Flush all Rx buffer descriptors at once */ + flush_dcache_range((unsigned int)priv->rx_mac_descrtable, + (unsigned int)priv->rx_mac_descrtable + + sizeof(priv->rx_mac_descrtable)); + + writel((ulong)&desc_table_p[0], &dma_p->rxdesclistaddr); + priv->rx_currdescnum = 0; +} + +static int dw_write_hwaddr(struct eth_device *dev) +{ + struct dw_eth_dev *priv = dev->priv; + struct eth_mac_regs *mac_p = priv->mac_regs_p; + u32 macid_lo, macid_hi; + u8 *mac_id = &dev->enetaddr[0]; + + macid_lo = mac_id[0] + (mac_id[1] << 8) + (mac_id[2] << 16) + + (mac_id[3] << 24); + macid_hi = mac_id[4] + (mac_id[5] << 8); + + writel(macid_hi, &mac_p->macaddr0hi); + writel(macid_lo, &mac_p->macaddr0lo); + + return 0; +} + +static void dw_adjust_link(struct eth_mac_regs *mac_p, + struct phy_device *phydev) +{ + u32 conf = readl(&mac_p->conf) | FRAMEBURSTENABLE | DISABLERXOWN; + + if (!phydev->link) { + printf("%s: No link.\n", phydev->dev->name); + return; + } + + if (phydev->speed != 1000) + conf |= MII_PORTSELECT; + + if (phydev->speed == 100) + conf |= FES_100; + + if (phydev->duplex) + conf |= FULLDPLXMODE; + + writel(conf, &mac_p->conf); + + printf("Speed: %d, %s duplex%s\n", phydev->speed, + (phydev->duplex) ? "full" : "half", + (phydev->port == PORT_FIBRE) ? ", fiber mode" : ""); +} + +static void dw_eth_halt(struct eth_device *dev) +{ + struct dw_eth_dev *priv = dev->priv; + struct eth_mac_regs *mac_p = priv->mac_regs_p; + struct eth_dma_regs *dma_p = priv->dma_regs_p; + + writel(readl(&mac_p->conf) & ~(RXENABLE | TXENABLE), &mac_p->conf); + writel(readl(&dma_p->opmode) & ~(RXSTART | TXSTART), &dma_p->opmode); + + phy_shutdown(priv->phydev); +} + +static int dw_eth_init(struct eth_device *dev, bd_t *bis) +{ + struct dw_eth_dev *priv = dev->priv; + struct eth_mac_regs *mac_p = priv->mac_regs_p; + struct eth_dma_regs *dma_p = priv->dma_regs_p; + unsigned int start; + + writel(readl(&dma_p->busmode) | DMAMAC_SRST, &dma_p->busmode); + + start = get_timer(0); + while (readl(&dma_p->busmode) & DMAMAC_SRST) { + if (get_timer(start) >= CONFIG_MACRESET_TIMEOUT) + return -1; + + mdelay(100); + }; + + /* Soft reset above clears HW address registers. + * So we have to set it here once again */ + dw_write_hwaddr(dev); + + rx_descs_init(dev); + tx_descs_init(dev); + + writel(FIXEDBURST | PRIORXTX_41 | BURST_16, &dma_p->busmode); + + writel(readl(&dma_p->opmode) | FLUSHTXFIFO | STOREFORWARD, + &dma_p->opmode); + + writel(readl(&dma_p->opmode) | RXSTART | TXSTART, &dma_p->opmode); + + /* Start up the PHY */ + if (phy_startup(priv->phydev)) { + printf("Could not initialize PHY %s\n", + priv->phydev->dev->name); + return -1; + } + + dw_adjust_link(mac_p, priv->phydev); + + if (!priv->phydev->link) + return -1; + + writel(readl(&mac_p->conf) | RXENABLE | TXENABLE, &mac_p->conf); + + return 0; +} + +static int dw_eth_send(struct eth_device *dev, void *packet, int length) +{ + struct dw_eth_dev *priv = dev->priv; + struct eth_dma_regs *dma_p = priv->dma_regs_p; + u32 desc_num = priv->tx_currdescnum; + struct dmamacdescr *desc_p = &priv->tx_mac_descrtable[desc_num]; + + /* Invalidate only "status" field for the following check */ + invalidate_dcache_range((unsigned long)&desc_p->txrx_status, + (unsigned long)&desc_p->txrx_status + + sizeof(desc_p->txrx_status)); + + /* Check if the descriptor is owned by CPU */ + if (desc_p->txrx_status & DESC_TXSTS_OWNBYDMA) { + printf("CPU not owner of tx frame\n"); + return -1; + } + + memcpy((void *)desc_p->dmamac_addr, packet, length); + + /* Flush data to be sent */ + flush_dcache_range((unsigned long)desc_p->dmamac_addr, + (unsigned long)desc_p->dmamac_addr + length); + +#if defined(CONFIG_DW_ALTDESCRIPTOR) + desc_p->txrx_status |= DESC_TXSTS_TXFIRST | DESC_TXSTS_TXLAST; + desc_p->dmamac_cntl |= (length << DESC_TXCTRL_SIZE1SHFT) & \ + DESC_TXCTRL_SIZE1MASK; + + desc_p->txrx_status &= ~(DESC_TXSTS_MSK); + desc_p->txrx_status |= DESC_TXSTS_OWNBYDMA; +#else + desc_p->dmamac_cntl |= ((length << DESC_TXCTRL_SIZE1SHFT) & \ + DESC_TXCTRL_SIZE1MASK) | DESC_TXCTRL_TXLAST | \ + DESC_TXCTRL_TXFIRST; + + desc_p->txrx_status = DESC_TXSTS_OWNBYDMA; +#endif + + /* Flush modified buffer descriptor */ + flush_dcache_range((unsigned long)desc_p, + (unsigned long)desc_p + sizeof(struct dmamacdescr)); + + /* Test the wrap-around condition. */ + if (++desc_num >= CONFIG_TX_DESCR_NUM) + desc_num = 0; + + priv->tx_currdescnum = desc_num; + + /* Start the transmission */ + writel(POLL_DATA, &dma_p->txpolldemand); + + return 0; +} + +static int dw_eth_recv(struct eth_device *dev) +{ + struct dw_eth_dev *priv = dev->priv; + u32 status, desc_num = priv->rx_currdescnum; + struct dmamacdescr *desc_p = &priv->rx_mac_descrtable[desc_num]; + int length = 0; + + /* Invalidate entire buffer descriptor */ + invalidate_dcache_range((unsigned long)desc_p, + (unsigned long)desc_p + + sizeof(struct dmamacdescr)); + + status = desc_p->txrx_status; + + /* Check if the owner is the CPU */ + if (!(status & DESC_RXSTS_OWNBYDMA)) { + + length = (status & DESC_RXSTS_FRMLENMSK) >> \ + DESC_RXSTS_FRMLENSHFT; + + /* Invalidate received data */ + invalidate_dcache_range((unsigned long)desc_p->dmamac_addr, + (unsigned long)desc_p->dmamac_addr + + length); + + NetReceive(desc_p->dmamac_addr, length); + + /* + * Make the current descriptor valid again and go to + * the next one + */ + desc_p->txrx_status |= DESC_RXSTS_OWNBYDMA; + + /* Flush only status field - others weren't changed */ + flush_dcache_range((unsigned long)&desc_p->txrx_status, + (unsigned long)&desc_p->txrx_status + + sizeof(desc_p->txrx_status)); + + /* Test the wrap-around condition. */ + if (++desc_num >= CONFIG_RX_DESCR_NUM) + desc_num = 0; + } + + priv->rx_currdescnum = desc_num; + + return length; +} + +static int dw_phy_init(struct eth_device *dev) +{ + struct dw_eth_dev *priv = dev->priv; + struct phy_device *phydev; + int mask = 0xffffffff; + +#ifdef CONFIG_PHY_ADDR + mask = 1 << CONFIG_PHY_ADDR; +#endif + + phydev = phy_find_by_mask(priv->bus, mask, priv->interface); + if (!phydev) + return -1; + + phy_connect_dev(phydev, dev); + + phydev->supported &= PHY_GBIT_FEATURES; + phydev->advertising = phydev->supported; + + priv->phydev = phydev; + phy_config(phydev); + + return 1; +} + +int designware_initialize(ulong base_addr, u32 interface) +{ + struct eth_device *dev; + struct dw_eth_dev *priv; + + dev = (struct eth_device *) malloc(sizeof(struct eth_device)); + if (!dev) + return -ENOMEM; + + /* + * Since the priv structure contains the descriptors which need a strict + * buswidth alignment, memalign is used to allocate memory + */ + priv = (struct dw_eth_dev *) memalign(16, sizeof(struct dw_eth_dev)); + if (!priv) { + free(dev); + return -ENOMEM; + } + + memset(dev, 0, sizeof(struct eth_device)); + memset(priv, 0, sizeof(struct dw_eth_dev)); + + sprintf(dev->name, "dwmac.%lx", base_addr); + dev->iobase = (int)base_addr; + dev->priv = priv; + + priv->dev = dev; + priv->mac_regs_p = (struct eth_mac_regs *)base_addr; + priv->dma_regs_p = (struct eth_dma_regs *)(base_addr + + DW_DMA_BASE_OFFSET); + + dev->init = dw_eth_init; + dev->send = dw_eth_send; + dev->recv = dw_eth_recv; + dev->halt = dw_eth_halt; + dev->write_hwaddr = dw_write_hwaddr; + + eth_register(dev); + + priv->interface = interface; + + dw_mdio_init(dev->name, priv->mac_regs_p); + priv->bus = miiphy_get_dev_by_name(dev->name); + + return dw_phy_init(dev); +} |