]> git.sur5r.net Git - u-boot/blobdiff - drivers/net/fec_mxc.c
Merge branch 'u-boot-ti/master' into 'u-boot-arm/master'
[u-boot] / drivers / net / fec_mxc.c
index d8db9f0c6e808fd2d14a6f000f2f4dd0c80fbd47..4dbcdca4a03741f5e3bfa3c334bb2fbcbbe8b7ea 100644 (file)
 #include <asm/arch/imx-regs.h>
 #include <asm/io.h>
 #include <asm/errno.h>
+#include <linux/compiler.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
+/*
+ * Timeout the transfer after 5 mS. This is usually a bit more, since
+ * the code in the tightloops this timeout is used in adds some overhead.
+ */
+#define FEC_XFER_TIMEOUT       5000
+
 #ifndef CONFIG_MII
 #error "CONFIG_MII has to be defined!"
 #endif
@@ -128,15 +135,15 @@ static int fec_mdio_read(struct ethernet_regs *eth, uint8_t phyAddr,
        return val;
 }
 
-static void fec_mii_setspeed(struct fec_priv *fec)
+static void fec_mii_setspeed(struct ethernet_regs *eth)
 {
        /*
         * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock
         * and do not drop the Preamble.
         */
        writel((((imx_get_fecclk() / 1000000) + 2) / 5) << 1,
-                       &fec->eth->mii_speed);
-       debug("%s: mii_speed %08x\n", __func__, readl(&fec->eth->mii_speed));
+                       &eth->mii_speed);
+       debug("%s: mii_speed %08x\n", __func__, readl(&eth->mii_speed));
 }
 
 static int fec_mdio_write(struct ethernet_regs *eth, uint8_t phyAddr,
@@ -187,9 +194,10 @@ int fec_phy_write(struct mii_dev *bus, int phyAddr, int dev_addr, int regAddr,
 #ifndef CONFIG_PHYLIB
 static int miiphy_restart_aneg(struct eth_device *dev)
 {
+       int ret = 0;
+#if !defined(CONFIG_FEC_MXC_NO_ANEG)
        struct fec_priv *fec = (struct fec_priv *)dev->priv;
        struct ethernet_regs *eth = fec->bus->priv;
-       int ret = 0;
 
        /*
         * Wake up from sleep if necessary
@@ -213,6 +221,7 @@ static int miiphy_restart_aneg(struct eth_device *dev)
        if (fec->mii_postcall)
                ret = fec->mii_postcall(fec->phy_id);
 
+#endif
        return ret;
 }
 
@@ -247,7 +256,7 @@ static int miiphy_wait_aneg(struct eth_device *dev)
 
 static int fec_rx_task_enable(struct fec_priv *fec)
 {
-       writel(1 << 24, &fec->eth->r_des_active);
+       writel(FEC_R_DES_ACTIVE_RDAR, &fec->eth->r_des_active);
        return 0;
 }
 
@@ -258,7 +267,7 @@ static int fec_rx_task_disable(struct fec_priv *fec)
 
 static int fec_tx_task_enable(struct fec_priv *fec)
 {
-       writel(1 << 24, &fec->eth->x_des_active);
+       writel(FEC_X_DES_ACTIVE_TDAR, &fec->eth->x_des_active);
        return 0;
 }
 
@@ -383,19 +392,38 @@ static int fec_set_hwaddr(struct eth_device *dev)
        return 0;
 }
 
-static void fec_eth_phy_config(struct eth_device *dev)
+/*
+ * Do initial configuration of the FEC registers
+ */
+static void fec_reg_setup(struct fec_priv *fec)
 {
-#ifdef CONFIG_PHYLIB
-       struct fec_priv *fec = (struct fec_priv *)dev->priv;
-       struct phy_device *phydev;
+       uint32_t rcntrl;
 
-       phydev = phy_connect(fec->bus, fec->phy_id, dev,
-                       PHY_INTERFACE_MODE_RGMII);
-       if (phydev) {
-               fec->phydev = phydev;
-               phy_config(phydev);
-       }
-#endif
+       /*
+        * Set interrupt mask register
+        */
+       writel(0x00000000, &fec->eth->imask);
+
+       /*
+        * Clear FEC-Lite interrupt event register(IEVENT)
+        */
+       writel(0xffffffff, &fec->eth->ievent);
+
+
+       /*
+        * Set FEC-Lite receive control register(R_CNTRL):
+        */
+
+       /* Start with frame length = 1518, common for all modes. */
+       rcntrl = PKTSIZE << FEC_RCNTRL_MAX_FL_SHIFT;
+       if (fec->xcv_type != SEVENWIRE)         /* xMII modes */
+               rcntrl |= FEC_RCNTRL_FCE | FEC_RCNTRL_MII_MODE;
+       if (fec->xcv_type == RGMII)
+               rcntrl |= FEC_RCNTRL_RGMII;
+       else if (fec->xcv_type == RMII)
+               rcntrl |= FEC_RCNTRL_RMII;
+
+       writel(rcntrl, &fec->eth->r_cntrl);
 }
 
 /**
@@ -468,14 +496,16 @@ static int fec_open(struct eth_device *edev)
 #endif
 
 #ifdef CONFIG_PHYLIB
-       if (!fec->phydev)
-               fec_eth_phy_config(edev);
-       if (fec->phydev) {
+       {
                /* Start up the PHY */
-               phy_startup(fec->phydev);
+               int ret = phy_startup(fec->phydev);
+
+               if (ret) {
+                       printf("Could not initialize PHY %s\n",
+                              fec->phydev->dev->name);
+                       return ret;
+               }
                speed = fec->phydev->speed;
-       } else {
-               speed = _100BASET;
        }
 #else
        miiphy_wait_aneg(edev);
@@ -512,7 +542,6 @@ static int fec_init(struct eth_device *dev, bd_t* bd)
 {
        struct fec_priv *fec = (struct fec_priv *)dev->priv;
        uint32_t mib_ptr = (uint32_t)&fec->eth->rmon_t_drop;
-       uint32_t rcntrl;
        uint32_t size;
        int i, ret;
 
@@ -560,36 +589,10 @@ static int fec_init(struct eth_device *dev, bd_t* bd)
                                   (unsigned)fec->rbd_base + size);
        }
 
-       /*
-        * Set interrupt mask register
-        */
-       writel(0x00000000, &fec->eth->imask);
-
-       /*
-        * Clear FEC-Lite interrupt event register(IEVENT)
-        */
-       writel(0xffffffff, &fec->eth->ievent);
-
-
-       /*
-        * Set FEC-Lite receive control register(R_CNTRL):
-        */
-
-       /* Start with frame length = 1518, common for all modes. */
-       rcntrl = PKTSIZE << FEC_RCNTRL_MAX_FL_SHIFT;
-       if (fec->xcv_type == SEVENWIRE)
-               rcntrl |= FEC_RCNTRL_FCE;
-       else if (fec->xcv_type == RGMII)
-               rcntrl |= FEC_RCNTRL_RGMII;
-       else if (fec->xcv_type == RMII)
-               rcntrl |= FEC_RCNTRL_RMII;
-       else    /* MII mode */
-               rcntrl |= FEC_RCNTRL_FCE | FEC_RCNTRL_MII_MODE;
-
-       writel(rcntrl, &fec->eth->r_cntrl);
+       fec_reg_setup(fec);
 
-       if (fec->xcv_type == MII10 || fec->xcv_type == MII100)
-               fec_mii_setspeed(fec);
+       if (fec->xcv_type != SEVENWIRE)
+               fec_mii_setspeed(fec->bus->priv);
 
        /*
         * Set Opcode/Pause Duration Register
@@ -676,11 +679,13 @@ static void fec_halt(struct eth_device *dev)
  * @param[in] length Data count in bytes
  * @return 0 on success
  */
-static int fec_send(struct eth_device *dev, volatile void *packet, int length)
+static int fec_send(struct eth_device *dev, void *packet, int length)
 {
        unsigned int status;
-       uint32_t size;
+       uint32_t size, end;
        uint32_t addr;
+       int timeout = FEC_XFER_TIMEOUT;
+       int ret = 0;
 
        /*
         * This routine transmits one frame.  This routine only accepts
@@ -706,8 +711,9 @@ static int fec_send(struct eth_device *dev, volatile void *packet, int length)
 #endif
 
        addr = (uint32_t)packet;
-       size = roundup(length, ARCH_DMA_MINALIGN);
-       flush_dcache_range(addr, addr + size);
+       end = roundup(addr + length, ARCH_DMA_MINALIGN);
+       addr &= ~(ARCH_DMA_MINALIGN - 1);
+       flush_dcache_range(addr, end);
 
        writew(length, &fec->tbd_base[fec->tbd_index].data_length);
        writel(addr, &fec->tbd_base[fec->tbd_index].data_pointer);
@@ -743,22 +749,28 @@ static int fec_send(struct eth_device *dev, volatile void *packet, int length)
         * invalidate data cache to see what's really in RAM. Also, we need
         * barrier here.
         */
-       invalidate_dcache_range(addr, addr + size);
-       while (readw(&fec->tbd_base[fec->tbd_index].status) & FEC_TBD_READY) {
-               udelay(1);
-               invalidate_dcache_range(addr, addr + size);
+       while (--timeout) {
+               if (!(readl(&fec->eth->x_des_active) & FEC_X_DES_ACTIVE_TDAR))
+                       break;
        }
 
-       debug("fec_send: status 0x%x index %d\n",
+       if (!timeout)
+               ret = -EINVAL;
+
+       invalidate_dcache_range(addr, addr + size);
+       if (readw(&fec->tbd_base[fec->tbd_index].status) & FEC_TBD_READY)
+               ret = -EINVAL;
+
+       debug("fec_send: status 0x%x index %d ret %i\n",
                        readw(&fec->tbd_base[fec->tbd_index].status),
-                       fec->tbd_index);
+                       fec->tbd_index, ret);
        /* for next transmission use the other buffer */
        if (fec->tbd_index)
                fec->tbd_index = 0;
        else
                fec->tbd_index = 1;
 
-       return 0;
+       return ret;
 }
 
 /**
@@ -774,9 +786,9 @@ static int fec_recv(struct eth_device *dev)
        int frame_length, len = 0;
        struct nbuf *frame;
        uint16_t bd_status;
-       uint32_t addr, size;
+       uint32_t addr, size, end;
        int i;
-       uchar buff[FEC_MAX_PKT_SIZE];
+       uchar buff[FEC_MAX_PKT_SIZE] __aligned(ARCH_DMA_MINALIGN);
 
        /*
         * Check if any critical events have happened
@@ -838,8 +850,9 @@ static int fec_recv(struct eth_device *dev)
                         * Invalidate data cache over the buffer
                         */
                        addr = (uint32_t)frame;
-                       size = roundup(frame_length, ARCH_DMA_MINALIGN);
-                       invalidate_dcache_range(addr, addr + size);
+                       end = roundup(addr + frame_length, ARCH_DMA_MINALIGN);
+                       addr &= ~(ARCH_DMA_MINALIGN - 1);
+                       invalidate_dcache_range(addr, end);
 
                        /*
                         *  Fill the buffer and pass it to upper layers
@@ -883,11 +896,21 @@ static int fec_recv(struct eth_device *dev)
        return len;
 }
 
-static int fec_probe(bd_t *bd, int dev_id, int phy_id, uint32_t base_addr)
+static void fec_set_dev_name(char *dest, int dev_id)
+{
+       sprintf(dest, (dev_id == -1) ? "FEC" : "FEC%i", dev_id);
+}
+
+#ifdef CONFIG_PHYLIB
+int fec_probe(bd_t *bd, int dev_id, uint32_t base_addr,
+               struct mii_dev *bus, struct phy_device *phydev)
+#else
+static int fec_probe(bd_t *bd, int dev_id, uint32_t base_addr,
+               struct mii_dev *bus, int phy_id)
+#endif
 {
        struct eth_device *edev;
        struct fec_priv *fec;
-       struct mii_dev *bus;
        unsigned char ethaddr[6];
        uint32_t start;
        int ret = 0;
@@ -933,71 +956,26 @@ static int fec_probe(bd_t *bd, int dev_id, int phy_id, uint32_t base_addr)
                udelay(10);
        }
 
-       /*
-        * Set interrupt mask register
-        */
-       writel(0x00000000, &fec->eth->imask);
-
-       /*
-        * Clear FEC-Lite interrupt event register(IEVENT)
-        */
-       writel(0xffffffff, &fec->eth->ievent);
-
-       /*
-        * Set FEC-Lite receive control register(R_CNTRL):
-        */
-       /*
-        * Frame length=1518; MII mode;
-        */
-       writel((PKTSIZE << FEC_RCNTRL_MAX_FL_SHIFT) | FEC_RCNTRL_FCE |
-               FEC_RCNTRL_MII_MODE, &fec->eth->r_cntrl);
-       fec_mii_setspeed(fec);
-
-       if (dev_id == -1) {
-               sprintf(edev->name, "FEC");
-               fec->dev_id = 0;
-       } else {
-               sprintf(edev->name, "FEC%i", dev_id);
-               fec->dev_id = dev_id;
-       }
-       fec->phy_id = phy_id;
-
-       bus = mdio_alloc();
-       if (!bus) {
-               printf("mdio_alloc failed\n");
-               ret = -ENOMEM;
-               goto err3;
-       }
-       bus->read = fec_phy_read;
-       bus->write = fec_phy_write;
-       sprintf(bus->name, edev->name);
-#ifdef CONFIG_MX28
-       /*
-        * The i.MX28 has two ethernet interfaces, but they are not equal.
-        * Only the first one can access the MDIO bus.
-        */
-       bus->priv = (struct ethernet_regs *)MXS_ENET0_BASE;
+       fec_reg_setup(fec);
+       fec_set_dev_name(edev->name, dev_id);
+       fec->dev_id = (dev_id == -1) ? 0 : dev_id;
+       fec->bus = bus;
+       fec_mii_setspeed(bus->priv);
+#ifdef CONFIG_PHYLIB
+       fec->phydev = phydev;
+       phy_connect_dev(phydev, edev);
+       /* Configure phy */
+       phy_config(phydev);
 #else
-       bus->priv = fec->eth;
+       fec->phy_id = phy_id;
 #endif
-       ret = mdio_register(bus);
-       if (ret) {
-               printf("mdio_register failed\n");
-               free(bus);
-               ret = -ENOMEM;
-               goto err3;
-       }
-       fec->bus = bus;
        eth_register(edev);
 
        if (fec_get_hwaddr(edev, dev_id, ethaddr) == 0) {
                debug("got MAC%d address from fuse: %pM\n", dev_id, ethaddr);
                memcpy(edev->enetaddr, ethaddr, 6);
        }
-       /* Configure phy */
-       fec_eth_phy_config(edev);
        return ret;
-
 err3:
        free(fec);
 err2:
@@ -1006,27 +984,80 @@ err1:
        return ret;
 }
 
-#ifndef CONFIG_FEC_MXC_MULTI
-int fecmxc_initialize(bd_t *bd)
+struct mii_dev *fec_get_miibus(uint32_t base_addr, int dev_id)
 {
-       int lout = 1;
+       struct ethernet_regs *eth = (struct ethernet_regs *)base_addr;
+       struct mii_dev *bus;
+       int ret;
 
-       debug("eth_init: fec_probe(bd)\n");
-       lout = fec_probe(bd, -1, CONFIG_FEC_MXC_PHYADDR, IMX_FEC_BASE);
+       bus = mdio_alloc();
+       if (!bus) {
+               printf("mdio_alloc failed\n");
+               return NULL;
+       }
+       bus->read = fec_phy_read;
+       bus->write = fec_phy_write;
+       bus->priv = eth;
+       fec_set_dev_name(bus->name, dev_id);
 
-       return lout;
+       ret = mdio_register(bus);
+       if (ret) {
+               printf("mdio_register failed\n");
+               free(bus);
+               return NULL;
+       }
+       fec_mii_setspeed(eth);
+       return bus;
 }
-#endif
 
 int fecmxc_initialize_multi(bd_t *bd, int dev_id, int phy_id, uint32_t addr)
 {
-       int lout = 1;
+       uint32_t base_mii;
+       struct mii_dev *bus = NULL;
+#ifdef CONFIG_PHYLIB
+       struct phy_device *phydev = NULL;
+#endif
+       int ret;
 
+#ifdef CONFIG_MX28
+       /*
+        * The i.MX28 has two ethernet interfaces, but they are not equal.
+        * Only the first one can access the MDIO bus.
+        */
+       base_mii = MXS_ENET0_BASE;
+#else
+       base_mii = addr;
+#endif
        debug("eth_init: fec_probe(bd, %i, %i) @ %08x\n", dev_id, phy_id, addr);
-       lout = fec_probe(bd, dev_id, phy_id, addr);
+       bus = fec_get_miibus(base_mii, dev_id);
+       if (!bus)
+               return -ENOMEM;
+#ifdef CONFIG_PHYLIB
+       phydev = phy_find_by_mask(bus, 1 << phy_id, PHY_INTERFACE_MODE_RGMII);
+       if (!phydev) {
+               free(bus);
+               return -ENOMEM;
+       }
+       ret = fec_probe(bd, dev_id, addr, bus, phydev);
+#else
+       ret = fec_probe(bd, dev_id, addr, bus, phy_id);
+#endif
+       if (ret) {
+#ifdef CONFIG_PHYLIB
+               free(phydev);
+#endif
+               free(bus);
+       }
+       return ret;
+}
 
-       return lout;
+#ifdef CONFIG_FEC_MXC_PHYADDR
+int fecmxc_initialize(bd_t *bd)
+{
+       return fecmxc_initialize_multi(bd, -1, CONFIG_FEC_MXC_PHYADDR,
+                       IMX_FEC_BASE);
 }
+#endif
 
 #ifndef CONFIG_PHYLIB
 int fecmxc_register_mii_postcall(struct eth_device *dev, int (*cb)(int))