Skip to content

Commit

Permalink
Merge branch 'phy_reset'
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
David S. Miller committed Dec 10, 2013
2 parents 7371335 + 0c9eb5b commit 65be629
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 63 deletions.
3 changes: 2 additions & 1 deletion Documentation/networking/phy.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion drivers/net/ethernet/adi/bfin_mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion drivers/net/ethernet/aeroflex/greth.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
21 changes: 1 addition & 20 deletions drivers/net/ethernet/marvell/mv643xx_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}

Expand Down Expand Up @@ -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;
Expand Down
20 changes: 1 addition & 19 deletions drivers/net/ethernet/marvell/pxa168_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);

Expand Down
5 changes: 4 additions & 1 deletion drivers/net/ethernet/renesas/sh_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
15 changes: 4 additions & 11 deletions drivers/net/ethernet/toshiba/tc35815.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 5 additions & 6 deletions drivers/net/phy/phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -376,7 +375,7 @@ int phy_mii_ioctl(struct phy_device *phydev,
return -EOPNOTSUPP;
}

return 0;
return ret;
}
EXPORT_SYMBOL(phy_mii_ioctl);

Expand Down
62 changes: 61 additions & 1 deletion drivers/net/phy/phy_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -497,19 +501,69 @@ 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;

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
Expand Down Expand Up @@ -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)) {
Expand All @@ -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;
}

Expand All @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions include/linux/phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -340,6 +340,7 @@ struct phy_device {
/* See mii.h for more info */
u32 supported;
u32 advertising;
u32 lp_advertising;

int autoneg;

Expand Down

0 comments on commit 65be629

Please sign in to comment.