Add support for fast link failure for lan8842, when this is enabled the PHY will detect link down immediately (~1ms). The disadvantage of this is that also small instability might be reported as link down. Therefore add this feature as a tunable configuration and the user will know when to enable or not. By default it is not enabled. Signed-off-by: Horatiu Vultur --- v2->v3: - improve comment for LAN8814_PAGE_PCS by saying explict what it contains v1->v2: - fix typo appaers->appears - simplify lan8842_set_fast_down as suggested by Russell --- drivers/net/phy/micrel.c | 79 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index e403cbbcead5b..af05764394f69 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -107,6 +107,7 @@ #define LAN8814_INTC 0x18 #define LAN8814_INTS 0x1B +#define LAN8814_INT_FLF BIT(15) #define LAN8814_INT_LINK_DOWN BIT(2) #define LAN8814_INT_LINK_UP BIT(0) #define LAN8814_INT_LINK (LAN8814_INT_LINK_UP |\ @@ -2805,6 +2806,14 @@ static int ksz886x_cable_test_get_status(struct phy_device *phydev, return ret; } +/** + * LAN8814_PAGE_PCS - Selects Extended Page 0. + * + * This page contains timers used for auto-negotiation, debug registers and + * register to configure fast link failure. + */ +#define LAN8814_PAGE_PCS 0 + /** * LAN8814_PAGE_AFE_PMA - Selects Extended Page 1. * @@ -5910,7 +5919,8 @@ static int lan8842_config_intr(struct phy_device *phydev) if (err) return err; - err = phy_write(phydev, LAN8814_INTC, LAN8814_INT_LINK); + err = phy_write(phydev, LAN8814_INTC, + LAN8814_INT_LINK | LAN8814_INT_FLF); } else { err = phy_write(phydev, LAN8814_INTC, 0); if (err) @@ -5986,7 +5996,7 @@ static irqreturn_t lan8842_handle_interrupt(struct phy_device *phydev) return IRQ_NONE; } - if (irq_status & LAN8814_INT_LINK) { + if (irq_status & (LAN8814_INT_LINK | LAN8814_INT_FLF)) { phy_trigger_machine(phydev); ret = IRQ_HANDLED; } @@ -6055,6 +6065,69 @@ static int lan8842_update_stats(struct phy_device *phydev) return 0; } +#define LAN8842_FLF 15 /* 0x0e */ +#define LAN8842_FLF_ENA BIT(1) +#define LAN8842_FLF_ENA_LINK_DOWN BIT(0) + +static int lan8842_get_fast_down(struct phy_device *phydev, u8 *msecs) +{ + int ret; + + ret = lanphy_read_page_reg(phydev, LAN8814_PAGE_PCS, LAN8842_FLF); + if (ret < 0) + return ret; + + if (ret & LAN8842_FLF_ENA) + *msecs = ETHTOOL_PHY_FAST_LINK_DOWN_ON; + else + *msecs = ETHTOOL_PHY_FAST_LINK_DOWN_OFF; + + return 0; +} + +static int lan8842_set_fast_down(struct phy_device *phydev, const u8 *msecs) +{ + u16 flf; + + switch (*msecs) { + case ETHTOOL_PHY_FAST_LINK_DOWN_OFF: + flf = 0; + break; + case ETHTOOL_PHY_FAST_LINK_DOWN_ON: + flf = LAN8842_FLF_ENA | LAN8842_FLF_ENA_LINK_DOWN; + break; + default: + return -EINVAL; + } + + return lanphy_modify_page_reg(phydev, LAN8814_PAGE_PCS, + LAN8842_FLF, + LAN8842_FLF_ENA | + LAN8842_FLF_ENA_LINK_DOWN, flf); +} + +static int lan8842_get_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, void *data) +{ + switch (tuna->id) { + case ETHTOOL_PHY_FAST_LINK_DOWN: + return lan8842_get_fast_down(phydev, data); + default: + return -EOPNOTSUPP; + } +} + +static int lan8842_set_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, const void *data) +{ + switch (tuna->id) { + case ETHTOOL_PHY_FAST_LINK_DOWN: + return lan8842_set_fast_down(phydev, data); + default: + return -EOPNOTSUPP; + } +} + static void lan8842_get_phy_stats(struct phy_device *phydev, struct ethtool_eth_phy_stats *eth_stats, struct ethtool_phy_stats *stats) @@ -6299,6 +6372,8 @@ static struct phy_driver ksphy_driver[] = { .handle_interrupt = lan8842_handle_interrupt, .get_phy_stats = lan8842_get_phy_stats, .update_stats = lan8842_update_stats, + .get_tunable = lan8842_get_tunable, + .set_tunable = lan8842_set_tunable, .cable_test_start = lan8814_cable_test_start, .cable_test_get_status = ksz886x_cable_test_get_status, }, { -- 2.34.1