mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-03 09:59:44 +00:00
a1ca92eaaf
Some devices can take a long time to work out whether they have a new packet or now. For example the ASIX USB Ethernet dongle can take 5 seconds to do this, since it waits until it gets a new packet on the wire before allowing the USB bulk read packet to be submitted. At present with driver mode the Ethernet receive code reads 32 packets. This can take a very long time if we must wait for all 32 packets. The old code (before driver model) worked by reading a single set of packets from the USB device, then processing all the packets with in. It would be nice to use the same behaviour with driver model. Add a flag to the receive method which indicates that the driver should try to find a packet if available, by consulting the hardware. When the flag is not set, it should just return any packet data it has already received. If there is none, it should return -EAGAIN so that the loop will terminate. Signed-off-by: Simon Glass <sjg@chromium.org>
625 lines
16 KiB
C
625 lines
16 KiB
C
/*
|
|
* (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 <dm.h>
|
|
#include <errno.h>
|
|
#include <miiphy.h>
|
|
#include <malloc.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/err.h>
|
|
#include <asm/io.h>
|
|
#include "designware.h"
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#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 -ETIMEDOUT;
|
|
}
|
|
|
|
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 = -ETIMEDOUT, 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(const 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 -ENOMEM;
|
|
}
|
|
|
|
bus->read = dw_mdio_read;
|
|
bus->write = dw_mdio_write;
|
|
snprintf(bus->name, sizeof(bus->name), name);
|
|
|
|
bus->priv = (void *)mac_regs_p;
|
|
|
|
return mdio_register(bus);
|
|
}
|
|
|
|
static void tx_descs_init(struct dw_eth_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 dw_eth_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 dw_eth_dev *priv, u8 *mac_id)
|
|
{
|
|
struct eth_mac_regs *mac_p = priv->mac_regs_p;
|
|
u32 macid_lo, macid_hi;
|
|
|
|
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 dw_eth_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 dw_eth_dev *priv, u8 *enetaddr)
|
|
{
|
|
struct eth_mac_regs *mac_p = priv->mac_regs_p;
|
|
struct eth_dma_regs *dma_p = priv->dma_regs_p;
|
|
unsigned int start;
|
|
int ret;
|
|
|
|
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) {
|
|
printf("DMA reset timeout\n");
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
mdelay(100);
|
|
};
|
|
|
|
/*
|
|
* Soft reset above clears HW address registers.
|
|
* So we have to set it here once again.
|
|
*/
|
|
_dw_write_hwaddr(priv, enetaddr);
|
|
|
|
rx_descs_init(priv);
|
|
tx_descs_init(priv);
|
|
|
|
writel(FIXEDBURST | PRIORXTX_41 | DMA_PBL, &dma_p->busmode);
|
|
|
|
#ifndef CONFIG_DW_MAC_FORCE_THRESHOLD_MODE
|
|
writel(readl(&dma_p->opmode) | FLUSHTXFIFO | STOREFORWARD,
|
|
&dma_p->opmode);
|
|
#else
|
|
writel(readl(&dma_p->opmode) | FLUSHTXFIFO,
|
|
&dma_p->opmode);
|
|
#endif
|
|
|
|
writel(readl(&dma_p->opmode) | RXSTART | TXSTART, &dma_p->opmode);
|
|
|
|
#ifdef CONFIG_DW_AXI_BURST_LEN
|
|
writel((CONFIG_DW_AXI_BURST_LEN & 0x1FF >> 1), &dma_p->axibus);
|
|
#endif
|
|
|
|
/* Start up the PHY */
|
|
ret = phy_startup(priv->phydev);
|
|
if (ret) {
|
|
printf("Could not initialize PHY %s\n",
|
|
priv->phydev->dev->name);
|
|
return ret;
|
|
}
|
|
|
|
dw_adjust_link(mac_p, priv->phydev);
|
|
|
|
if (!priv->phydev->link)
|
|
return -EIO;
|
|
|
|
writel(readl(&mac_p->conf) | RXENABLE | TXENABLE, &mac_p->conf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _dw_eth_send(struct dw_eth_dev *priv, void *packet, int length)
|
|
{
|
|
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];
|
|
uint32_t desc_start = (uint32_t)desc_p;
|
|
uint32_t desc_end = desc_start +
|
|
roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN);
|
|
uint32_t data_start = (uint32_t)desc_p->dmamac_addr;
|
|
uint32_t data_end = data_start +
|
|
roundup(length, ARCH_DMA_MINALIGN);
|
|
/*
|
|
* Strictly we only need to invalidate the "txrx_status" field
|
|
* for the following check, but on some platforms we cannot
|
|
* invalidate only 4 bytes, so we flush the entire descriptor,
|
|
* which is 16 bytes in total. This is safe because the
|
|
* individual descriptors in the array are each aligned to
|
|
* ARCH_DMA_MINALIGN and padded appropriately.
|
|
*/
|
|
invalidate_dcache_range(desc_start, desc_end);
|
|
|
|
/* 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 -EPERM;
|
|
}
|
|
|
|
memcpy(desc_p->dmamac_addr, packet, length);
|
|
|
|
/* Flush data to be sent */
|
|
flush_dcache_range(data_start, data_end);
|
|
|
|
#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(desc_start, desc_end);
|
|
|
|
/* 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 dw_eth_dev *priv, uchar **packetp)
|
|
{
|
|
u32 status, desc_num = priv->rx_currdescnum;
|
|
struct dmamacdescr *desc_p = &priv->rx_mac_descrtable[desc_num];
|
|
int length = -EAGAIN;
|
|
uint32_t desc_start = (uint32_t)desc_p;
|
|
uint32_t desc_end = desc_start +
|
|
roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN);
|
|
uint32_t data_start = (uint32_t)desc_p->dmamac_addr;
|
|
uint32_t data_end;
|
|
|
|
/* Invalidate entire buffer descriptor */
|
|
invalidate_dcache_range(desc_start, desc_end);
|
|
|
|
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 */
|
|
data_end = data_start + roundup(length, ARCH_DMA_MINALIGN);
|
|
invalidate_dcache_range(data_start, data_end);
|
|
*packetp = desc_p->dmamac_addr;
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
static int _dw_free_pkt(struct dw_eth_dev *priv)
|
|
{
|
|
u32 desc_num = priv->rx_currdescnum;
|
|
struct dmamacdescr *desc_p = &priv->rx_mac_descrtable[desc_num];
|
|
uint32_t desc_start = (uint32_t)desc_p;
|
|
uint32_t desc_end = desc_start +
|
|
roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN);
|
|
|
|
/*
|
|
* 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(desc_start, desc_end);
|
|
|
|
/* Test the wrap-around condition. */
|
|
if (++desc_num >= CONFIG_RX_DESCR_NUM)
|
|
desc_num = 0;
|
|
priv->rx_currdescnum = desc_num;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dw_phy_init(struct dw_eth_dev *priv, void *dev)
|
|
{
|
|
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 -ENODEV;
|
|
|
|
phy_connect_dev(phydev, dev);
|
|
|
|
phydev->supported &= PHY_GBIT_FEATURES;
|
|
phydev->advertising = phydev->supported;
|
|
|
|
priv->phydev = phydev;
|
|
phy_config(phydev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifndef CONFIG_DM_ETH
|
|
static int dw_eth_init(struct eth_device *dev, bd_t *bis)
|
|
{
|
|
return _dw_eth_init(dev->priv, dev->enetaddr);
|
|
}
|
|
|
|
static int dw_eth_send(struct eth_device *dev, void *packet, int length)
|
|
{
|
|
return _dw_eth_send(dev->priv, packet, length);
|
|
}
|
|
|
|
static int dw_eth_recv(struct eth_device *dev)
|
|
{
|
|
uchar *packet;
|
|
int length;
|
|
|
|
length = _dw_eth_recv(dev->priv, &packet);
|
|
if (length == -EAGAIN)
|
|
return 0;
|
|
net_process_received_packet(packet, length);
|
|
|
|
_dw_free_pkt(dev->priv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void dw_eth_halt(struct eth_device *dev)
|
|
{
|
|
return _dw_eth_halt(dev->priv);
|
|
}
|
|
|
|
static int dw_write_hwaddr(struct eth_device *dev)
|
|
{
|
|
return _dw_write_hwaddr(dev->priv, dev->enetaddr);
|
|
}
|
|
|
|
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(ARCH_DMA_MINALIGN,
|
|
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(priv, dev);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_DM_ETH
|
|
static int designware_eth_start(struct udevice *dev)
|
|
{
|
|
struct eth_pdata *pdata = dev_get_platdata(dev);
|
|
|
|
return _dw_eth_init(dev->priv, pdata->enetaddr);
|
|
}
|
|
|
|
static int designware_eth_send(struct udevice *dev, void *packet, int length)
|
|
{
|
|
struct dw_eth_dev *priv = dev_get_priv(dev);
|
|
|
|
return _dw_eth_send(priv, packet, length);
|
|
}
|
|
|
|
static int designware_eth_recv(struct udevice *dev, int flags, uchar **packetp)
|
|
{
|
|
struct dw_eth_dev *priv = dev_get_priv(dev);
|
|
|
|
return _dw_eth_recv(priv, packetp);
|
|
}
|
|
|
|
static int designware_eth_free_pkt(struct udevice *dev, uchar *packet,
|
|
int length)
|
|
{
|
|
struct dw_eth_dev *priv = dev_get_priv(dev);
|
|
|
|
return _dw_free_pkt(priv);
|
|
}
|
|
|
|
static void designware_eth_stop(struct udevice *dev)
|
|
{
|
|
struct dw_eth_dev *priv = dev_get_priv(dev);
|
|
|
|
return _dw_eth_halt(priv);
|
|
}
|
|
|
|
static int designware_eth_write_hwaddr(struct udevice *dev)
|
|
{
|
|
struct eth_pdata *pdata = dev_get_platdata(dev);
|
|
struct dw_eth_dev *priv = dev_get_priv(dev);
|
|
|
|
return _dw_write_hwaddr(priv, pdata->enetaddr);
|
|
}
|
|
|
|
static int designware_eth_probe(struct udevice *dev)
|
|
{
|
|
struct eth_pdata *pdata = dev_get_platdata(dev);
|
|
struct dw_eth_dev *priv = dev_get_priv(dev);
|
|
int ret;
|
|
|
|
debug("%s, iobase=%lx, priv=%p\n", __func__, pdata->iobase, priv);
|
|
priv->mac_regs_p = (struct eth_mac_regs *)pdata->iobase;
|
|
priv->dma_regs_p = (struct eth_dma_regs *)(pdata->iobase +
|
|
DW_DMA_BASE_OFFSET);
|
|
priv->interface = pdata->phy_interface;
|
|
|
|
dw_mdio_init(dev->name, priv->mac_regs_p);
|
|
priv->bus = miiphy_get_dev_by_name(dev->name);
|
|
|
|
ret = dw_phy_init(priv, dev);
|
|
debug("%s, ret=%d\n", __func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct eth_ops designware_eth_ops = {
|
|
.start = designware_eth_start,
|
|
.send = designware_eth_send,
|
|
.recv = designware_eth_recv,
|
|
.free_pkt = designware_eth_free_pkt,
|
|
.stop = designware_eth_stop,
|
|
.write_hwaddr = designware_eth_write_hwaddr,
|
|
};
|
|
|
|
static int designware_eth_ofdata_to_platdata(struct udevice *dev)
|
|
{
|
|
struct eth_pdata *pdata = dev_get_platdata(dev);
|
|
const char *phy_mode;
|
|
|
|
pdata->iobase = dev_get_addr(dev);
|
|
pdata->phy_interface = -1;
|
|
phy_mode = fdt_getprop(gd->fdt_blob, dev->of_offset, "phy-mode", NULL);
|
|
if (phy_mode)
|
|
pdata->phy_interface = phy_get_interface_by_name(phy_mode);
|
|
if (pdata->phy_interface == -1) {
|
|
debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct udevice_id designware_eth_ids[] = {
|
|
{ .compatible = "allwinner,sun7i-a20-gmac" },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(eth_sandbox) = {
|
|
.name = "eth_designware",
|
|
.id = UCLASS_ETH,
|
|
.of_match = designware_eth_ids,
|
|
.ofdata_to_platdata = designware_eth_ofdata_to_platdata,
|
|
.probe = designware_eth_probe,
|
|
.ops = &designware_eth_ops,
|
|
.priv_auto_alloc_size = sizeof(struct dw_eth_dev),
|
|
.platdata_auto_alloc_size = sizeof(struct eth_pdata),
|
|
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
|
};
|
|
#endif
|