[<prev] [next>] [day] [month] [year] [list]
Message-ID: <13B9B4C6EF24D648824FF11BE8967162035BC6AEB3@dlee02.ent.ti.com>
Date: Wed, 2 Jul 2008 20:08:10 -0500
From: "Woodruff, Richard" <r-woodruff2@...com>
To: "jgarzik@...ox.com" <jgarzik@...ox.com>,
"netdev@...r.kernel.org" <netdev@...r.kernel.org>
Subject: [PATCH] Fix smc911x.c suspend/resume and power up issues.
This patch fixes lockups seen on resume and allows a ifdown/ifup to work.
ifdown/up fix:
- MII BMCR_PDOWN is set on smc911x_close() but is not cleared on the
autonegotiaion path on open. Clearing is added into smc911x_reset()
if bit was set after making sure chip is accessible.
suspend/resume fixes:
- When trying for D2 power state the part family specification says
MODE_CTRL_STS_EDPWRDOWN_ should be written before PM_CTRL, this was missing.
This same bit also needs to be cleared on resume after chip is woken.
- The sequence of resume was changed to match init {reset, config, enable}
from old {reset, enable, config}
Patch tested on SMC9211 variant on embedded boards.
Signed-off-by: Richard Woodruff <r-woodruff2@...com>
--- smc911x.c 2008-07-02 18:32:59.000000000 -0500
+++ ../smc911x.c 2008-07-02 19:24:35.000000000 -0500
@@ -232,14 +232,16 @@ static void smc911x_reset(struct net_dev
struct smc911x_local *lp = netdev_priv(dev);
unsigned int reg, timeout=0, resets=1;
unsigned long flags;
+ int bmcr, phyaddr = lp->mii.phy_id;
DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);
- /* Take out of PM setting first */
+ /* Take out of PM setting first */
if ((SMC_GET_PMT_CTRL() & PMT_CTRL_READY_) == 0) {
/* Write to the bytetest will take out of powerdown */
SMC_SET_BYTE_TEST(0);
timeout=10;
+ /* no other writes to device before ready */
do {
udelay(10);
reg = SMC_GET_PMT_CTRL() & PMT_CTRL_READY_;
@@ -248,6 +250,16 @@ static void smc911x_reset(struct net_dev
PRINTK("%s: smc911x_reset timeout waiting for PM restore\n", dev->name);
return;
}
+ /* clear low power state */
+ SMC_SET_PHY_CTRL_STS(phyaddr, MODE_CTRL_STS_EDPWRDOWN_);
+
+ /* Link Power ON if off*/
+ SMC_GET_PHY_BMCR(phyaddr, bmcr);
+ if (bmcr & BMCR_PDOWN) {
+ bmcr &= ~BMCR_PDOWN;
+ SMC_SET_PHY_BMCR(phyaddr, bmcr);
+ mdelay(10);
+ }
}
/* Disable all interrupts */
@@ -2207,18 +2219,28 @@ static int smc911x_drv_remove(struct pla
static int smc911x_drv_suspend(struct platform_device *dev, pm_message_t state)
{
struct net_device *ndev = platform_get_drvdata(dev);
- unsigned long ioaddr = ndev->base_addr;
+ unsigned long flags, ioaddr = ndev->base_addr;
DBG(SMC_DEBUG_FUNC, "--> %s\n", __FUNCTION__);
if (ndev) {
- if (netif_running(ndev)) {
+ struct smc911x_local *lp = netdev_priv(ndev);
+ spin_lock_irqsave(&lp->lock, flags);
+ if (netif_running(ndev))
netif_device_detach(ndev);
- smc911x_shutdown(ndev);
+
+ netif_stop_queue(ndev);
+ smc911x_shutdown(ndev);
+
#if POWER_DOWN
- /* Set D2 - Energy detect only setting */
- SMC_SET_PMT_CTRL(2<<12);
-#endif
+ {
+ /* Set D2 - Energy detect only setting */
+ int phyaddr = lp->mii.phy_id;
+
+ SMC_SET_PHY_CTRL_STS(phyaddr, MODE_CTRL_STS_EDPWRDOWN_);
+ SMC_SET_PMT_CTRL(2<<12);
}
+#endif
+ spin_unlock_irqrestore(&lp->lock, flags);
}
return 0;
}
@@ -2226,18 +2248,20 @@ static int smc911x_drv_suspend(struct pl
static int smc911x_drv_resume(struct platform_device *dev)
{
struct net_device *ndev = platform_get_drvdata(dev);
+ unsigned long flags;
DBG(SMC_DEBUG_FUNC, "--> %s\n", __FUNCTION__);
if (ndev) {
struct smc911x_local *lp = netdev_priv(ndev);
+ spin_lock_irqsave(&lp->lock, flags);
+ smc911x_reset(ndev);
+ if (lp->phy_type != 0)
+ smc911x_phy_configure(&lp->phy_configure);
+ smc911x_enable(ndev);
- if (netif_running(ndev)) {
- smc911x_reset(ndev);
- smc911x_enable(ndev);
- if (lp->phy_type != 0)
- smc911x_phy_configure(&lp->phy_configure);
+ if (netif_running(ndev))
netif_device_attach(ndev);
- }
+ spin_unlock_irqrestore(&lp->lock, flags);
}
return 0;
}
Download attachment "smc911x_suspend_resume_fix.diff" of type "application/octet-stream" (3668 bytes)
Powered by blists - more mailing lists