]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: phy: micrel: expose KSZ87xx low-loss cable tunables
authorFidelio Lawson <lawson.fidelio@gmail.com>
Tue, 9 Jun 2026 16:19:57 +0000 (18:19 +0200)
committerJakub Kicinski <kuba@kernel.org>
Thu, 11 Jun 2026 22:47:56 +0000 (15:47 -0700)
Add support for the KSZ87xx low-loss cable PHY tunables in the Micrel
PHY driver by implementing get_tunable and set_tunable callbacks.

These callbacks expose vendor-specific PHY tunables used to control the
KSZ87xx embedded PHY receiver behavior when operating with short or
low-loss Ethernet cables. The tunables provide:

- a boolean short-cable preset applying known good settings;
- an integer LPF bandwidth control;
- an integer DSP EQ initial value control.

The Micrel PHY driver forwards these tunables via standard phy_read() /
phy_write() operations, which are virtualized by the KSZ8 DSA driver and
translated into the appropriate indirect switch register accesses.

Reviewed-by: Marek Vasut <marex@nabladev.com>
Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>
Signed-off-by: Fidelio Lawson <fidelio.lawson@exotec.com>
Link: https://patch.msgid.link/20260609-ksz87xx_errata_low_loss_connections-v10-3-9ba4418cf3db@exotec.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/micrel.c

index e211a523c2584eee3e0f9ca6d7e3537a354786aa..55df5efcfc862225dca8fc5a34f8468331488fbd 100644 (file)
 /* PHY Control 2 / PHY Control (if no PHY Control 1) */
 #define MII_KSZPHY_CTRL_2                      0x1f
 #define MII_KSZPHY_CTRL                                MII_KSZPHY_CTRL_2
+
+/* Vendor-specific Clause 22 register, virtualized by KSZ87xx embedded PHYs DSA driver */
+#define MII_KSZ87XX_SHORT_CABLE                        0x1a
+#define MII_KSZ87XX_LPF_BW                             0x1b
+#define MII_KSZ87XX_EQ_INIT                            0x1c
+
 /* bitmap of PHY register to set interrupt mode */
 #define KSZ8081_CTRL2_HP_MDIX                  BIT(15)
 #define KSZ8081_CTRL2_MDI_MDI_X_SELECT         BIT(14)
@@ -940,6 +946,59 @@ static int ksz8795_match_phy_device(struct phy_device *phydev,
        return ksz8051_ksz8795_match_phy_device(phydev, false);
 }
 
+static int ksz8795_get_tunable(struct phy_device *phydev,
+                              struct ethtool_tunable *tuna, void *data)
+{
+       int ret;
+
+       switch (tuna->id) {
+       case ETHTOOL_PHY_SHORT_CABLE_PRESET:
+               ret = phy_read(phydev, MII_KSZ87XX_SHORT_CABLE);
+               if (ret < 0)
+                       return ret;
+               *(u8 *)data = ret;
+               return 0;
+       case ETHTOOL_PHY_LPF_BW:
+               ret = phy_read(phydev, MII_KSZ87XX_LPF_BW);
+               if (ret < 0)
+                       return ret;
+               *(u32 *)data = ret & 0xff;
+               return 0;
+       case ETHTOOL_PHY_DSP_EQ_INIT_VALUE:
+               ret = phy_read(phydev, MII_KSZ87XX_EQ_INIT);
+               if (ret < 0)
+                       return ret;
+               *(u32 *)data = ret & 0xff;
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int ksz8795_set_tunable(struct phy_device *phydev,
+                              struct ethtool_tunable *tuna, const void *data)
+{
+       u32 val;
+
+       switch (tuna->id) {
+       case ETHTOOL_PHY_SHORT_CABLE_PRESET:
+               return phy_write(phydev, MII_KSZ87XX_SHORT_CABLE,
+                                *(const u8 *)data);
+       case ETHTOOL_PHY_LPF_BW:
+               val = *(const u32 *)data;
+               if (val > 0xff)
+                       return -EINVAL;
+               return phy_write(phydev, MII_KSZ87XX_LPF_BW, (u8)val);
+       case ETHTOOL_PHY_DSP_EQ_INIT_VALUE:
+               val = *(const u32 *)data;
+               if (val > 0xff)
+                       return -EINVAL;
+               return phy_write(phydev, MII_KSZ87XX_EQ_INIT, (u8)val);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 static int ksz9021_load_values_from_of(struct phy_device *phydev,
                                       const struct device_node *of_node,
                                       u16 reg,
@@ -6961,6 +7020,8 @@ static struct phy_driver ksphy_driver[] = {
        /* PHY_BASIC_FEATURES */
        .config_init    = kszphy_config_init,
        .match_phy_device = ksz8795_match_phy_device,
+       .get_tunable    = ksz8795_get_tunable,
+       .set_tunable    = ksz8795_set_tunable,
        .suspend        = genphy_suspend,
        .resume         = genphy_resume,
 }, {