]> www.infradead.org Git - users/hch/misc.git/commitdiff
net: phy: micrel: Add Fast link failure support for lan8842
authorHoratiu Vultur <horatiu.vultur@microchip.com>
Wed, 17 Sep 2025 10:46:30 +0000 (12:46 +0200)
committerJakub Kicinski <kuba@kernel.org>
Thu, 18 Sep 2025 22:50:28 +0000 (15:50 -0700)
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 <horatiu.vultur@microchip.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20250917104630.3931969-1-horatiu.vultur@microchip.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/micrel.c

index e403cbbcead5b3ba620d5b9052cbdd29cd19e8fc..af05764394f69f201c3295b0c3acf2e5f3efeef0 100644 (file)
 #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,
 }, {