diff options
author | David S. Miller <davem@davemloft.net> | 2013-12-10 05:39:05 +0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-12-10 05:39:05 +0400 |
commit | 65be6291c856bee0308c535287f813418402d8b3 (patch) | |
tree | bc291d4587494cd5419e672eaeb3e9682239c608 | |
parent | 73713357ab58aacda1af715bb5a623528dbbfd79 (diff) | |
parent | 0c9eb5b931c3da3a79faa889b903dc7bd318203c (diff) | |
download | linux-65be6291c856bee0308c535287f813418402d8b3.tar.xz |
Merge branch 'phy_reset'
Florian Fainelli says:
====================
net: phy: consolidate PHY reset
This patchset consolidates the PHY reset through the MII BMCR
register by using a central place were this is done.
This patchset resumes the work Kyle Moffett started here:
https://lkml.org/lkml/2011/10/20/301
Note that at this point, drivers doing funky things after issuing
a PHY reset using phy_init_hw() will still suffer from PHY state
machine problems, this will be taken care of later on.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | Documentation/networking/phy.txt | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/adi/bfin_mac.c | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/aeroflex/greth.c | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/marvell/mv643xx_eth.c | 21 | ||||
-rw-r--r-- | drivers/net/ethernet/marvell/pxa168_eth.c | 20 | ||||
-rw-r--r-- | drivers/net/ethernet/renesas/sh_eth.c | 5 | ||||
-rw-r--r-- | drivers/net/ethernet/toshiba/tc35815.c | 15 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 11 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 62 | ||||
-rw-r--r-- | include/linux/phy.h | 5 |
10 files changed, 82 insertions, 63 deletions
diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt index d5b1a3935245..ebf270719402 100644 --- a/Documentation/networking/phy.txt +++ b/Documentation/networking/phy.txt @@ -255,7 +255,8 @@ Writing a PHY driver config_init: configures PHY into a sane state after a reset. For instance, a Davicom PHY requires descrambling disabled. - probe: Does any setup needed by the driver + probe: Allocate phy->priv, optionally refuse to bind. + PHY may not have been reset or had fixups run yet. suspend/resume: power management config_aneg: Changes the speed/duplex/negotiation settings read_status: Reads the current speed/duplex/negotiation settings diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index 0d4f29579879..c0f68dcd1dc1 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -1557,7 +1557,6 @@ static int bfin_mac_open(struct net_device *dev) return ret; phy_start(lp->phydev); - phy_write(lp->phydev, MII_BMCR, BMCR_RESET); setup_system_regs(dev); setup_mac_addr(dev->dev_addr); diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index e06694555144..b20cbf0323e0 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -1361,7 +1361,7 @@ static int greth_mdio_init(struct greth_private *greth) timeout = jiffies + 6*HZ; while (!phy_aneg_done(greth->phy) && time_before(jiffies, timeout)) { } - genphy_read_status(greth->phy); + phy_read_status(greth->phy); greth_link_change(greth->netdev); } diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index fa905052fbf4..b3d9327f78b9 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2066,23 +2066,6 @@ static inline void oom_timer_wrapper(unsigned long data) napi_schedule(&mp->napi); } -static void phy_reset(struct mv643xx_eth_private *mp) -{ - int data; - - data = phy_read(mp->phy, MII_BMCR); - if (data < 0) - return; - - data |= BMCR_RESET; - if (phy_write(mp->phy, MII_BMCR, data) < 0) - return; - - do { - data = phy_read(mp->phy, MII_BMCR); - } while (data >= 0 && data & BMCR_RESET); -} - static void port_start(struct mv643xx_eth_private *mp) { u32 pscr; @@ -2095,7 +2078,7 @@ static void port_start(struct mv643xx_eth_private *mp) struct ethtool_cmd cmd; mv643xx_eth_get_settings(mp->dev, &cmd); - phy_reset(mp); + phy_init_hw(mp->phy); mv643xx_eth_set_settings(mp->dev, &cmd); } @@ -2763,8 +2746,6 @@ static void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex) { struct phy_device *phy = mp->phy; - phy_reset(mp); - if (speed == 0) { phy->autoneg = AUTONEG_ENABLE; phy->speed = 0; diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 2ad32417a52c..452e81de33de 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -320,23 +320,6 @@ static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int phy_addr) wrl(pep, PHY_ADDRESS, reg_data); } -static void ethernet_phy_reset(struct pxa168_eth_private *pep) -{ - int data; - - data = phy_read(pep->phy, MII_BMCR); - if (data < 0) - return; - - data |= BMCR_RESET; - if (phy_write(pep->phy, MII_BMCR, data) < 0) - return; - - do { - data = phy_read(pep->phy, MII_BMCR); - } while (data >= 0 && data & BMCR_RESET); -} - static void rxq_refill(struct net_device *dev) { struct pxa168_eth_private *pep = netdev_priv(dev); @@ -645,7 +628,7 @@ static void eth_port_start(struct net_device *dev) struct ethtool_cmd cmd; pxa168_get_settings(pep->dev, &cmd); - ethernet_phy_reset(pep); + phy_init_hw(pep->phy); pxa168_set_settings(pep->dev, &cmd); } @@ -1382,7 +1365,6 @@ static struct phy_device *phy_scan(struct pxa168_eth_private *pep, int phy_addr) static void phy_init(struct pxa168_eth_private *pep, int speed, int duplex) { struct phy_device *phy = pep->phy; - ethernet_phy_reset(pep); phy_attach(pep->dev, dev_name(&phy->dev), PHY_INTERFACE_MODE_MII); diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index d256ce19d4de..6761dde7007a 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1704,7 +1704,10 @@ static int sh_eth_phy_start(struct net_device *ndev) return ret; /* reset phy - this also wakes it from PDOWN */ - phy_write(mdp->phydev, MII_BMCR, BMCR_RESET); + ret = phy_init_hw(mdp->phydev); + if (ret) + return ret; + phy_start(mdp->phydev); return 0; diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index 1322546d92ac..f6b3212ec339 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -1170,19 +1170,12 @@ static int tc35815_tx_full(struct net_device *dev) static void tc35815_restart(struct net_device *dev) { struct tc35815_local *lp = netdev_priv(dev); + int ret; if (lp->phy_dev) { - int timeout; - - phy_write(lp->phy_dev, MII_BMCR, BMCR_RESET); - timeout = 100; - while (--timeout) { - if (!(phy_read(lp->phy_dev, MII_BMCR) & BMCR_RESET)) - break; - udelay(1); - } - if (!timeout) - printk(KERN_ERR "%s: BMCR reset failed.\n", dev->name); + ret = phy_init_hw(lp->phy_dev); + if (ret) + printk(KERN_ERR "%s: PHY init failed.\n", dev->name); } spin_lock_bh(&lp->rx_lock); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 36c6994436b7..e3dd69100da8 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -289,6 +289,7 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd) cmd->supported = phydev->supported; cmd->advertising = phydev->advertising; + cmd->lp_advertising = phydev->lp_advertising; ethtool_cmd_speed_set(cmd, phydev->speed); cmd->duplex = phydev->duplex; @@ -317,6 +318,7 @@ int phy_mii_ioctl(struct phy_device *phydev, { struct mii_ioctl_data *mii_data = if_mii(ifr); u16 val = mii_data->val_in; + int ret = 0; switch (cmd) { case SIOCGMIIPHY: @@ -360,11 +362,8 @@ int phy_mii_ioctl(struct phy_device *phydev, mii_data->reg_num, val); if (mii_data->reg_num == MII_BMCR && - val & BMCR_RESET && - phydev->drv->config_init) { - phy_scan_fixups(phydev); - phydev->drv->config_init(phydev); - } + val & BMCR_RESET) + ret = phy_init_hw(phydev); break; case SIOCSHWTSTAMP: @@ -376,7 +375,7 @@ int phy_mii_ioctl(struct phy_device *phydev, return -EOPNOTSUPP; } - return 0; + return ret; } EXPORT_SYMBOL(phy_mii_ioctl); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index d6447b3f7409..5a619f0dcf73 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -364,7 +364,11 @@ int phy_device_register(struct phy_device *phydev) phydev->bus->phy_map[phydev->addr] = phydev; /* Run all of the fixups for this PHY */ - phy_scan_fixups(phydev); + err = phy_init_hw(phydev); + if (err) { + pr_err("PHY %d failed to initialize\n", phydev->addr); + goto out; + } err = device_add(&phydev->dev); if (err) { @@ -497,6 +501,47 @@ void phy_disconnect(struct phy_device *phydev) } EXPORT_SYMBOL(phy_disconnect); +/** + * phy_poll_reset - Safely wait until a PHY reset has properly completed + * @phydev: The PHY device to poll + * + * Description: According to IEEE 802.3, Section 2, Subsection 22.2.4.1.1, as + * published in 2008, a PHY reset may take up to 0.5 seconds. The MII BMCR + * register must be polled until the BMCR_RESET bit clears. + * + * Furthermore, any attempts to write to PHY registers may have no effect + * or even generate MDIO bus errors until this is complete. + * + * Some PHYs (such as the Marvell 88E1111) don't entirely conform to the + * standard and do not fully reset after the BMCR_RESET bit is set, and may + * even *REQUIRE* a soft-reset to properly restart autonegotiation. In an + * effort to support such broken PHYs, this function is separate from the + * standard phy_init_hw() which will zero all the other bits in the BMCR + * and reapply all driver-specific and board-specific fixups. + */ +static int phy_poll_reset(struct phy_device *phydev) +{ + /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */ + unsigned int retries = 12; + int ret; + + do { + msleep(50); + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + } while (ret & BMCR_RESET && --retries); + if (ret & BMCR_RESET) + return -ETIMEDOUT; + + /* + * Some chips (smsc911x) may still need up to another 1ms after the + * BMCR_RESET bit is cleared before they are usable. + */ + msleep(1); + return 0; +} + int phy_init_hw(struct phy_device *phydev) { int ret; @@ -504,12 +549,21 @@ int phy_init_hw(struct phy_device *phydev) if (!phydev->drv || !phydev->drv->config_init) return 0; + ret = phy_write(phydev, MII_BMCR, BMCR_RESET); + if (ret < 0) + return ret; + + ret = phy_poll_reset(phydev); + if (ret < 0) + return ret; + ret = phy_scan_fixups(phydev); if (ret < 0) return ret; return phydev->drv->config_init(phydev); } +EXPORT_SYMBOL(phy_init_hw); /** * phy_attach_direct - attach a network device to a given PHY device pointer @@ -839,6 +893,8 @@ int genphy_read_status(struct phy_device *phydev) if (err) return err; + phydev->lp_advertising = 0; + if (AUTONEG_ENABLE == phydev->autoneg) { if (phydev->supported & (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)) { @@ -852,6 +908,8 @@ int genphy_read_status(struct phy_device *phydev) if (adv < 0) return adv; + phydev->lp_advertising = + mii_stat1000_to_ethtool_lpa_t(lpagb); lpagb &= adv << 2; } @@ -860,6 +918,8 @@ int genphy_read_status(struct phy_device *phydev) if (lpa < 0) return lpa; + phydev->lp_advertising |= mii_lpa_to_ethtool_lpa_t(lpa); + adv = phy_read(phydev, MII_ADVERTISE); if (adv < 0) diff --git a/include/linux/phy.h b/include/linux/phy.h index 7ff751ae6f0a..90a666e0884b 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -287,8 +287,8 @@ struct phy_c45_device_ids { * adjust_state: Callback for the enet driver to respond to * changes in the state machine. * - * speed, duplex, pause, supported, advertising, and - * autoneg are used like in mii_if_info + * speed, duplex, pause, supported, advertising, lp_advertising, + * and autoneg are used like in mii_if_info * * interrupts currently only supports enabled or disabled, * but could be changed in the future to support enabling @@ -340,6 +340,7 @@ struct phy_device { /* See mii.h for more info */ u32 supported; u32 advertising; + u32 lp_advertising; int autoneg; |