]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/ibm_emac/ibm_emac_phy.c
tty_check_change(): avoid taking tasklist_lock while holding tty->ctrl_lock
[linux-2.6-omap-h63xx.git] / drivers / net / ibm_emac / ibm_emac_phy.c
index 9074f76ee2bf2f088f3e838f55ed5e28f9136a34..e57862b34caef73b311b64892e03d0090e63c2db 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <asm/ocp.h>
 
+#include "ibm_emac_core.h"
 #include "ibm_emac_phy.h"
 
 static inline int phy_read(struct mii_phy *phy, int reg)
@@ -34,11 +35,39 @@ static inline void phy_write(struct mii_phy *phy, int reg, int val)
        phy->mdio_write(phy->dev, phy->address, reg, val);
 }
 
-int mii_reset_phy(struct mii_phy *phy)
+/*
+ * polls MII_BMCR until BMCR_RESET bit clears or operation times out.
+ *
+ * returns:
+ *     >= 0 => success, value in BMCR returned to caller
+ *     -EBUSY => failure, RESET bit never cleared
+ *     otherwise => failure, lower level PHY read failed
+ */
+static int mii_spin_reset_complete(struct mii_phy *phy)
 {
        int val;
        int limit = 10000;
 
+       while (limit--) {
+               val = phy_read(phy, MII_BMCR);
+               if (val >= 0 && !(val & BMCR_RESET))
+                       return val;     /* success */
+               udelay(10);
+       }
+       if (val & BMCR_RESET)
+               val = -EBUSY;
+
+       if (net_ratelimit())
+               printk(KERN_ERR "emac%d: PHY reset timeout (%d)\n", 
+                      ((struct ocp_enet_private *)phy->dev->priv)->def->index,
+                      val);
+       return val;                 
+}
+
+int mii_reset_phy(struct mii_phy *phy)
+{
+       int val;
+
        val = phy_read(phy, MII_BMCR);
        val &= ~BMCR_ISOLATE;
        val |= BMCR_RESET;
@@ -46,16 +75,11 @@ int mii_reset_phy(struct mii_phy *phy)
 
        udelay(300);
 
-       while (limit--) {
-               val = phy_read(phy, MII_BMCR);
-               if (val >= 0 && (val & BMCR_RESET) == 0)
-                       break;
-               udelay(10);
-       }
-       if ((val & BMCR_ISOLATE) && limit > 0)
+       val = mii_spin_reset_complete(phy);
+       if (val >= 0 && (val & BMCR_ISOLATE))
                phy_write(phy, MII_BMCR, val & ~BMCR_ISOLATE);
 
-       return limit <= 0;
+       return val < 0;
 }
 
 static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
@@ -102,8 +126,14 @@ static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
        }
 
        /* Start/Restart aneg */
-       ctl = phy_read(phy, MII_BMCR);
-       ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+       /* on some PHYs (e.g. National DP83843) a write to MII_ADVERTISE
+        * causes BMCR_RESET to be set on the next read of MII_BMCR, which
+        * if not checked for causes the PHY to be reset below */
+       ctl = mii_spin_reset_complete(phy);
+       if (ctl < 0)
+               return ctl;
+
+       ctl |= BMCR_ANENABLE | BMCR_ANRESTART;
        phy_write(phy, MII_BMCR, ctl);
 
        return 0;
@@ -118,13 +148,13 @@ static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
        phy->duplex = fd;
        phy->pause = phy->asym_pause = 0;
 
+       /* First reset the PHY */
+       mii_reset_phy(phy);
+
        ctl = phy_read(phy, MII_BMCR);
        if (ctl < 0)
                return ctl;
-       ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_ANENABLE);
-
-       /* First reset the PHY */
-       phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
+       ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_ANENABLE | BMCR_SPEED1000);
 
        /* Select speed & duplex */
        switch (speed) {