Commit 65be6291 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 73713357 0c9eb5b9
...@@ -255,7 +255,8 @@ Writing a PHY driver ...@@ -255,7 +255,8 @@ Writing a PHY driver
config_init: configures PHY into a sane state after a reset. config_init: configures PHY into a sane state after a reset.
For instance, a Davicom PHY requires descrambling disabled. 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 suspend/resume: power management
config_aneg: Changes the speed/duplex/negotiation settings config_aneg: Changes the speed/duplex/negotiation settings
read_status: Reads the current speed/duplex/negotiation settings read_status: Reads the current speed/duplex/negotiation settings
......
...@@ -1557,7 +1557,6 @@ static int bfin_mac_open(struct net_device *dev) ...@@ -1557,7 +1557,6 @@ static int bfin_mac_open(struct net_device *dev)
return ret; return ret;
phy_start(lp->phydev); phy_start(lp->phydev);
phy_write(lp->phydev, MII_BMCR, BMCR_RESET);
setup_system_regs(dev); setup_system_regs(dev);
setup_mac_addr(dev->dev_addr); setup_mac_addr(dev->dev_addr);
......
...@@ -1361,7 +1361,7 @@ static int greth_mdio_init(struct greth_private *greth) ...@@ -1361,7 +1361,7 @@ static int greth_mdio_init(struct greth_private *greth)
timeout = jiffies + 6*HZ; timeout = jiffies + 6*HZ;
while (!phy_aneg_done(greth->phy) && time_before(jiffies, timeout)) { 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); greth_link_change(greth->netdev);
} }
......
...@@ -2066,23 +2066,6 @@ static inline void oom_timer_wrapper(unsigned long data) ...@@ -2066,23 +2066,6 @@ static inline void oom_timer_wrapper(unsigned long data)
napi_schedule(&mp->napi); 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) static void port_start(struct mv643xx_eth_private *mp)
{ {
u32 pscr; u32 pscr;
...@@ -2095,7 +2078,7 @@ static void port_start(struct mv643xx_eth_private *mp) ...@@ -2095,7 +2078,7 @@ static void port_start(struct mv643xx_eth_private *mp)
struct ethtool_cmd cmd; struct ethtool_cmd cmd;
mv643xx_eth_get_settings(mp->dev, &cmd); mv643xx_eth_get_settings(mp->dev, &cmd);
phy_reset(mp); phy_init_hw(mp->phy);
mv643xx_eth_set_settings(mp->dev, &cmd); mv643xx_eth_set_settings(mp->dev, &cmd);
} }
...@@ -2763,8 +2746,6 @@ static void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex) ...@@ -2763,8 +2746,6 @@ static void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex)
{ {
struct phy_device *phy = mp->phy; struct phy_device *phy = mp->phy;
phy_reset(mp);
if (speed == 0) { if (speed == 0) {
phy->autoneg = AUTONEG_ENABLE; phy->autoneg = AUTONEG_ENABLE;
phy->speed = 0; phy->speed = 0;
......
...@@ -320,23 +320,6 @@ static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int phy_addr) ...@@ -320,23 +320,6 @@ static void ethernet_phy_set_addr(struct pxa168_eth_private *pep, int phy_addr)
wrl(pep, PHY_ADDRESS, reg_data); 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) static void rxq_refill(struct net_device *dev)
{ {
struct pxa168_eth_private *pep = netdev_priv(dev); struct pxa168_eth_private *pep = netdev_priv(dev);
...@@ -645,7 +628,7 @@ static void eth_port_start(struct net_device *dev) ...@@ -645,7 +628,7 @@ static void eth_port_start(struct net_device *dev)
struct ethtool_cmd cmd; struct ethtool_cmd cmd;
pxa168_get_settings(pep->dev, &cmd); pxa168_get_settings(pep->dev, &cmd);
ethernet_phy_reset(pep); phy_init_hw(pep->phy);
pxa168_set_settings(pep->dev, &cmd); pxa168_set_settings(pep->dev, &cmd);
} }
...@@ -1382,7 +1365,6 @@ static struct phy_device *phy_scan(struct pxa168_eth_private *pep, int phy_addr) ...@@ -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) static void phy_init(struct pxa168_eth_private *pep, int speed, int duplex)
{ {
struct phy_device *phy = pep->phy; struct phy_device *phy = pep->phy;
ethernet_phy_reset(pep);
phy_attach(pep->dev, dev_name(&phy->dev), PHY_INTERFACE_MODE_MII); phy_attach(pep->dev, dev_name(&phy->dev), PHY_INTERFACE_MODE_MII);
......
...@@ -1704,7 +1704,10 @@ static int sh_eth_phy_start(struct net_device *ndev) ...@@ -1704,7 +1704,10 @@ static int sh_eth_phy_start(struct net_device *ndev)
return ret; return ret;
/* reset phy - this also wakes it from PDOWN */ /* 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); phy_start(mdp->phydev);
return 0; return 0;
......
...@@ -1170,19 +1170,12 @@ static int tc35815_tx_full(struct net_device *dev) ...@@ -1170,19 +1170,12 @@ static int tc35815_tx_full(struct net_device *dev)
static void tc35815_restart(struct net_device *dev) static void tc35815_restart(struct net_device *dev)
{ {
struct tc35815_local *lp = netdev_priv(dev); struct tc35815_local *lp = netdev_priv(dev);
int ret;
if (lp->phy_dev) { if (lp->phy_dev) {
int timeout; ret = phy_init_hw(lp->phy_dev);
if (ret)
phy_write(lp->phy_dev, MII_BMCR, BMCR_RESET); printk(KERN_ERR "%s: PHY init failed.\n", dev->name);
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);
} }
spin_lock_bh(&lp->rx_lock); spin_lock_bh(&lp->rx_lock);
......
...@@ -289,6 +289,7 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd) ...@@ -289,6 +289,7 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd)
cmd->supported = phydev->supported; cmd->supported = phydev->supported;
cmd->advertising = phydev->advertising; cmd->advertising = phydev->advertising;
cmd->lp_advertising = phydev->lp_advertising;
ethtool_cmd_speed_set(cmd, phydev->speed); ethtool_cmd_speed_set(cmd, phydev->speed);
cmd->duplex = phydev->duplex; cmd->duplex = phydev->duplex;
...@@ -317,6 +318,7 @@ int phy_mii_ioctl(struct phy_device *phydev, ...@@ -317,6 +318,7 @@ int phy_mii_ioctl(struct phy_device *phydev,
{ {
struct mii_ioctl_data *mii_data = if_mii(ifr); struct mii_ioctl_data *mii_data = if_mii(ifr);
u16 val = mii_data->val_in; u16 val = mii_data->val_in;
int ret = 0;
switch (cmd) { switch (cmd) {
case SIOCGMIIPHY: case SIOCGMIIPHY:
...@@ -360,11 +362,8 @@ int phy_mii_ioctl(struct phy_device *phydev, ...@@ -360,11 +362,8 @@ int phy_mii_ioctl(struct phy_device *phydev,
mii_data->reg_num, val); mii_data->reg_num, val);
if (mii_data->reg_num == MII_BMCR && if (mii_data->reg_num == MII_BMCR &&
val & BMCR_RESET && val & BMCR_RESET)
phydev->drv->config_init) { ret = phy_init_hw(phydev);
phy_scan_fixups(phydev);
phydev->drv->config_init(phydev);
}
break; break;
case SIOCSHWTSTAMP: case SIOCSHWTSTAMP:
...@@ -376,7 +375,7 @@ int phy_mii_ioctl(struct phy_device *phydev, ...@@ -376,7 +375,7 @@ int phy_mii_ioctl(struct phy_device *phydev,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
return 0; return ret;
} }
EXPORT_SYMBOL(phy_mii_ioctl); EXPORT_SYMBOL(phy_mii_ioctl);
......
...@@ -364,7 +364,11 @@ int phy_device_register(struct phy_device *phydev) ...@@ -364,7 +364,11 @@ int phy_device_register(struct phy_device *phydev)
phydev->bus->phy_map[phydev->addr] = phydev; phydev->bus->phy_map[phydev->addr] = phydev;
/* Run all of the fixups for this PHY */ /* 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); err = device_add(&phydev->dev);
if (err) { if (err) {
...@@ -497,6 +501,47 @@ void phy_disconnect(struct phy_device *phydev) ...@@ -497,6 +501,47 @@ void phy_disconnect(struct phy_device *phydev)
} }
EXPORT_SYMBOL(phy_disconnect); 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 phy_init_hw(struct phy_device *phydev)
{ {
int ret; int ret;
...@@ -504,12 +549,21 @@ int phy_init_hw(struct phy_device *phydev) ...@@ -504,12 +549,21 @@ int phy_init_hw(struct phy_device *phydev)
if (!phydev->drv || !phydev->drv->config_init) if (!phydev->drv || !phydev->drv->config_init)
return 0; 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); ret = phy_scan_fixups(phydev);
if (ret < 0) if (ret < 0)
return ret; return ret;
return phydev->drv->config_init(phydev); return phydev->drv->config_init(phydev);
} }
EXPORT_SYMBOL(phy_init_hw);
/** /**
* phy_attach_direct - attach a network device to a given PHY device pointer * 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) ...@@ -839,6 +893,8 @@ int genphy_read_status(struct phy_device *phydev)
if (err) if (err)
return err; return err;
phydev->lp_advertising = 0;
if (AUTONEG_ENABLE == phydev->autoneg) { if (AUTONEG_ENABLE == phydev->autoneg) {
if (phydev->supported & (SUPPORTED_1000baseT_Half if (phydev->supported & (SUPPORTED_1000baseT_Half
| SUPPORTED_1000baseT_Full)) { | SUPPORTED_1000baseT_Full)) {
...@@ -852,6 +908,8 @@ int genphy_read_status(struct phy_device *phydev) ...@@ -852,6 +908,8 @@ int genphy_read_status(struct phy_device *phydev)
if (adv < 0) if (adv < 0)
return adv; return adv;
phydev->lp_advertising =
mii_stat1000_to_ethtool_lpa_t(lpagb);
lpagb &= adv << 2; lpagb &= adv << 2;
} }
...@@ -860,6 +918,8 @@ int genphy_read_status(struct phy_device *phydev) ...@@ -860,6 +918,8 @@ int genphy_read_status(struct phy_device *phydev)
if (lpa < 0) if (lpa < 0)
return lpa; return lpa;
phydev->lp_advertising |= mii_lpa_to_ethtool_lpa_t(lpa);
adv = phy_read(phydev, MII_ADVERTISE); adv = phy_read(phydev, MII_ADVERTISE);
if (adv < 0) if (adv < 0)
......
...@@ -287,8 +287,8 @@ struct phy_c45_device_ids { ...@@ -287,8 +287,8 @@ struct phy_c45_device_ids {
* adjust_state: Callback for the enet driver to respond to * adjust_state: Callback for the enet driver to respond to
* changes in the state machine. * changes in the state machine.
* *
* speed, duplex, pause, supported, advertising, and * speed, duplex, pause, supported, advertising, lp_advertising,
* autoneg are used like in mii_if_info * and autoneg are used like in mii_if_info
* *
* interrupts currently only supports enabled or disabled, * interrupts currently only supports enabled or disabled,
* but could be changed in the future to support enabling * but could be changed in the future to support enabling
...@@ -340,6 +340,7 @@ struct phy_device { ...@@ -340,6 +340,7 @@ struct phy_device {
/* See mii.h for more info */ /* See mii.h for more info */
u32 supported; u32 supported;
u32 advertising; u32 advertising;
u32 lp_advertising;
int autoneg; int autoneg;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment