Merge tag 'clk' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[linux-2.6.git] / drivers / net / ethernet / freescale / fec.c
index 112af9b..ddcbbb3 100644 (file)
@@ -99,7 +99,7 @@ static struct platform_device_id fec_devtype[] = {
 MODULE_DEVICE_TABLE(platform, fec_devtype);
 
 enum imx_fec_type {
-       IMX25_FEC = 1,  /* runs on i.mx25/50/53 */
+       IMX25_FEC = 1,  /* runs on i.mx25/50/53 */
        IMX27_FEC,      /* runs on i.mx27/35/51 */
        IMX28_FEC,
        IMX6Q_FEC,
@@ -132,7 +132,7 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 #elif defined (CONFIG_M5272C3)
 #define        FEC_FLASHMAC    (0xffe04000 + 4)
 #elif defined(CONFIG_MOD5272)
-#define FEC_FLASHMAC   0xffc0406b
+#define FEC_FLASHMAC   0xffc0406b
 #else
 #define        FEC_FLASHMAC    0
 #endif
@@ -255,11 +255,13 @@ struct fec_enet_private {
 #define FEC_MMFR_TA            (2 << 16)
 #define FEC_MMFR_DATA(v)       (v & 0xffff)
 
-#define FEC_MII_TIMEOUT                1000 /* us */
+#define FEC_MII_TIMEOUT                30000 /* us */
 
 /* Transmitter timeout */
 #define TX_TIMEOUT (2 * HZ)
 
+static int mii_cnt;
+
 static void *swap_buffer(void *bufaddr, int len)
 {
        int i;
@@ -516,6 +518,7 @@ fec_stop(struct net_device *ndev)
        struct fec_enet_private *fep = netdev_priv(ndev);
        const struct platform_device_id *id_entry =
                                platform_get_device_id(fep->pdev);
+       u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
 
        /* We cannot expect a graceful transmit stop without link !!! */
        if (fep->link) {
@@ -532,8 +535,10 @@ fec_stop(struct net_device *ndev)
        writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
 
        /* We have to keep ENET enabled to have MII interrupt stay working */
-       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
+       if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
                writel(2, fep->hwp + FEC_ECNTRL);
+               writel(rmii_mode, fep->hwp + FEC_R_CNTRL);
+       }
 }
 
 
@@ -819,7 +824,7 @@ static void __inline__ fec_get_mac(struct net_device *ndev)
                        iap = (unsigned char *)FEC_FLASHMAC;
 #else
                if (pdata)
-                       memcpy(iap, pdata->mac, ETH_ALEN);
+                       iap = (unsigned char *)&pdata->mac;
 #endif
        }
 
@@ -866,6 +871,8 @@ static void fec_enet_adjust_link(struct net_device *ndev)
        if (phy_dev->link) {
                if (fep->full_duplex != phy_dev->duplex) {
                        fec_restart(ndev, phy_dev->duplex);
+                       /* prevent unnecessary second fec_restart() below */
+                       fep->link = phy_dev->link;
                        status_change = 1;
                }
        }
@@ -973,8 +980,9 @@ static int fec_enet_mii_probe(struct net_device *ndev)
        }
 
        if (phy_id >= PHY_MAX_ADDR) {
-               printk(KERN_INFO "%s: no PHY, assuming direct connection "
-                       "to switch\n", ndev->name);
+               printk(KERN_INFO
+                       "%s: no PHY, assuming direct connection to switch\n",
+                       ndev->name);
                strncpy(mdio_bus_id, "0", MII_BUS_ID_SIZE);
                phy_id = 0;
        }
@@ -999,8 +1007,9 @@ static int fec_enet_mii_probe(struct net_device *ndev)
        fep->link = 0;
        fep->full_duplex = 0;
 
-       printk(KERN_INFO "%s: Freescale FEC PHY driver [%s] "
-               "(mii_bus:phy_addr=%s, irq=%d)\n", ndev->name,
+       printk(KERN_INFO
+               "%s: Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+               ndev->name,
                fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
                fep->phy_dev->irq);
 
@@ -1034,8 +1043,12 @@ static int fec_enet_mii_init(struct platform_device *pdev)
         */
        if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) {
                /* fec1 uses fec0 mii_bus */
-               fep->mii_bus = fec0_mii_bus;
-               return 0;
+               if (mii_cnt && fec0_mii_bus) {
+                       fep->mii_bus = fec0_mii_bus;
+                       mii_cnt++;
+                       return 0;
+               }
+               return -ENOENT;
        }
 
        fep->mii_timeout = 0;
@@ -1080,6 +1093,8 @@ static int fec_enet_mii_init(struct platform_device *pdev)
        if (mdiobus_register(fep->mii_bus))
                goto err_out_free_mdio_irq;
 
+       mii_cnt++;
+
        /* save fec0 mii_bus */
        if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
                fec0_mii_bus = fep->mii_bus;
@@ -1096,11 +1111,11 @@ err_out:
 
 static void fec_enet_mii_remove(struct fec_enet_private *fep)
 {
-       if (fep->phy_dev)
-               phy_disconnect(fep->phy_dev);
-       mdiobus_unregister(fep->mii_bus);
-       kfree(fep->mii_bus->irq);
-       mdiobus_free(fep->mii_bus);
+       if (--mii_cnt == 0) {
+               mdiobus_unregister(fep->mii_bus);
+               kfree(fep->mii_bus->irq);
+               mdiobus_free(fep->mii_bus);
+       }
 }
 
 static int fec_enet_get_settings(struct net_device *ndev,
@@ -1137,7 +1152,7 @@ static void fec_enet_get_drvinfo(struct net_device *ndev,
        strcpy(info->bus_info, dev_name(&ndev->dev));
 }
 
-static struct ethtool_ops fec_enet_ethtool_ops = {
+static const struct ethtool_ops fec_enet_ethtool_ops = {
        .get_settings           = fec_enet_get_settings,
        .set_settings           = fec_enet_set_settings,
        .get_drvinfo            = fec_enet_get_drvinfo,
@@ -1574,8 +1589,12 @@ fec_probe(struct platform_device *pdev)
 
        for (i = 0; i < FEC_IRQ_NUM; i++) {
                irq = platform_get_irq(pdev, i);
-               if (i && irq < 0)
-                       break;
+               if (irq < 0) {
+                       if (i)
+                               break;
+                       ret = irq;
+                       goto failed_irq;
+               }
                ret = request_irq(irq, fec_enet_interrupt, IRQF_DISABLED, pdev->name, ndev);
                if (ret) {
                        while (--i >= 0) {
@@ -1586,7 +1605,7 @@ fec_probe(struct platform_device *pdev)
                }
        }
 
-       fep->clk = clk_get(&pdev->dev, "fec_clk");
+       fep->clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(fep->clk)) {
                ret = PTR_ERR(fep->clk);
                goto failed_clk;
@@ -1638,13 +1657,18 @@ fec_drv_remove(struct platform_device *pdev)
        struct net_device *ndev = platform_get_drvdata(pdev);
        struct fec_enet_private *fep = netdev_priv(ndev);
        struct resource *r;
+       int i;
 
-       fec_stop(ndev);
+       unregister_netdev(ndev);
        fec_enet_mii_remove(fep);
+       for (i = 0; i < FEC_IRQ_NUM; i++) {
+               int irq = platform_get_irq(pdev, i);
+               if (irq > 0)
+                       free_irq(irq, ndev);
+       }
        clk_disable_unprepare(fep->clk);
        clk_put(fep->clk);
        iounmap(fep->hwp);
-       unregister_netdev(ndev);
        free_netdev(ndev);
 
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);