From patchwork Thu Sep 9 19:51:58 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cyril Chemparathy X-Patchwork-Id: 165381 X-Patchwork-Delegate: khilman@deeprootsystems.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o89JrIiK017604 for ; Thu, 9 Sep 2010 19:53:18 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756091Ab0IITwH (ORCPT ); Thu, 9 Sep 2010 15:52:07 -0400 Received: from devils.ext.ti.com ([198.47.26.153]:48984 "EHLO devils.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753108Ab0IITwF (ORCPT ); Thu, 9 Sep 2010 15:52:05 -0400 Received: from dlep33.itg.ti.com ([157.170.170.112]) by devils.ext.ti.com (8.13.7/8.13.7) with ESMTP id o89Jq0bN007732 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Thu, 9 Sep 2010 14:52:00 -0500 Received: from legion.dal.design.ti.com (localhost [127.0.0.1]) by dlep33.itg.ti.com (8.13.7/8.13.7) with ESMTP id o89Jq0ic019147; Thu, 9 Sep 2010 14:52:00 -0500 (CDT) Received: from gtrgwdeb (gtrgwdeb.telogy.design.ti.com [158.218.102.24]) by legion.dal.design.ti.com (8.11.7p1+Sun/8.11.7) with ESMTP id o89Jpxf17286; Thu, 9 Sep 2010 14:51:59 -0500 (CDT) Received: from [127.0.0.1] (localhost [127.0.0.1]) by gtrgwdeb (Postfix) with ESMTP id 481E21E2732; Thu, 9 Sep 2010 15:51:59 -0400 (EDT) Message-ID: <4C893ADE.809@ti.com> Date: Thu, 09 Sep 2010 15:51:58 -0400 From: Cyril Chemparathy Reply-To: cyril@ti.com Organization: Texas Instruments User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.12) Gecko/20100826 Lightning/1.0b1 Thunderbird/3.0.7 ThunderBrowse/3.2.8.1 MIME-Version: 1.0 To: Michael Williamson CC: Kevin Hilman , "tony@atomide.com" , "netdev@vger.kernel.org" , "davinci-linux-open-source@linux.davincidsp.com" , "linux-omap@vger.kernel.org" , "davem@davemloft.net" Subject: Re: [PATCH v3 00/10] split out emac cpdma and mdio for reuse References: <1283891142-15522-1-git-send-email-cyril@ti.com> <871v95f2xv.fsf@deeprootsystems.com> <4C86F369.6040906@criticallink.com> <4C880733.5010909@ti.com> <4C882EAA.9030903@criticallink.com> <4C892AC0.80409@criticallink.com> In-Reply-To: <4C892AC0.80409@criticallink.com> Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Thu, 09 Sep 2010 19:53:19 +0000 (UTC) diff --git a/drivers/net/davinci_mdio.c b/drivers/net/davinci_mdio.c index d34a53a..96a0f9e 100644 --- a/drivers/net/davinci_mdio.c +++ b/drivers/net/davinci_mdio.c @@ -36,6 +36,7 @@ #include #include +#define MDIO_TIMEOUT 10 /* msecs */ #define PHY_REG_MASK 0x1f #define PHY_ID_MASK 0x1f @@ -85,31 +86,74 @@ struct davinci_mdio_data { bool suspended; }; +static void __davinci_mdio_reset(struct mii_bus *bus) +{ + struct davinci_mdio_data *data = bus->priv; + u32 mdio_in_freq, div; + + mdio_in_freq = clk_get_rate(data->clk); + div = (mdio_in_freq / data->pdata.bus_freq) - 1; + if (div > CONTROL_MAX_DIV) + div = CONTROL_MAX_DIV; + + /* set enable and clock divider */ + __raw_writel(div | CONTROL_ENABLE, &data->regs->control); +} + /* wait until hardware is ready for another user access */ -static inline u32 wait_for_user_access(struct davinci_mdio_data *data) +static inline int wait_for_user_access(struct davinci_mdio_data *data) { - struct davinci_mdio_regs __iomem *regs = data->regs; + unsigned long timeout = jiffies + msecs_to_jiffies(MDIO_TIMEOUT); u32 reg; + int ret = -ETIMEDOUT; - while ((reg = __raw_readl(®s->user[0].access)) & USERACCESS_GO) - ; + while (time_after(timeout, jiffies)) { + reg = __raw_readl(&data->regs->user[0].access); + if ((reg & USERACCESS_GO) == 0) { + ret = 0; + break; + } - return reg; + reg = __raw_readl(&data->regs->control); + if (reg & CONTROL_IDLE) { + /* + * An emac soft_reset may have clobbered the mdio + * controller's state machine. We need to reset and + * retry the current operation + */ + dev_warn(data->dev, "controller idle in transaction, " + "resetting\n"); + __davinci_mdio_reset(data->bus); + ret = -EAGAIN; + break; + } + } + return ret; } /* wait until hardware state machine is idle */ -static inline void wait_for_idle(struct davinci_mdio_data *data) +static inline int wait_for_idle(struct davinci_mdio_data *data) { - struct davinci_mdio_regs __iomem *regs = data->regs; + unsigned long timeout = jiffies + msecs_to_jiffies(MDIO_TIMEOUT); + int ret = -ETIMEDOUT; + u32 reg; - while ((__raw_readl(®s->control) & CONTROL_IDLE) == 0) - ; + while (time_after(timeout, jiffies)) { + reg = __raw_readl(&data->regs->control); + if (reg & CONTROL_IDLE) { + ret = 0; + break; + } + } + + return ret; } static int davinci_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg) { struct davinci_mdio_data *data = bus->priv; u32 reg; + int ret; if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK) return -EINVAL; @@ -121,14 +165,32 @@ static int davinci_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg) return -ENODEV; } - wait_for_user_access(data); reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) | (phy_id << 16)); - __raw_writel(reg, &data->regs->user[0].access); - reg = wait_for_user_access(data); + + while (1) { + ret = wait_for_user_access(data); + if (ret == -EAGAIN) + continue; + if (ret < 0) + break; + + __raw_writel(reg, &data->regs->user[0].access); + + ret = wait_for_user_access(data); + if (ret == -EAGAIN) + continue; + if (ret < 0) + break; + + reg = __raw_readl(&data->regs->user[0].access); + ret = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -EIO; + break; + } + spin_unlock(&data->lock); - return (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -EIO; + return ret; } static int davinci_mdio_write(struct mii_bus *bus, int phy_id, @@ -136,6 +198,7 @@ static int davinci_mdio_write(struct mii_bus *bus, int phy_id, { struct davinci_mdio_data *data = bus->priv; u32 reg; + int ret; if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK) return -EINVAL; @@ -147,23 +210,68 @@ static int davinci_mdio_write(struct mii_bus *bus, int phy_id, return -ENODEV; } - wait_for_user_access(data); reg = (USERACCESS_GO | USERACCESS_WRITE | (phy_reg << 21) | (phy_id << 16) | (phy_data & USERACCESS_DATA)); - __raw_writel(reg, &data->regs->user[0].access); - wait_for_user_access(data); + + while (1) { + ret = wait_for_user_access(data); + if (ret == -EAGAIN) + continue; + if (ret < 0) + break; + + __raw_writel(reg, &data->regs->user[0].access); + + ret = wait_for_user_access(data); + if (ret == -EAGAIN) + continue; + break; + } + spin_unlock(&data->lock); return 0; } +static int davinci_mdio_reset(struct mii_bus *bus) +{ + struct davinci_mdio_data *data = bus->priv; + u32 phy_mask; + + __davinci_mdio_reset(bus); + + /* + * wait for scan logic to settle: + * the scan time consists of (a) a large fixed component, and (b) a + * small component that varies with the mii bus frequency. These + * were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x + * silicon. Since the effect of (b) was found to be largely + * negligible, we keep things simple here. + */ + mdelay(1); + + /* get phy mask from the alive register */ + phy_mask = __raw_readl(&data->regs->alive); + if (phy_mask) { + /* restrict mdio bus to live phys only */ + dev_info(data->dev, "detected phy mask %x\n", ~phy_mask); + phy_mask = ~phy_mask; + } else { + /* desperately scan all phys */ + dev_warn(data->dev, "failed to detect live phys, scanning all\n"); + phy_mask = 0; + } + bus->phy_mask = phy_mask; + + return 0; +} + static int __devinit davinci_mdio_probe(struct platform_device *pdev) { struct mdio_platform_data *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct davinci_mdio_data *data; struct resource *res; - u32 mdio_in_freq, mdio_out_freq, div, phy_mask, ver; struct phy_device *phy; int ret, addr; @@ -185,6 +293,7 @@ static int __devinit davinci_mdio_probe(struct platform_device *pdev) data->bus->name = dev_name(dev); data->bus->read = davinci_mdio_read, data->bus->write = davinci_mdio_write, + data->bus->reset = davinci_mdio_reset, data->bus->parent = dev; data->bus->priv = data; snprintf(data->bus->id, MII_BUS_ID_SIZE, "%x", pdev->id); @@ -225,43 +334,6 @@ static int __devinit davinci_mdio_probe(struct platform_device *pdev) goto bail_out; } - mdio_in_freq = clk_get_rate(data->clk); - div = (mdio_in_freq / data->pdata.bus_freq) - 1; - if (div > CONTROL_MAX_DIV) - div = CONTROL_MAX_DIV; - mdio_out_freq = mdio_in_freq / (div + 1); - - /* set enable and clock divider */ - __raw_writel(div | CONTROL_ENABLE, &data->regs->control); - - /* - * wait for scan logic to settle: - * the scan time consists of (a) a large fixed component, and (b) a - * small component that varies with the mii bus frequency. These - * were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x - * silicon. Since the effect of (b) was found to be largely - * negligible, we keep things simple here. - */ - mdelay(1); - - /* dump hardware version info */ - ver = __raw_readl(&data->regs->version); - dev_info(dev, "davinci mdio revision %d.%d\n", - (ver >> 8) & 0xff, ver & 0xff); - - /* get phy mask from the alive register */ - phy_mask = __raw_readl(&data->regs->alive); - if (phy_mask) { - /* restrict mdio bus to live phys only */ - dev_info(dev, "detected phy mask %x\n", ~phy_mask); - phy_mask = ~phy_mask; - } else { - /* desperately scan all phys */ - dev_warn(dev, "failed to detect live phys, scanning all\n"); - phy_mask = 0; - } - data->bus->phy_mask = phy_mask; - /* register the mii bus */ ret = mdiobus_register(data->bus); if (ret) @@ -324,7 +396,7 @@ static int davinci_mdio_suspend(struct device *dev) ctrl = __raw_readl(&data->regs->control); ctrl &= ~CONTROL_ENABLE; __raw_writel(ctrl, &data->regs->control); - wait_for_idle(data); + WARN_ON(wait_for_idle(data) < 0); if (data->clk) clk_disable(data->clk);