From 9860118b58241169f67ba77dfeb935fcf53ce4cd Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Mar 2017 16:36:37 +0000 Subject: net: phy: move phy MMD accessors to phy-core.c Move the phy_(read|write)__mmd() helpers out of line, they will become our main MMD accessor functions, and so will be a little more complex. This complexity doesn't belong in an inline function. Also move the _indirect variants as well to keep like functionality together. Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 85 --------------------------------------------------- 1 file changed, 85 deletions(-) (limited to 'drivers/net/phy/phy.c') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 1be69d8bc909..ffc28c42e2d1 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1192,91 +1192,6 @@ void phy_mac_interrupt(struct phy_device *phydev, int new_link) } EXPORT_SYMBOL(phy_mac_interrupt); -static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, - int addr) -{ - /* Write the desired MMD Devad */ - bus->write(bus, addr, MII_MMD_CTRL, devad); - - /* Write the desired MMD register address */ - bus->write(bus, addr, MII_MMD_DATA, prtad); - - /* Select the Function : DATA with no post increment */ - bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); -} - -/** - * phy_read_mmd_indirect - reads data from the MMD registers - * @phydev: The PHY device bus - * @prtad: MMD Address - * @devad: MMD DEVAD - * - * Description: it reads data from the MMD registers (clause 22 to access to - * clause 45) of the specified phy address. - * To read these register we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Read reg 14 // Read MMD data - */ -int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) -{ - struct phy_driver *phydrv = phydev->drv; - int addr = phydev->mdio.addr; - int value = -1; - - if (!phydrv->read_mmd_indirect) { - struct mii_bus *bus = phydev->mdio.bus; - - mutex_lock(&bus->mdio_lock); - mmd_phy_indirect(bus, prtad, devad, addr); - - /* Read the content of the MMD's selected register */ - value = bus->read(bus, addr, MII_MMD_DATA); - mutex_unlock(&bus->mdio_lock); - } else { - value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr); - } - return value; -} -EXPORT_SYMBOL(phy_read_mmd_indirect); - -/** - * phy_write_mmd_indirect - writes data to the MMD registers - * @phydev: The PHY device - * @prtad: MMD Address - * @devad: MMD DEVAD - * @data: data to write in the MMD register - * - * Description: Write data from the MMD registers of the specified - * phy address. - * To write these register we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Write reg 14 // Write MMD data - */ -void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, u32 data) -{ - struct phy_driver *phydrv = phydev->drv; - int addr = phydev->mdio.addr; - - if (!phydrv->write_mmd_indirect) { - struct mii_bus *bus = phydev->mdio.bus; - - mutex_lock(&bus->mdio_lock); - mmd_phy_indirect(bus, prtad, devad, addr); - - /* Write the data into MMD's selected register */ - bus->write(bus, addr, MII_MMD_DATA, data); - mutex_unlock(&bus->mdio_lock); - } else { - phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data); - } -} -EXPORT_SYMBOL(phy_write_mmd_indirect); - /** * phy_init_eee - init and check the EEE feature * @phydev: target phy_device struct -- cgit v1.2.3 From a6d99fcd3fc4f6e71630eba8e7f4d2b3b396c4c9 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Mar 2017 16:36:53 +0000 Subject: net: phy: switch remaining users to phy_(read|write)_mmd() Switch everyone over to using phy_read_mmd() and phy_write_mmd() now that they are able to handle both Clause 22 indirect addressing and Clause 45 direct addressing methods to the MMD registers. Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/bcm-phy-lib.c | 12 ++++-------- drivers/net/phy/dp83867.c | 25 +++++++++++-------------- drivers/net/phy/intel-xway.c | 26 +++++++++++++------------- drivers/net/phy/microchip.c | 5 ++--- drivers/net/phy/phy.c | 25 ++++++++++--------------- drivers/net/phy/phy_device.c | 4 ++-- 6 files changed, 42 insertions(+), 55 deletions(-) (limited to 'drivers/net/phy/phy.c') diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c index 9656dbeb5de5..171010eb4d9c 100644 --- a/drivers/net/phy/bcm-phy-lib.c +++ b/drivers/net/phy/bcm-phy-lib.c @@ -201,8 +201,7 @@ int bcm_phy_set_eee(struct phy_device *phydev, bool enable) int val; /* Enable EEE at PHY level */ - val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, - MDIO_MMD_AN); + val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL); if (val < 0) return val; @@ -211,12 +210,10 @@ int bcm_phy_set_eee(struct phy_device *phydev, bool enable) else val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X); - phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, - MDIO_MMD_AN, (u32)val); + phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val); /* Advertise EEE */ - val = phy_read_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, - MDIO_MMD_AN); + val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV); if (val < 0) return val; @@ -225,8 +222,7 @@ int bcm_phy_set_eee(struct phy_device *phydev, bool enable) else val &= ~(MDIO_EEE_100TX | MDIO_EEE_1000T); - phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, - MDIO_MMD_AN, (u32)val); + phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val); return 0; } diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 19865530e0b1..b57f20e552ba 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -133,14 +133,14 @@ static int dp83867_config_port_mirroring(struct phy_device *phydev) (struct dp83867_private *)phydev->priv; u16 val; - val = phy_read_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR); + val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4); if (dp83867->port_mirroring == DP83867_PORT_MIRROING_EN) val |= DP83867_CFG4_PORT_MIRROR_EN; else val &= ~DP83867_CFG4_PORT_MIRROR_EN; - phy_write_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR, val); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val); return 0; } @@ -231,8 +231,7 @@ static int dp83867_config_init(struct phy_device *phydev) * register's bit 11 (marked as RESERVED). */ - bs = phy_read_mmd_indirect(phydev, DP83867_STRAP_STS1, - DP83867_DEVADDR); + bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS1); if (bs & DP83867_STRAP_STS1_RESERVED) val &= ~DP83867_PHYCR_RESERVED_MASK; @@ -243,8 +242,7 @@ static int dp83867_config_init(struct phy_device *phydev) if ((phydev->interface >= PHY_INTERFACE_MODE_RGMII_ID) && (phydev->interface <= PHY_INTERFACE_MODE_RGMII_RXID)) { - val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR); + val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL); if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) val |= (DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN); @@ -255,25 +253,24 @@ static int dp83867_config_init(struct phy_device *phydev) if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) val |= DP83867_RGMII_RX_CLK_DELAY_EN; - phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, val); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, val); delay = (dp83867->rx_id_delay | (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT)); - phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL, - DP83867_DEVADDR, delay); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIIDCTL, + delay); if (dp83867->io_impedance >= 0) { - val = phy_read_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR); + val = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_IO_MUX_CFG); val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; val |= dp83867->io_impedance & DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; - phy_write_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR, val); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_IO_MUX_CFG, val); } } diff --git a/drivers/net/phy/intel-xway.c b/drivers/net/phy/intel-xway.c index b1fd7bb0e4db..55f8c52dd2f1 100644 --- a/drivers/net/phy/intel-xway.c +++ b/drivers/net/phy/intel-xway.c @@ -166,13 +166,13 @@ static int xway_gphy_config_init(struct phy_device *phydev) /* Clear all pending interrupts */ phy_read(phydev, XWAY_MDIO_ISTAT); - phy_write_mmd_indirect(phydev, XWAY_MMD_LEDCH, MDIO_MMD_VEND2, - XWAY_MMD_LEDCH_NACS_NONE | - XWAY_MMD_LEDCH_SBF_F02HZ | - XWAY_MMD_LEDCH_FBF_F16HZ); - phy_write_mmd_indirect(phydev, XWAY_MMD_LEDCL, MDIO_MMD_VEND2, - XWAY_MMD_LEDCH_CBLINK_NONE | - XWAY_MMD_LEDCH_SCAN_NONE); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCH, + XWAY_MMD_LEDCH_NACS_NONE | + XWAY_MMD_LEDCH_SBF_F02HZ | + XWAY_MMD_LEDCH_FBF_F16HZ); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCL, + XWAY_MMD_LEDCH_CBLINK_NONE | + XWAY_MMD_LEDCH_SCAN_NONE); /** * In most cases only one LED is connected to this phy, so @@ -183,12 +183,12 @@ static int xway_gphy_config_init(struct phy_device *phydev) ledxh = XWAY_MMD_LEDxH_BLINKF_NONE | XWAY_MMD_LEDxH_CON_LINK10XX; ledxl = XWAY_MMD_LEDxL_PULSE_TXACT | XWAY_MMD_LEDxL_PULSE_RXACT | XWAY_MMD_LEDxL_BLINKS_NONE; - phy_write_mmd_indirect(phydev, XWAY_MMD_LED0H, MDIO_MMD_VEND2, ledxh); - phy_write_mmd_indirect(phydev, XWAY_MMD_LED0L, MDIO_MMD_VEND2, ledxl); - phy_write_mmd_indirect(phydev, XWAY_MMD_LED1H, MDIO_MMD_VEND2, ledxh); - phy_write_mmd_indirect(phydev, XWAY_MMD_LED1L, MDIO_MMD_VEND2, ledxl); - phy_write_mmd_indirect(phydev, XWAY_MMD_LED2H, MDIO_MMD_VEND2, ledxh); - phy_write_mmd_indirect(phydev, XWAY_MMD_LED2L, MDIO_MMD_VEND2, ledxl); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0H, ledxh); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0L, ledxl); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1H, ledxh); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1L, ledxl); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, ledxh); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, ledxl); return 0; } diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c index 324fbf6ad8ff..2b2f543cf9f0 100644 --- a/drivers/net/phy/microchip.c +++ b/drivers/net/phy/microchip.c @@ -78,9 +78,8 @@ static int lan88xx_probe(struct phy_device *phydev) priv->wolopts = 0; /* these values can be used to identify internal PHY */ - priv->chip_id = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_ID, 3); - priv->chip_rev = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_REV, - 3); + priv->chip_id = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_ID); + priv->chip_rev = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_REV); phydev->priv = priv; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index ffc28c42e2d1..ba4676ee9018 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1227,8 +1227,7 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) return status; /* First check if the EEE ability is supported */ - eee_cap = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, - MDIO_MMD_PCS); + eee_cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); if (eee_cap <= 0) goto eee_exit_err; @@ -1239,13 +1238,11 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) /* Check which link settings negotiated and verify it in * the EEE advertising registers. */ - eee_lp = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE, - MDIO_MMD_AN); + eee_lp = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); if (eee_lp <= 0) goto eee_exit_err; - eee_adv = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, - MDIO_MMD_AN); + eee_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); if (eee_adv <= 0) goto eee_exit_err; @@ -1258,14 +1255,12 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) /* Configure the PHY to stop receiving xMII * clock while it is signaling LPI. */ - int val = phy_read_mmd_indirect(phydev, MDIO_CTRL1, - MDIO_MMD_PCS); + int val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); if (val < 0) return val; val |= MDIO_PCS_CTRL1_CLKSTOP_EN; - phy_write_mmd_indirect(phydev, MDIO_CTRL1, - MDIO_MMD_PCS, val); + phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, val); } return 0; /* EEE supported */ @@ -1287,7 +1282,7 @@ int phy_get_eee_err(struct phy_device *phydev) if (!phydev->drv) return -EIO; - return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR, MDIO_MMD_PCS); + return phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_WK_ERR); } EXPORT_SYMBOL(phy_get_eee_err); @@ -1307,19 +1302,19 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data) return -EIO; /* Get Supported EEE */ - val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, MDIO_MMD_PCS); + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); if (val < 0) return val; data->supported = mmd_eee_cap_to_ethtool_sup_t(val); /* Get advertisement EEE */ - val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN); + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); if (val < 0) return val; data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); /* Get LP advertisement EEE */ - val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE, MDIO_MMD_AN); + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); if (val < 0) return val; data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); @@ -1345,7 +1340,7 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) /* Mask prohibited EEE modes */ val &= ~phydev->eee_broken_modes; - phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, val); + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val); return 0; } diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 5198ccfa347f..1219eeab69d1 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1217,7 +1217,7 @@ static int genphy_config_eee_advert(struct phy_device *phydev) * supported by the phy. If we read 0, EEE is not advertised * In both case, we don't need to continue */ - adv = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN); + adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); if (adv <= 0) return 0; @@ -1228,7 +1228,7 @@ static int genphy_config_eee_advert(struct phy_device *phydev) if (old_adv == adv) return 0; - phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, adv); + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); return 1; } -- cgit v1.2.3 From 83ea067fe2eae9a67c172aede6e11b9f194b1d52 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 31 Mar 2017 10:37:07 +0100 Subject: net: phy: avoid setting unsupported EEE advertisments We currently allow userspace to set any EEE advertisments it desires, whether or not the PHY supports them. For example: # ethtool --set-eee eth1 advertise 0xffffffff # ethtool --show-eee eth1 EEE Settings for eth1: EEE status: disabled Tx LPI: disabled Supported EEE link modes: 100baseT/Full 1000baseT/Full 10000baseT/Full Advertised EEE link modes: 100baseT/Full 1000baseT/Full 1000baseKX/Full 10000baseT/Full 10000baseKX4/Full 10000baseKR/Full Clearly, this is not sane, we should only allow link modes that are supported to be advertised (as we do elsewhere.) Ensure that we mask the MDIO_AN_EEE_ADV value with the capabilities retrieved from the MDIO_PCS_EEE_ABLE register. Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/net/phy/phy.c') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index ba4676ee9018..7b1c93b0233a 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1332,17 +1332,22 @@ EXPORT_SYMBOL(phy_ethtool_get_eee); */ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) { - int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised); + int cap, adv; if (!phydev->drv) return -EIO; - /* Mask prohibited EEE modes */ - val &= ~phydev->eee_broken_modes; + /* Get Supported EEE */ + cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); + if (cap < 0) + return cap; - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val); + adv = ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap; - return 0; + /* Mask prohibited EEE modes */ + adv &= ~phydev->eee_broken_modes; + + return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); } EXPORT_SYMBOL(phy_ethtool_set_eee); -- cgit v1.2.3 From f75abeb8338e2d5bcdfc393dff3950a7039eab5a Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 31 Mar 2017 10:37:12 +0100 Subject: net: phy: restart phy autonegotiation after EEE advertisment change When the EEE advertisment is changed, we should restart autonegotiation to update the link partner with the new EEE settings. Add this trigger but only if the advertisment has changed. Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers/net/phy/phy.c') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 7b1c93b0233a..345251f21699 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1332,7 +1332,7 @@ EXPORT_SYMBOL(phy_ethtool_get_eee); */ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) { - int cap, adv; + int cap, old_adv, adv, ret; if (!phydev->drv) return -EIO; @@ -1342,12 +1342,29 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) if (cap < 0) return cap; + old_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); + if (old_adv < 0) + return old_adv; + adv = ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap; /* Mask prohibited EEE modes */ adv &= ~phydev->eee_broken_modes; - return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); + if (old_adv != adv) { + ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); + if (ret < 0) + return ret; + + /* Restart autonegotiation so the new modes get sent to the + * link partner. + */ + ret = genphy_restart_aneg(phydev); + if (ret < 0) + return ret; + } + + return 0; } EXPORT_SYMBOL(phy_ethtool_set_eee); -- cgit v1.2.3 From 32d751412b8baf977deb3b2dce72025a98cbdd5e Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 31 Mar 2017 10:37:18 +0100 Subject: net: phy: allow EEE with any interface mode EEE is able to work in any PHY interface mode, there is nothing which fundamentally restricts it to only a few modes. For example, EEE works in SGMII mode with the Marvell 88E1512. Rather than just adding SGMII mode to the list, Florian suggests removing the list of interface modes entirely: It actually sounds like we should just kill the check entirely, it does not appear that any of the interface mode would not fundamentally be able to support EEE, because the "lowest" mode we support is MII, and even there it's quite possible to support EEE. Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers/net/phy/phy.c') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 345251f21699..867c42154087 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1208,15 +1208,8 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) return -EIO; /* According to 802.3az,the EEE is supported only in full duplex-mode. - * Also EEE feature is active when core is operating with MII, GMII - * or RGMII (all kinds). Internal PHYs are also allowed to proceed and - * should return an error if they do not support EEE. */ - if ((phydev->duplex == DUPLEX_FULL) && - ((phydev->interface == PHY_INTERFACE_MODE_MII) || - (phydev->interface == PHY_INTERFACE_MODE_GMII) || - phy_interface_is_rgmii(phydev) || - phy_is_internal(phydev))) { + if (phydev->duplex == DUPLEX_FULL) { int eee_lp, eee_cap, eee_adv; u32 lp, cap, adv; int status; -- cgit v1.2.3 From 1f37b177fd36790be4f281d538a8c9de67013606 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 2 Apr 2017 14:30:06 -0700 Subject: phy/ethtool: Add missing SPEED_ strings Add all the currently available SPEED_ strings. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 14 ++++++++++++++ include/uapi/linux/ethtool.h | 1 + 2 files changed, 15 insertions(+) (limited to 'drivers/net/phy/phy.c') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 867c42154087..6811d1ef4ef2 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -50,8 +50,22 @@ static const char *phy_speed_to_str(int speed) return "1Gbps"; case SPEED_2500: return "2.5Gbps"; + case SPEED_5000: + return "5Gbps"; case SPEED_10000: return "10Gbps"; + case SPEED_20000: + return "20Gbps"; + case SPEED_25000: + return "25Gbps"; + case SPEED_40000: + return "40Gbps"; + case SPEED_50000: + return "50Gbps"; + case SPEED_56000: + return "56Gbps"; + case SPEED_100000: + return "100Gbps"; case SPEED_UNKNOWN: return "Unknown"; default: diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 3dc91a46e8b8..5f4ea28eabe4 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -1487,6 +1487,7 @@ enum ethtool_link_mode_bit_indices { */ /* The forced speed, in units of 1Mb. All values 0 to INT_MAX are legal. */ +/* Update drivers/net/phy/phy.c:phy_speed_to_str() when adding new values */ #define SPEED_10 10 #define SPEED_100 100 #define SPEED_1000 1000 -- cgit v1.2.3 From d06130377c4826624681505c0bb45bfd6eb7cd4f Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 13 Apr 2017 16:49:15 +0100 Subject: net: phy: improve phylib correctness for non-autoneg settings phylib has some undesirable behaviour when forcing a link mode through ethtool. phylib uses this code: idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex), features); to find an index in the settings table. phy_find_setting() starts at index 0, and scans upwards looking for an exact speed and duplex match. When it doesn't find it, it returns MAX_NUM_SETTINGS - 1, which is 10baseT-Half duplex. phy_find_valid() then scans from the point (and effectively only checks one entry) before bailing out, returning MAX_NUM_SETTINGS - 1. phy_sanitize_settings() then sets ->speed to SPEED_10 and ->duplex to DUPLEX_HALF whether or not 10baseT-Half is supported or not. This goes against all the comments against these functions, and 10baseT-Half may not even be supported by the hardware. Rework these functions, introducing a new method of scanning the table. There are two modes of lookup that phylib wants: exact, and inexact. - in exact mode, we return either an exact match or failure - in inexact mode, we return an exact match if it exists, a match at the highest speed that is not greater than the requested speed (ignoring duplex), or failing that, the lowest supported speed, or failure. The biggest difference is that we always check whether the entry is supported before further consideration, so all unsupported entries are not considered as candidates. This results in arguably saner behaviour, better matches the comments, and is probably what users would expect. This becomes important as ethernet speeds increase, PHYs exist which do not support the 10Mbit speeds, and half-duplex is likely to become obsolete - it's already not even an option on 10Gbit and faster links. Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 109 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 66 insertions(+), 43 deletions(-) (limited to 'drivers/net/phy/phy.c') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index bf7d614ff18f..00280d4eeb56 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -176,7 +176,9 @@ struct phy_setting { u32 setting; }; -/* A mapping of all SUPPORTED settings to speed/duplex */ +/* A mapping of all SUPPORTED settings to speed/duplex. This table + * must be grouped by speed and sorted in descending match priority + * - iow, descending speed. */ static const struct phy_setting settings[] = { { .speed = SPEED_10000, @@ -235,45 +237,70 @@ static const struct phy_setting settings[] = { }, }; -#define MAX_NUM_SETTINGS ARRAY_SIZE(settings) - /** - * phy_find_setting - find a PHY settings array entry that matches speed & duplex + * phy_lookup_setting - lookup a PHY setting * @speed: speed to match * @duplex: duplex to match + * @feature: allowed link modes + * @exact: an exact match is required + * + * Search the settings array for a setting that matches the speed and + * duplex, and which is supported. + * + * If @exact is unset, either an exact match or %NULL for no match will + * be returned. * - * Description: Searches the settings array for the setting which - * matches the desired speed and duplex, and returns the index - * of that setting. Returns the index of the last setting if - * none of the others match. + * If @exact is set, an exact match, the fastest supported setting at + * or below the specified speed, the slowest supported setting, or if + * they all fail, %NULL will be returned. */ -static inline unsigned int phy_find_setting(int speed, int duplex) +static const struct phy_setting * +phy_lookup_setting(int speed, int duplex, u32 features, bool exact) { - unsigned int idx = 0; + const struct phy_setting *p, *match = NULL, *last = NULL; + int i; + + for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) { + if (p->setting & features) { + last = p; + if (p->speed == speed && p->duplex == duplex) { + /* Exact match for speed and duplex */ + match = p; + break; + } else if (!exact) { + if (!match && p->speed <= speed) + /* Candidate */ + match = p; + + if (p->speed < speed) + break; + } + } + } - while (idx < ARRAY_SIZE(settings) && - (settings[idx].speed != speed || settings[idx].duplex != duplex)) - idx++; + if (!match && !exact) + match = last; - return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; + return match; } /** - * phy_find_valid - find a PHY setting that matches the requested features mask - * @idx: The first index in settings[] to search - * @features: A mask of the valid settings + * phy_find_valid - find a PHY setting that matches the requested parameters + * @speed: desired speed + * @duplex: desired duplex + * @supported: mask of supported link modes * - * Description: Returns the index of the first valid setting less - * than or equal to the one pointed to by idx, as determined by - * the mask in features. Returns the index of the last setting - * if nothing else matches. + * Locate a supported phy setting that is, in priority order: + * - an exact match for the specified speed and duplex mode + * - a match for the specified speed, or slower speed + * - the slowest supported speed + * Returns the matched phy_setting entry, or %NULL if no supported phy + * settings were found. */ -static inline unsigned int phy_find_valid(unsigned int idx, u32 features) +static const struct phy_setting * +phy_find_valid(int speed, int duplex, u32 supported) { - while (idx < MAX_NUM_SETTINGS && !(settings[idx].setting & features)) - idx++; - - return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; + return phy_lookup_setting(speed, duplex, supported, false); } /** @@ -293,11 +320,9 @@ unsigned int phy_supported_speeds(struct phy_device *phy, unsigned int count = 0; unsigned int idx = 0; - while (idx < MAX_NUM_SETTINGS && count < size) { - idx = phy_find_valid(idx, phy->supported); - + for (idx = 0; idx < ARRAY_SIZE(settings) && count < size; idx++) { if (!(settings[idx].setting & phy->supported)) - break; + continue; /* Assumes settings are grouped by speed */ if ((count == 0) || @@ -305,7 +330,6 @@ unsigned int phy_supported_speeds(struct phy_device *phy, speeds[count] = settings[idx].speed; count++; } - idx++; } return count; @@ -322,12 +346,7 @@ unsigned int phy_supported_speeds(struct phy_device *phy, */ static inline bool phy_check_valid(int speed, int duplex, u32 features) { - unsigned int idx; - - idx = phy_find_valid(phy_find_setting(speed, duplex), features); - - return settings[idx].speed == speed && settings[idx].duplex == duplex && - (settings[idx].setting & features); + return !!phy_lookup_setting(speed, duplex, features, true); } /** @@ -340,18 +359,22 @@ static inline bool phy_check_valid(int speed, int duplex, u32 features) */ static void phy_sanitize_settings(struct phy_device *phydev) { + const struct phy_setting *setting; u32 features = phydev->supported; - unsigned int idx; /* Sanitize settings based on PHY capabilities */ if ((features & SUPPORTED_Autoneg) == 0) phydev->autoneg = AUTONEG_DISABLE; - idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex), - features); - - phydev->speed = settings[idx].speed; - phydev->duplex = settings[idx].duplex; + setting = phy_find_valid(phydev->speed, phydev->duplex, features); + if (setting) { + phydev->speed = setting->speed; + phydev->duplex = setting->duplex; + } else { + /* We failed to find anything (no supported speeds?) */ + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + } } /** -- cgit v1.2.3 From 786df9c2a4216e64e5b7c321405d706036a76ba3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 13 Apr 2017 16:49:20 +0100 Subject: net: phy: simplify phy_supported_speeds() Simplify the loop in phy_supported_speeds(). Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers/net/phy/phy.c') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 00280d4eeb56..6afbd973a779 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -320,17 +320,11 @@ unsigned int phy_supported_speeds(struct phy_device *phy, unsigned int count = 0; unsigned int idx = 0; - for (idx = 0; idx < ARRAY_SIZE(settings) && count < size; idx++) { - if (!(settings[idx].setting & phy->supported)) - continue; - + for (idx = 0; idx < ARRAY_SIZE(settings) && count < size; idx++) /* Assumes settings are grouped by speed */ - if ((count == 0) || - (speeds[count - 1] != settings[idx].speed)) { - speeds[count] = settings[idx].speed; - count++; - } - } + if ((settings[idx].setting & phy->supported) && + (count == 0 || speeds[count - 1] != settings[idx].speed)) + speeds[count++] = settings[idx].speed; return count; } -- cgit v1.2.3