[PATCH v3 1/3] net: dsa: microchip: implement KSZ87xx Module 3 low-loss cable errata

From: Fidelio Lawson

Date: Tue Apr 14 2026 - 05:13:47 EST


Implement the "Module 3: Equalizer fix for short cables" erratum from
Microchip document DS80000687C for KSZ87xx switches.

The issue affects short or low-loss cable links (e.g. CAT5e/CAT6),
where the PHY receiver equalizer may amplify high-amplitude signals
excessively, resulting in internal distortion and link establishment
failures.

KSZ87xx devices require a workaround for the Module 3 low-loss cable
condition, controlled through the switch TABLE_LINK_MD_V indirect
registers.

The affected registers are part of the switch address space and are not
directly accessible from the PHY driver. To keep the PHY-facing API
clean and avoid leaking switch-specific details, model this errata
control as vendor-specific Clause 22 PHY registers.

A vendor-specific Clause 22 PHY register is introduced as a mode
selector in PHY_REG_LOW_LOSS_CTRL, and ksz8_r_phy() / ksz8_w_phy()
translate accesses to these bits into the appropriate indirect
TABLE_LINK_MD_V accesses.

The control register defines the following modes:
0: disabled (default behavior)
1: EQ training workaround
2: LPF 90 MHz
3: LPF 62 MHz
4: LPF 55 MHz
5: LPF 44 MHz

Workaround 1: Adjusts the DSP EQ training behavior via LinkMD register
0x3C. Widens and optimizes the DSP EQ compensation range,
and is expected to solve most short/low-loss cable issues.

Workaround 2: for the cases where Workaround 1 is not sufficient.
This one adjusts the receiver low-pass filter bandwidth, effectively
reducing the high-frequency component of the received signal

The register is accessible through standard PHY read/write operations
(e.g. phytool), without requiring any switch-specific userspace
interface. This allows robust link establishment on short or
low-loss cabling without requiring DTS properties and without
constraining hardware design choices.

The erratum affects the shared PHY analog front-end and therefore
applies globally to the switch.

Signed-off-by: Fidelio Lawson <fidelio.lawson@xxxxxxxxxx>
---
drivers/net/dsa/microchip/ksz8.c | 45 ++++++++++++++++++++++++++++++++++
drivers/net/dsa/microchip/ksz8_reg.h | 36 ++++++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz_common.h | 3 +++
3 files changed, 83 insertions(+), 1 deletion(-)

diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index c354abdafc1b..596c85654f24 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -1058,6 +1058,11 @@ int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val)
if (ret)
return ret;

+ break;
+ case PHY_REG_KSZ87XX_LOW_LOSS:
+ if (!ksz_is_ksz87xx(dev))
+ return -EOPNOTSUPP;
+ data = dev->low_loss_wa_mode;
break;
default:
processed = false;
@@ -1271,6 +1276,46 @@ int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val)
if (ret)
return ret;
break;
+ case PHY_REG_KSZ87XX_LOW_LOSS:
+ if (!ksz_is_ksz87xx(dev))
+ return -EOPNOTSUPP;
+
+ switch (val & PHY_KSZ87XX_LOW_LOSS_MASK) {
+ case PHY_LOW_LOSS_ERRATA_DISABLED:
+ ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_EQ_TRAIN,
+ KSZ87XX_EQ_TRAIN_DEFAULT);
+ if (!ret)
+ ret = ksz8_ind_write8(dev, TABLE_LINK_MD,
+ KSZ87XX_REG_PHY_LPF,
+ KSZ87XX_LOW_LOSS_LPF_90MHZ);
+ break;
+ case KSZ87XX_LOW_LOSS_EQ_TRAIN:
+ ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_EQ_TRAIN,
+ KSZ87XX_EQ_TRAIN_LOW_LOSS);
+ break;
+ case KSZ87XX_LOW_LOSS_LPF_90MHZ:
+ ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_PHY_LPF,
+ KSZ87XX_PHY_LPF_90MHZ);
+ break;
+ case KSZ87XX_LOW_LOSS_LPF_62MHZ:
+ ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_PHY_LPF,
+ KSZ87XX_PHY_LPF_62MHZ);
+ break;
+ case KSZ87XX_LOW_LOSS_LPF_55MHZ:
+ ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_PHY_LPF,
+ KSZ87XX_PHY_LPF_55MHZ);
+ break;
+ case KSZ87XX_LOW_LOSS_LPF_44MHZ:
+ ret = ksz8_ind_write8(dev, TABLE_LINK_MD, KSZ87XX_REG_PHY_LPF,
+ KSZ87XX_PHY_LPF_44MHZ);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!ret)
+ dev->low_loss_wa_mode = val & PHY_KSZ87XX_LOW_LOSS_MASK;
+ return ret;
default:
break;
}
diff --git a/drivers/net/dsa/microchip/ksz8_reg.h b/drivers/net/dsa/microchip/ksz8_reg.h
index 332408567b47..4e02e044339c 100644
--- a/drivers/net/dsa/microchip/ksz8_reg.h
+++ b/drivers/net/dsa/microchip/ksz8_reg.h
@@ -202,6 +202,10 @@
#define REG_PORT_3_STATUS_0 0x38
#define REG_PORT_4_STATUS_0 0x48

