diff options
Diffstat (limited to 'drivers/net/phy/phy_device.c')
-rw-r--r-- | drivers/net/phy/phy_device.c | 208 |
1 files changed, 149 insertions, 59 deletions
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 77068c545de0..dcc93a873174 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -225,7 +225,7 @@ static void phy_mdio_device_remove(struct mdio_device *mdiodev) } static struct phy_driver genphy_driver; -extern struct phy_driver genphy_10g_driver; +extern struct phy_driver genphy_c45_driver; static LIST_HEAD(phy_fixup_list); static DEFINE_MUTEX(phy_fixup_lock); @@ -1174,7 +1174,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, */ if (!d->driver) { if (phydev->is_c45) - d->driver = &genphy_10g_driver.mdiodrv.driver; + d->driver = &genphy_c45_driver.mdiodrv.driver; else d->driver = &genphy_driver.mdiodrv.driver; @@ -1335,7 +1335,7 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy); bool phy_driver_is_genphy_10g(struct phy_device *phydev) { return phy_driver_is_genphy_kind(phydev, - &genphy_10g_driver.mdiodrv.driver); + &genphy_c45_driver.mdiodrv.driver); } EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g); @@ -1710,23 +1710,19 @@ int genphy_update_link(struct phy_device *phydev) */ if (!phy_polling_mode(phydev)) { status = phy_read(phydev, MII_BMSR); - if (status < 0) { + if (status < 0) return status; - } else if (status & BMSR_LSTATUS) { - phydev->link = 1; - return 0; - } + else if (status & BMSR_LSTATUS) + goto done; } /* Read link and autonegotiation status */ status = phy_read(phydev, MII_BMSR); if (status < 0) return status; - - if ((status & BMSR_LSTATUS) == 0) - phydev->link = 0; - else - phydev->link = 1; +done: + phydev->link = status & BMSR_LSTATUS ? 1 : 0; + phydev->autoneg_complete = status & BMSR_ANEGCOMPLETE ? 1 : 0; return 0; } @@ -1743,23 +1739,26 @@ EXPORT_SYMBOL(genphy_update_link); */ int genphy_read_status(struct phy_device *phydev) { - int adv; - int err; - int lpa; - int lpagb = 0; + int adv, lpa, lpagb, err, old_link = phydev->link; /* Update the link, but return if there was an error */ err = genphy_update_link(phydev); if (err) return err; + /* why bother the PHY if nothing can have changed */ + if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) + return 0; + + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; + linkmode_zero(phydev->lp_advertising); - if (AUTONEG_ENABLE == phydev->autoneg) { - if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, - phydev->supported) || - linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, - phydev->supported)) { + if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { + if (phydev->is_gigabit_capable) { lpagb = phy_read(phydev, MII_STAT1000); if (lpagb < 0) return lpagb; @@ -1785,14 +1784,8 @@ int genphy_read_status(struct phy_device *phydev) return lpa; mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa); - - phydev->speed = SPEED_UNKNOWN; - phydev->duplex = DUPLEX_UNKNOWN; - phydev->pause = 0; - phydev->asym_pause = 0; - phy_resolve_aneg_linkmode(phydev); - } else { + } else if (phydev->autoneg == AUTONEG_DISABLE) { int bmcr = phy_read(phydev, MII_BMCR); if (bmcr < 0) @@ -1809,9 +1802,6 @@ int genphy_read_status(struct phy_device *phydev) phydev->speed = SPEED_100; else phydev->speed = SPEED_10; - - phydev->pause = 0; - phydev->asym_pause = 0; } return 0; @@ -1829,13 +1819,25 @@ EXPORT_SYMBOL(genphy_read_status); */ int genphy_soft_reset(struct phy_device *phydev) { + u16 res = BMCR_RESET; int ret; - ret = phy_set_bits(phydev, MII_BMCR, BMCR_RESET); + if (phydev->autoneg == AUTONEG_ENABLE) + res |= BMCR_ANRESTART; + + ret = phy_modify(phydev, MII_BMCR, BMCR_ISOLATE, res); if (ret < 0) return ret; - return phy_poll_reset(phydev); + ret = phy_poll_reset(phydev); + if (ret) + return ret; + + /* BMCR may be reset to defaults */ + if (phydev->autoneg == AUTONEG_DISABLE) + ret = genphy_setup_forced(phydev); + + return ret; } EXPORT_SYMBOL(genphy_soft_reset); @@ -1887,6 +1889,54 @@ int genphy_config_init(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_config_init); +/** + * genphy_read_abilities - read PHY abilities from Clause 22 registers + * @phydev: target phy_device struct + * + * Description: Reads the PHY's abilities and populates + * phydev->supported accordingly. + * + * Returns: 0 on success, < 0 on failure + */ +int genphy_read_abilities(struct phy_device *phydev) +{ + int val; + + linkmode_set_bit_array(phy_basic_ports_array, + ARRAY_SIZE(phy_basic_ports_array), + phydev->supported); + + val = phy_read(phydev, MII_BMSR); + if (val < 0) + return val; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported, + val & BMSR_ANEGCAPABLE); + + linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported, + val & BMSR_100FULL); + linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported, + val & BMSR_100HALF); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported, + val & BMSR_10FULL); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported, + val & BMSR_10HALF); + + if (val & BMSR_ESTATEN) { + val = phy_read(phydev, MII_ESTATUS); + if (val < 0) + return val; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + phydev->supported, val & ESTATUS_1000_TFULL); + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, + phydev->supported, val & ESTATUS_1000_THALF); + } + + return 0; +} +EXPORT_SYMBOL(genphy_read_abilities); + /* This is used for the phy device which doesn't support the MMD extended * register access, but it does have side effect when we are trying to access * the MMD register via indirect method. @@ -1935,10 +1985,35 @@ EXPORT_SYMBOL(genphy_loopback); void phy_remove_link_mode(struct phy_device *phydev, u32 link_mode) { linkmode_clear_bit(link_mode, phydev->supported); - linkmode_copy(phydev->advertising, phydev->supported); + phy_advertise_supported(phydev); } EXPORT_SYMBOL(phy_remove_link_mode); +static void phy_copy_pause_bits(unsigned long *dst, unsigned long *src) +{ + linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, dst, + linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, src)); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, dst, + linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, src)); +} + +/** + * phy_advertise_supported - Advertise all supported modes + * @phydev: target phy_device struct + * + * Description: Called to advertise all supported modes, doesn't touch + * pause mode advertising. + */ +void phy_advertise_supported(struct phy_device *phydev) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(new); + + linkmode_copy(new, phydev->supported); + phy_copy_pause_bits(new, phydev->advertising); + linkmode_copy(phydev->advertising, new); +} +EXPORT_SYMBOL(phy_advertise_supported); + /** * phy_support_sym_pause - Enable support of symmetrical pause * @phydev: target phy_device struct @@ -1949,8 +2024,7 @@ EXPORT_SYMBOL(phy_remove_link_mode); void phy_support_sym_pause(struct phy_device *phydev) { linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported); - linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported); - linkmode_copy(phydev->advertising, phydev->supported); + phy_copy_pause_bits(phydev->advertising, phydev->supported); } EXPORT_SYMBOL(phy_support_sym_pause); @@ -1962,9 +2036,7 @@ EXPORT_SYMBOL(phy_support_sym_pause); */ void phy_support_asym_pause(struct phy_device *phydev) { - linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported); - linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported); - linkmode_copy(phydev->advertising, phydev->supported); + phy_copy_pause_bits(phydev->advertising, phydev->supported); } EXPORT_SYMBOL(phy_support_asym_pause); @@ -2044,11 +2116,14 @@ bool phy_validate_pause(struct phy_device *phydev, struct ethtool_pauseparam *pp) { if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, - phydev->supported) || - (!linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, - phydev->supported) && - pp->rx_pause != pp->tx_pause)) + phydev->supported) && pp->rx_pause) + return false; + + if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + phydev->supported) && + pp->rx_pause != pp->tx_pause) return false; + return true; } EXPORT_SYMBOL(phy_validate_pause); @@ -2104,14 +2179,30 @@ static int phy_probe(struct device *dev) */ if (phydrv->features) { linkmode_copy(phydev->supported, phydrv->features); - } else { + } else if (phydrv->get_features) { err = phydrv->get_features(phydev); - if (err) - goto out; + } else if (phydev->is_c45) { + err = genphy_c45_pma_read_abilities(phydev); + } else { + err = genphy_read_abilities(phydev); } + if (err) + goto out; + + if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + phydev->supported)) + phydev->autoneg = 0; + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, + phydev->supported)) + phydev->is_gigabit_capable = 1; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + phydev->supported)) + phydev->is_gigabit_capable = 1; + of_set_phy_supported(phydev); - linkmode_copy(phydev->advertising, phydev->supported); + phy_advertise_supported(phydev); /* Get the EEE modes we want to prohibit. We will ask * the PHY stop advertising these mode later on @@ -2177,11 +2268,11 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner) int retval; /* Either the features are hard coded, or dynamically - * determine. It cannot be both or neither + * determined. It cannot be both. */ - if (WARN_ON((!new_driver->features && !new_driver->get_features) || - (new_driver->features && new_driver->get_features))) { - pr_err("%s: Driver features are missing\n", new_driver->name); + if (WARN_ON(new_driver->features && new_driver->get_features)) { + pr_err("%s: features and get_features must not both be set\n", + new_driver->name); return -EINVAL; } @@ -2243,8 +2334,7 @@ static struct phy_driver genphy_driver = { .phy_id_mask = 0xffffffff, .name = "Generic PHY", .soft_reset = genphy_no_soft_reset, - .config_init = genphy_config_init, - .features = PHY_GBIT_ALL_PORTS_FEATURES, + .get_features = genphy_read_abilities, .aneg_done = genphy_aneg_done, .suspend = genphy_suspend, .resume = genphy_resume, @@ -2261,14 +2351,14 @@ static int __init phy_init(void) features_init(); - rc = phy_driver_register(&genphy_10g_driver, THIS_MODULE); + rc = phy_driver_register(&genphy_c45_driver, THIS_MODULE); if (rc) - goto err_10g; + goto err_c45; rc = phy_driver_register(&genphy_driver, THIS_MODULE); if (rc) { - phy_driver_unregister(&genphy_10g_driver); -err_10g: + phy_driver_unregister(&genphy_c45_driver); +err_c45: mdio_bus_exit(); } @@ -2277,7 +2367,7 @@ err_10g: static void __exit phy_exit(void) { - phy_driver_unregister(&genphy_10g_driver); + phy_driver_unregister(&genphy_c45_driver); phy_driver_unregister(&genphy_driver); mdio_bus_exit(); } |