lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
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

Powered by Openwall GNU/*/Linux Powered by OpenVZ