X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fnet%2Fibm_emac%2Fibm_emac_phy.c;h=e57862b34caef73b311b64892e03d0090e63c2db;hb=5bb96e9f2434b49a5b8f135f2a384974aa73db51;hp=67935dd33a6578788b0062122b69473cbc6e1fc8;hpb=6adfd34e85d6ddcf56a652a3dccb26f76aff8fd9;p=linux-2.6 diff --git a/drivers/net/ibm_emac/ibm_emac_phy.c b/drivers/net/ibm_emac/ibm_emac_phy.c index 67935dd33a..e57862b34c 100644 --- a/drivers/net/ibm_emac/ibm_emac_phy.c +++ b/drivers/net/ibm_emac/ibm_emac_phy.c @@ -12,7 +12,6 @@ * (c) 2004-2005, Eugene Surovegin * */ -#include #include #include #include @@ -23,6 +22,7 @@ #include +#include "ibm_emac_core.h" #include "ibm_emac_phy.h" static inline int phy_read(struct mii_phy *phy, int reg) @@ -35,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; @@ -47,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) @@ -103,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; @@ -119,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) { @@ -310,7 +339,7 @@ int mii_phy_probe(struct mii_phy *phy, int address) { struct mii_phy_def *def; int i; - u32 id; + int id; phy->autoneg = AUTONEG_DISABLE; phy->advertising = 0; @@ -325,6 +354,8 @@ int mii_phy_probe(struct mii_phy *phy, int address) /* Read ID and find matching entry */ id = (phy_read(phy, MII_PHYSID1) << 16) | phy_read(phy, MII_PHYSID2); + if (id < 0) + return -ENODEV; for (i = 0; (def = mii_phy_table[i]) != NULL; i++) if ((id & def->phy_id_mask) == def->phy_id) break;