+/* KSZ87xx LinkMD registers (TABLE_LINK_MD_V) */
+#define KSZ87XX_REG_EQ_TRAIN 0x3C
+#define KSZ87XX_REG_PHY_LPF 0x4C
+
/* For KSZ8765. */
#define PORT_REMOTE_ASYM_PAUSE BIT(5)
#define PORT_REMOTE_SYM_PAUSE BIT(4)
@@ -342,7 +346,7 @@
#define TABLE_EEE (TABLE_EEE_V << TABLE_EXT_SELECT_S)
#define TABLE_ACL (TABLE_ACL_V << TABLE_EXT_SELECT_S)
#define TABLE_PME (TABLE_PME_V << TABLE_EXT_SELECT_S)
-#define TABLE_LINK_MD (TABLE_LINK_MD << TABLE_EXT_SELECT_S)
+#define TABLE_LINK_MD (TABLE_LINK_MD_V << TABLE_EXT_SELECT_S)
#define TABLE_READ BIT(4)
#define TABLE_SELECT_S 2
#define TABLE_STATIC_MAC_V 0
@@ -729,6 +733,36 @@
#define PHY_POWER_SAVING_ENABLE BIT(2)
#define PHY_REMOTE_LOOPBACK BIT(1)

+/* Equalizer low-loss workaround */
+#define PHY_REG_KSZ87XX_LOW_LOSS 0x1C
+#define PHY_KSZ87XX_LOW_LOSS_MASK GENMASK(2, 0)
+
+/* KSZ87xx low-loss EQ mode selector (vendor-specific PHY reg 0x1c)
+ *
+ * Values:
+ * 0: disabled (default behavior)
+ * 1: EQ training workaround
+ * 2: LPF 90 MHz
+ * 3: LPF 62 MHz
+ * 4: LPF 55 MHz
+ * 5: LPF 44 MHz
+ */
+#define PHY_LOW_LOSS_ERRATA_DISABLED 0
+#define KSZ87XX_LOW_LOSS_EQ_TRAIN 1
+#define KSZ87XX_LOW_LOSS_LPF_90MHZ 2
+#define KSZ87XX_LOW_LOSS_LPF_62MHZ 3
+#define KSZ87XX_LOW_LOSS_LPF_55MHZ 4
+#define KSZ87XX_LOW_LOSS_LPF_44MHZ 5
+
+#define KSZ87XX_EQ_TRAIN_DEFAULT 0x0A
+#define KSZ87XX_EQ_TRAIN_LOW_LOSS 0x15
+
+/* LPF bandwidth bits [7:6]: 00 = 90MHz, 01 = 62MHz, 10 = 55MHz, 11 = 44MHz */
+#define KSZ87XX_PHY_LPF_90MHZ 0x00
+#define KSZ87XX_PHY_LPF_62MHZ 0x40
+#define KSZ87XX_PHY_LPF_55MHZ 0x80
+#define KSZ87XX_PHY_LPF_44MHZ 0xC0
+
/* KSZ8463 specific registers. */
#define P1MBCR 0x4C
#define P1MBSR 0x4E
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 929aff4c55de..16a6074ea4b4 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -219,6 +219,9 @@ struct ksz_device {
* the switch’s internal PHYs, bypassing the main SPI interface.
*/
struct mii_bus *parent_mdio_bus;
+
+ /* Equalizer low-loss workaround tunable */
+ u8 low_loss_wa_mode; /* KSZ87xx low-loss EQ/LPF mode selector (0-5) */
};

/* List of supported models */

--
2.53.0