diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index e2f6626603138e7e7d4c7cc01e4853e83a0920a9..971a4c1bbbaa721cb43b5178fc9963cabc177c80 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -44,6 +44,15 @@ #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,34 +77,33 @@ 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; + unsigned long timeout = usecs_to_jiffies(MVMDIO_SMI_TIMEOUT); + unsigned long end = jiffies + timeout; + int timedout = 0; - if (dev->err_interrupt <= 0) { - count = 0; - while (1) { - if (orion_mdio_smi_is_done(dev)) - break; + while (1) { + if (orion_mdio_smi_is_done(dev)) + return 0; + else if (timedout) + break; - if (count > 100) { - dev_err(bus->parent, - "Timeout: SMI busy for too long\n"); - return -ETIMEDOUT; - } + if (dev->err_interrupt <= 0) { + usleep_range(MVMDIO_SMI_POLL_INTERVAL_MIN, + MVMDIO_SMI_POLL_INTERVAL_MAX); - udelay(10); - count++; - } - } else { - if (!orion_mdio_smi_is_done(dev)) { + 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,