Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetoot...
[cascardo/linux.git] / drivers / net / ethernet / marvell / mvmdio.c
index e2f6626..7354960 100644 (file)
@@ -4,11 +4,9 @@
  * Since the MDIO interface of Marvell network interfaces is shared
  * between all network interfaces, having a single driver allows to
  * handle concurrent accesses properly (you may have four Ethernet
- * ports, but they in fact share the same SMI interface to access the
- * MDIO bus). Moreover, this MDIO interface code is similar between
- * the mv643xx_eth driver and the mvneta driver. For now, it is only
- * used by the mvneta driver, but it could later be used by the
- * mv643xx_eth driver as well.
+ * ports, but they in fact share the same SMI interface to access
+ * the MDIO bus). This driver is currently used by the mvneta and
+ * mv643xx_eth drivers.
  *
  * Copyright (C) 2012 Marvell
  *
 #define  MVMDIO_ERR_INT_SMI_DONE          0x00000010
 #define MVMDIO_ERR_INT_MASK               0x0080
 
+/*
+ * SMI Timeout measurements:
+ * - Kirkwood 88F6281 (Globalscale Dreamplug): 45us to 95us (Interrupt)
+ * - Armada 370       (Globalscale Mirabox):   41us to 43us (Polled)
+ */
+#define MVMDIO_SMI_TIMEOUT                1000 /* 1000us = 1ms */
+#define MVMDIO_SMI_POLL_INTERVAL_MIN      45
+#define MVMDIO_SMI_POLL_INTERVAL_MAX      55
+
 struct orion_mdio_dev {
        struct mutex lock;
        void __iomem *regs;
@@ -68,77 +75,68 @@ static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev)
 static int orion_mdio_wait_ready(struct mii_bus *bus)
 {
        struct orion_mdio_dev *dev = bus->priv;
-       int count;
-
-       if (dev->err_interrupt <= 0) {
-               count = 0;
-               while (1) {
-                       if (orion_mdio_smi_is_done(dev))
-                               break;
-
-                       if (count > 100) {
-                               dev_err(bus->parent,
-                                       "Timeout: SMI busy for too long\n");
-                               return -ETIMEDOUT;
-                       }
-
-                       udelay(10);
-                       count++;
-               }
-       } else {
-               if (!orion_mdio_smi_is_done(dev)) {
+       unsigned long timeout = usecs_to_jiffies(MVMDIO_SMI_TIMEOUT);
+       unsigned long end = jiffies + timeout;
+       int timedout = 0;
+
+       while (1) {
+               if (orion_mdio_smi_is_done(dev))
+                       return 0;
+               else if (timedout)
+                       break;
+
+               if (dev->err_interrupt <= 0) {
+                       usleep_range(MVMDIO_SMI_POLL_INTERVAL_MIN,
+                                    MVMDIO_SMI_POLL_INTERVAL_MAX);
+
+                       if (time_is_before_jiffies(end))
+                               ++timedout;
+               } else {
                        wait_event_timeout(dev->smi_busy_wait,
-                               orion_mdio_smi_is_done(dev),
-                               msecs_to_jiffies(100));
-                       if (!orion_mdio_smi_is_done(dev))
-                               return -ETIMEDOUT;
-               }
+                                          orion_mdio_smi_is_done(dev),
+                                          timeout);
+
+                       ++timedout;
+               }
        }
 
-       return 0;
+       dev_err(bus->parent, "Timeout: SMI busy for too long\n");
+       return  -ETIMEDOUT;
 }
 
 static int orion_mdio_read(struct mii_bus *bus, int mii_id,
                           int regnum)
 {
        struct orion_mdio_dev *dev = bus->priv;
-       int count;
        u32 val;
        int ret;
 
        mutex_lock(&dev->lock);
 
        ret = orion_mdio_wait_ready(bus);
-       if (ret < 0) {
-               mutex_unlock(&dev->lock);
-               return ret;
-       }
+       if (ret < 0)
+               goto out;
 
        writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
                (regnum << MVMDIO_SMI_PHY_REG_SHIFT)  |
                MVMDIO_SMI_READ_OPERATION),
               dev->regs);
 
-       /* Wait for the value to become available */
-       count = 0;
-       while (1) {
-               val = readl(dev->regs);
-               if (val & MVMDIO_SMI_READ_VALID)
-                       break;
-
-               if (count > 100) {
-                       dev_err(bus->parent, "Timeout when reading PHY\n");
-                       mutex_unlock(&dev->lock);
-                       return -ETIMEDOUT;
-               }
+       ret = orion_mdio_wait_ready(bus);
+       if (ret < 0)
+               goto out;
 
-               udelay(10);
-               count++;
+       val = readl(dev->regs);
+       if (!(val & MVMDIO_SMI_READ_VALID)) {
+               dev_err(bus->parent, "SMI bus read not valid\n");
+               ret = -ENODEV;
+               goto out;
        }
 
+       ret = val & 0xFFFF;
+out:
        mutex_unlock(&dev->lock);
-
-       return val & 0xFFFF;
+       return ret;
 }
 
 static int orion_mdio_write(struct mii_bus *bus, int mii_id,
@@ -150,10 +148,8 @@ static int orion_mdio_write(struct mii_bus *bus, int mii_id,
        mutex_lock(&dev->lock);
 
        ret = orion_mdio_wait_ready(bus);
-       if (ret < 0) {
-               mutex_unlock(&dev->lock);
-               return ret;
-       }
+       if (ret < 0)
+               goto out;
 
        writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
                (regnum << MVMDIO_SMI_PHY_REG_SHIFT)  |
@@ -161,9 +157,9 @@ static int orion_mdio_write(struct mii_bus *bus, int mii_id,
                (value << MVMDIO_SMI_DATA_SHIFT)),
               dev->regs);
 
+out:
        mutex_unlock(&dev->lock);
-
-       return 0;
+       return ret;
 }
 
 static int orion_mdio_reset(struct mii_bus *bus)