diff options
Diffstat (limited to 'drivers/net/phy')
| -rw-r--r-- | drivers/net/phy/mdio_bus.c | 3 | ||||
| -rw-r--r-- | drivers/net/phy/microchip_t1.c | 44 | ||||
| -rw-r--r-- | drivers/net/phy/phy.c | 7 | ||||
| -rw-r--r-- | drivers/net/phy/phylink.c | 27 |
4 files changed, 78 insertions, 3 deletions
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index c204067f1890..c198722e4871 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -460,6 +460,9 @@ static void of_mdiobus_link_mdiodev(struct mii_bus *bus, if (addr == mdiodev->addr) { device_set_node(dev, of_fwnode_handle(child)); + /* The refcount on "child" is passed to the mdio + * device. Do _not_ use of_node_put(child) here. + */ return; } } diff --git a/drivers/net/phy/microchip_t1.c b/drivers/net/phy/microchip_t1.c index a4de3d2081c5..bc50224d43dd 100644 --- a/drivers/net/phy/microchip_t1.c +++ b/drivers/net/phy/microchip_t1.c @@ -28,6 +28,11 @@ #define LAN87XX_MASK_LINK_UP (0x0004) #define LAN87XX_MASK_LINK_DOWN (0x0002) +/* MISC Control 1 Register */ +#define LAN87XX_CTRL_1 (0x11) +#define LAN87XX_MASK_RGMII_TXC_DLY_EN (0x4000) +#define LAN87XX_MASK_RGMII_RXC_DLY_EN (0x2000) + /* phyaccess nested types */ #define PHYACC_ATTR_MODE_READ 0 #define PHYACC_ATTR_MODE_WRITE 1 @@ -112,6 +117,43 @@ static int access_ereg_modify_changed(struct phy_device *phydev, return rc; } +static int lan87xx_config_rgmii_delay(struct phy_device *phydev) +{ + int rc; + + if (!phy_interface_is_rgmii(phydev)) + return 0; + + rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, + PHYACC_ATTR_BANK_MISC, LAN87XX_CTRL_1, 0); + if (rc < 0) + return rc; + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + rc &= ~LAN87XX_MASK_RGMII_TXC_DLY_EN; + rc &= ~LAN87XX_MASK_RGMII_RXC_DLY_EN; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + rc |= LAN87XX_MASK_RGMII_TXC_DLY_EN; + rc |= LAN87XX_MASK_RGMII_RXC_DLY_EN; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + rc &= ~LAN87XX_MASK_RGMII_TXC_DLY_EN; + rc |= LAN87XX_MASK_RGMII_RXC_DLY_EN; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + rc |= LAN87XX_MASK_RGMII_TXC_DLY_EN; + rc &= ~LAN87XX_MASK_RGMII_RXC_DLY_EN; + break; + default: + return 0; + } + + return access_ereg(phydev, PHYACC_ATTR_MODE_WRITE, + PHYACC_ATTR_BANK_MISC, LAN87XX_CTRL_1, rc); +} + static int lan87xx_phy_init(struct phy_device *phydev) { static const struct access_ereg_val init[] = { @@ -185,7 +227,7 @@ static int lan87xx_phy_init(struct phy_device *phydev) return rc; } - return 0; + return lan87xx_config_rgmii_delay(phydev); } static int lan87xx_phy_config_intr(struct phy_device *phydev) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index a3bfb156c83d..beb2b66da132 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -815,7 +815,12 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev, phydev->mdix_ctrl = cmd->base.eth_tp_mdix_ctrl; /* Restart the PHY */ - _phy_start_aneg(phydev); + if (phy_is_started(phydev)) { + phydev->state = PHY_UP; + phy_trigger_machine(phydev); + } else { + _phy_start_aneg(phydev); + } mutex_unlock(&phydev->lock); return 0; diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 3ad7397b8119..ea82ea5660e7 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -710,6 +710,7 @@ static void phylink_resolve(struct work_struct *w) struct phylink_link_state link_state; struct net_device *ndev = pl->netdev; bool mac_config = false; + bool retrigger = false; bool cur_link_state; mutex_lock(&pl->state_mutex); @@ -723,6 +724,7 @@ static void phylink_resolve(struct work_struct *w) link_state.link = false; } else if (pl->mac_link_dropped) { link_state.link = false; + retrigger = true; } else { switch (pl->cur_link_an_mode) { case MLO_AN_PHY: @@ -739,6 +741,19 @@ static void phylink_resolve(struct work_struct *w) case MLO_AN_INBAND: phylink_mac_pcs_get_state(pl, &link_state); + /* The PCS may have a latching link-fail indicator. + * If the link was up, bring the link down and + * re-trigger the resolve. Otherwise, re-read the + * PCS state to get the current status of the link. + */ + if (!link_state.link) { + if (cur_link_state) + retrigger = true; + else + phylink_mac_pcs_get_state(pl, + &link_state); + } + /* If we have a phy, the "up" state is the union of * both the PHY and the MAC */ @@ -747,6 +762,15 @@ static void phylink_resolve(struct work_struct *w) /* Only update if the PHY link is up */ if (pl->phydev && pl->phy_state.link) { + /* If the interface has changed, force a + * link down event if the link isn't already + * down, and re-resolve. + */ + if (link_state.interface != + pl->phy_state.interface) { + retrigger = true; + link_state.link = false; + } link_state.interface = pl->phy_state.interface; /* If we have a PHY, we need to update with @@ -789,7 +813,7 @@ static void phylink_resolve(struct work_struct *w) else phylink_link_up(pl, link_state); } - if (!link_state.link && pl->mac_link_dropped) { + if (!link_state.link && retrigger) { pl->mac_link_dropped = false; queue_work(system_power_efficient_wq, &pl->resolve); } @@ -1364,6 +1388,7 @@ EXPORT_SYMBOL_GPL(phylink_stop); * @mac_wol: true if the MAC needs to receive packets for Wake-on-Lan * * Handle a network device suspend event. There are several cases: + * * - If Wake-on-Lan is not active, we can bring down the link between * the MAC and PHY by calling phylink_stop(). * - If Wake-on-Lan is active, and being handled only by the PHY, we |