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.
Depending on the selected workaround (1 or 2), the driver writes a
specific value to the indirect PHY register
using the 6E/6F/A0 indirect access mechanism.
The errata fix is applied during global switch initialization when
enabled via device tree.
Signed-off-by: Fidelio Lawson <fidelio.lawson@exotec.com>
---
drivers/net/dsa/microchip/ksz8.c | 46 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index 78b42cf50ce2..b6f3a1ce85fc 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -1901,6 +1901,41 @@ void ksz8_phylink_mac_link_up(struct phylink_config *config,
ksz8_phy_port_link_up(dev, port, duplex, tx_pause, rx_pause);
}
+static int ksz8_handle_module3_errata(struct ksz_device *dev)
+{
+ int ret = 0;
+ const u16 *regs = dev->info->regs;
+ u16 indir_reg = 0x0000;
+ u8 indir_val = 0x00;
+
+ switch (dev->low_loss_wa_mode) {
+ case KSZ_LOW_LOSS_WA_1:
+ indir_reg = 0x3C;
+ indir_val = 0x15;
+ break;
+ case KSZ_LOW_LOSS_WA_2:
+ indir_reg = 0x4C;
+ indir_val = 0x40;
+ break;
+ default:
+ break;
+ }
+
+ mutex_lock(&dev->alu_mutex);
+
+ ret = ksz_write8(dev, regs[REG_IND_CTRL_0], 0xA0);
+
+ if (!ret)
+ ret = ksz_write8(dev, 0x6F, indir_reg);
+
+ if (!ret)
+ ret = ksz_write8(dev, regs[REG_IND_BYTE], indir_val);
+
+ mutex_unlock(&dev->alu_mutex);
+
+ return ret;
+}
+
static int ksz8_handle_global_errata(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
@@ -1915,6 +1950,17 @@ static int ksz8_handle_global_errata(struct dsa_switch *ds)
if (dev->info->ksz87xx_eee_link_erratum)
ret = ksz8_ind_write8(dev, TABLE_EEE, REG_IND_EEE_GLOB2_HI, 0);
+ /* KSZ87xx Errata DS80000687C.
+ * Module 3: Equalizer fix for short cables
+ * The receiver of the embedded PHYs is tuned by default
+ * to support long cable length applications.
+ * Because of this, the equalizer in the PHY may amplify
+ * high amplitude receiver signals to the point that
+ * the signal is distorted internally
+ */
+ if (!ret && dev->low_loss_wa_enable && ksz_is_ksz87xx(dev))
+ ret = ksz8_handle_module3_errata(dev);
+
return ret;
}
--
2.53.0
On 3/26/26 10:10 AM, Fidelio Lawson wrote:
> +static int ksz8_handle_module3_errata(struct ksz_device *dev)
> +{
> + int ret = 0;
> + const u16 *regs = dev->info->regs;
> + u16 indir_reg = 0x0000;
> + u8 indir_val = 0x00;
> +
> + switch (dev->low_loss_wa_mode) {
> + case KSZ_LOW_LOSS_WA_1:
> + indir_reg = 0x3C;
> + indir_val = 0x15;
> + break;
> + case KSZ_LOW_LOSS_WA_2:
> + indir_reg = 0x4C;
> + indir_val = 0x40;
> + break;
> + default:
> + break;
> + }
> +
> + mutex_lock(&dev->alu_mutex);
> +
> + ret = ksz_write8(dev, regs[REG_IND_CTRL_0], 0xA0);
> +
> + if (!ret)
> + ret = ksz_write8(dev, 0x6F, indir_reg);
> +
> + if (!ret)
> + ret = ksz_write8(dev, regs[REG_IND_BYTE], indir_val);
> +
> + mutex_unlock(&dev->alu_mutex);
> +
> + return ret;
> +}
Since this is configurable from DT, please adjust the bindings and make
the low pass filter bandwidth actually configurable according to the
values supported by the hardware, see this article:
https://microchip.my.site.com/s/article/Solution-for-Using-CAT-5E-or-CAT-6-Short-Cable-with-a-Link-Issue-for-the-KSZ8795-Family
The indirect register (0x4C) is an 8-bit register. The bits [7:6] are
described in the table below.
Low pass filter bandwidth
00 = 90MHz
01 = 62MHz
10 = 55MHz
11 = 44MHz
...
I had this attached patch in my tree for a while, I just never got
around to finishing it, because even with this in place, there was some
packet loss with short cables at low temperatures. Maybe it can help:
From 937278ee6dc0aa46797cc4b2d53078f95b6557a9 Mon Sep 17 00:00:00 2001
From: Marek Vasut <marex@nabladev.com>
Date: Mon, 2 Jun 2025 01:42:17 +0200
Subject: [PATCH] net: dsa: microchip: Fix KSZ87xx high quality cable errata
module 3
KSZ87xx may fail to establish link, or may take long time to
establish link, when using high quality > CAT-5 ethernet cables.
This is described in KSZ87xx Errata DS80000687C Module 3:
Module 3: Establishing a link through low loss connections.
The receiver of the embedded PHYs is tuned by default to
support long cable length applications. This was developed
using low quality, high loss cables. Because of this, the
equalizer in the PHY may amplify high amplitude receiver
signals to the point that the signal is distorted internally,
preventing a link from being established.
More detailed description and a more advanced fix is described in
"
Solution for Using CAT-5E or CAT-6 Short Cable with a Link Issue
for the KSZ8795 Family
"
https://microchip.my.site.com/s/article/Solution-for-Using-CAT-5E-or-CAT-6-Short-Cable-with-a-Link-Issue-for-the-KSZ8795-Family
The fix reduces low pass filter bandwidth from 90 MHz to 62 MHz
to block higher frequency components which make it through the
higher quality cables and saturate the receiver amplifier. In
case of even shorter cables, the link equalizer function has to
start from position 0 instead of the default position 0xf to
cater for those cables, configure the equalizer position as well.
Signed-off-by: Marek Vasut <marex@nabladev.com>
---
drivers/net/dsa/microchip/ksz8.c | 33 ++++++++++++++++++++++++++--
drivers/net/dsa/microchip/ksz8_reg.h | 2 +-
2 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c
b/drivers/net/dsa/microchip/ksz8.c
index 3309c2c172f02..f8716c8d0d375 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -1891,10 +1891,39 @@ static int ksz8_handle_global_errata(struct
dsa_switch *ds)
* KSZ879x/KSZ877x/KSZ876x and some EEE link partners may result in
* the link dropping.
*/
- if (dev->info->ksz87xx_eee_link_erratum)
+ if (dev->info->ksz87xx_eee_link_erratum) {
ret = ksz8_ind_write8(dev, TABLE_EEE, REG_IND_EEE_GLOB2_HI, 0);
+ if (ret)
+ return ret;
+ }
- return ret;
+ /*
+ * Module 3: Establishing a link through low loss connections.
+ * The receiver of the embedded PHYs is tuned by default to
+ * support long cable length applications. This was developed
+ * using low quality, high loss cables. Because of this, the
+ * equalizer in the PHY may amplify high amplitude receiver
+ * signals to the point that the signal is distorted internally,
+ * preventing a link from being established.
+ *
+ * More detailed description and a more advanced fix is described in
+ * "
+ * Solution for Using CAT-5E or CAT-6 Short Cable with a Link Issue
+ * for the KSZ8795 Family
+ * "
+ *
https://microchip.my.site.com/s/article/Solution-for-Using-CAT-5E-or-CAT-6-Short-Cable-with-a-Link-Issue-for-the-KSZ8795-Family
+ *
+ * The following two magic writes are the implementation of the
+ * aforementioned workaround.
+ */
+
+ /* Force low pass filter bandwidth from 90 MHz to 62 MHz */
+ ret = ksz8_ind_write8(dev, TABLE_LINK_MD, 0x4c, 0x40);
+ if (ret)
+ return ret;
+
+ /* Force DSP EQ initial value to 0 */
+ return ksz8_ind_write8(dev, TABLE_LINK_MD, 0x08, 0);
}
int ksz8_enable_stp_addr(struct ksz_device *dev)
diff --git a/drivers/net/dsa/microchip/ksz8_reg.h
b/drivers/net/dsa/microchip/ksz8_reg.h
index 329688603a582..c4897f27c6d1c 100644
--- a/drivers/net/dsa/microchip/ksz8_reg.h
+++ b/drivers/net/dsa/microchip/ksz8_reg.h
@@ -342,7 +342,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
--
2.53.0
> + mutex_lock(&dev->alu_mutex); > + > + ret = ksz_write8(dev, regs[REG_IND_CTRL_0], 0xA0); > + > + if (!ret) > + ret = ksz_write8(dev, 0x6F, indir_reg); > + > + if (!ret) > + ret = ksz_write8(dev, regs[REG_IND_BYTE], indir_val); > + > + mutex_unlock(&dev->alu_mutex); What address space are these registers in? Normally workarounds for a PHY would be in the PHY driver. But that assumes the registers are accessible from the PHY driver. Andrew
On Thu, Mar 26, 2026 at 10:10:23AM +0100, Fidelio Lawson wrote:
> 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.
>
> Depending on the selected workaround (1 or 2), the driver writes a
> specific value to the indirect PHY register
> using the 6E/6F/A0 indirect access mechanism.
>
> The errata fix is applied during global switch initialization when
> enabled via device tree.
>
> Signed-off-by: Fidelio Lawson <fidelio.lawson@exotec.com>
> ---
> drivers/net/dsa/microchip/ksz8.c | 46 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 46 insertions(+)
>
> diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
> index 78b42cf50ce2..b6f3a1ce85fc 100644
> --- a/drivers/net/dsa/microchip/ksz8.c
> +++ b/drivers/net/dsa/microchip/ksz8.c
> @@ -1901,6 +1901,41 @@ void ksz8_phylink_mac_link_up(struct phylink_config *config,
> ksz8_phy_port_link_up(dev, port, duplex, tx_pause, rx_pause);
> }
>
> +static int ksz8_handle_module3_errata(struct ksz_device *dev)
> +{
> + int ret = 0;
"ret" does not need to be zero-initialized. It is unconditionally
overwritten by ksz_write8().
And please sort lines with variable declarations in decreasing line
length order. Netdev calls this "reverse Christmas tree" ordering and is
the preferred coding style.
> + const u16 *regs = dev->info->regs;
> + u16 indir_reg = 0x0000;
> + u8 indir_val = 0x00;
> +
> + switch (dev->low_loss_wa_mode) {
> + case KSZ_LOW_LOSS_WA_1:
> + indir_reg = 0x3C;
> + indir_val = 0x15;
> + break;
> + case KSZ_LOW_LOSS_WA_2:
> + indir_reg = 0x4C;
> + indir_val = 0x40;
Do the 3c and 4c registers have any associated documentation? Do we know
what they are or what they do? We should have some macros for them,
instead of magic numbers.
> + break;
> + default:
> + break;
Is it expected that in the default case (no workaround), the code flow
writes indir_val = 0x00 to indir_reg = 0x0000? Or would it be better to
just exit early without making any change?
> + }
> +
> + mutex_lock(&dev->alu_mutex);
> +
> + ret = ksz_write8(dev, regs[REG_IND_CTRL_0], 0xA0);
> +
> + if (!ret)
> + ret = ksz_write8(dev, 0x6F, indir_reg);
> +
> + if (!ret)
> + ret = ksz_write8(dev, regs[REG_IND_BYTE], indir_val);
Is this sequence better suited for ksz8_ind_write8()? Perhaps wrapped in
another layer similar to ksz8_pme_write8(), once we know what the magic
numbers represent in terms of indirect table?
> +
> + mutex_unlock(&dev->alu_mutex);
> +
> + return ret;
> +}
> +
> static int ksz8_handle_global_errata(struct dsa_switch *ds)
> {
> struct ksz_device *dev = ds->priv;
> @@ -1915,6 +1950,17 @@ static int ksz8_handle_global_errata(struct dsa_switch *ds)
> if (dev->info->ksz87xx_eee_link_erratum)
> ret = ksz8_ind_write8(dev, TABLE_EEE, REG_IND_EEE_GLOB2_HI, 0);
>
> + /* KSZ87xx Errata DS80000687C.
> + * Module 3: Equalizer fix for short cables
> + * The receiver of the embedded PHYs is tuned by default
> + * to support long cable length applications.
> + * Because of this, the equalizer in the PHY may amplify
> + * high amplitude receiver signals to the point that
> + * the signal is distorted internally
> + */
> + if (!ret && dev->low_loss_wa_enable && ksz_is_ksz87xx(dev))
> + ret = ksz8_handle_module3_errata(dev);
> +
> return ret;
> }
>
>
> --
> 2.53.0
>
FYI, the driver is in a restructuring process. The ksz88xx_switch_ops
will be split out of the common ksz_switch_ops. This will conflict with
your series, so you should rebase on top of that much larger set.
You can coordinate with Bastien Curutchet to see what is the status:
https://lore.kernel.org/netdev/20260313153849.qkfzv5c2u6fepjku@skbuf
Hi Vladimir, Hi Fidelio
On 3/26/26 10:42 AM, Vladimir Oltean wrote:
> On Thu, Mar 26, 2026 at 10:10:23AM +0100, Fidelio Lawson wrote:
>> 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.
>>
>> Depending on the selected workaround (1 or 2), the driver writes a
>> specific value to the indirect PHY register
>> using the 6E/6F/A0 indirect access mechanism.
>>
>> The errata fix is applied during global switch initialization when
>> enabled via device tree.
>>
>> Signed-off-by: Fidelio Lawson <fidelio.lawson@exotec.com>
>> ---
>> drivers/net/dsa/microchip/ksz8.c | 46 ++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 46 insertions(+)
>>
>> diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
>> index 78b42cf50ce2..b6f3a1ce85fc 100644
>> --- a/drivers/net/dsa/microchip/ksz8.c
>> +++ b/drivers/net/dsa/microchip/ksz8.c
>> @@ -1901,6 +1901,41 @@ void ksz8_phylink_mac_link_up(struct phylink_config *config,
>> ksz8_phy_port_link_up(dev, port, duplex, tx_pause, rx_pause);
>> }
>>
>> +static int ksz8_handle_module3_errata(struct ksz_device *dev)
>> +{
>> + int ret = 0;
>
> "ret" does not need to be zero-initialized. It is unconditionally
> overwritten by ksz_write8().
>
> And please sort lines with variable declarations in decreasing line
> length order. Netdev calls this "reverse Christmas tree" ordering and is
> the preferred coding style.
>
>> + const u16 *regs = dev->info->regs;
>> + u16 indir_reg = 0x0000;
>> + u8 indir_val = 0x00;
>> +
>> + switch (dev->low_loss_wa_mode) {
>> + case KSZ_LOW_LOSS_WA_1:
>> + indir_reg = 0x3C;
>> + indir_val = 0x15;
>> + break;
>> + case KSZ_LOW_LOSS_WA_2:
>> + indir_reg = 0x4C;
>> + indir_val = 0x40;
>
> Do the 3c and 4c registers have any associated documentation? Do we know
> what they are or what they do? We should have some macros for them,
> instead of magic numbers.
>
>> + break;
>> + default:
>> + break;
>
> Is it expected that in the default case (no workaround), the code flow
> writes indir_val = 0x00 to indir_reg = 0x0000? Or would it be better to
> just exit early without making any change?
>
>> + }
>> +
>> + mutex_lock(&dev->alu_mutex);
>> +
>> + ret = ksz_write8(dev, regs[REG_IND_CTRL_0], 0xA0);
>> +
>> + if (!ret)
>> + ret = ksz_write8(dev, 0x6F, indir_reg);
>> +
>> + if (!ret)
>> + ret = ksz_write8(dev, regs[REG_IND_BYTE], indir_val);
>
> Is this sequence better suited for ksz8_ind_write8()? Perhaps wrapped in
> another layer similar to ksz8_pme_write8(), once we know what the magic
> numbers represent in terms of indirect table?
>
>> +
>> + mutex_unlock(&dev->alu_mutex);
>> +
>> + return ret;
>> +}
>> +
>> static int ksz8_handle_global_errata(struct dsa_switch *ds)
>> {
>> struct ksz_device *dev = ds->priv;
>> @@ -1915,6 +1950,17 @@ static int ksz8_handle_global_errata(struct dsa_switch *ds)
>> if (dev->info->ksz87xx_eee_link_erratum)
>> ret = ksz8_ind_write8(dev, TABLE_EEE, REG_IND_EEE_GLOB2_HI, 0);
>>
>> + /* KSZ87xx Errata DS80000687C.
>> + * Module 3: Equalizer fix for short cables
>> + * The receiver of the embedded PHYs is tuned by default
>> + * to support long cable length applications.
>> + * Because of this, the equalizer in the PHY may amplify
>> + * high amplitude receiver signals to the point that
>> + * the signal is distorted internally
>> + */
>> + if (!ret && dev->low_loss_wa_enable && ksz_is_ksz87xx(dev))
>> + ret = ksz8_handle_module3_errata(dev);
>> +
>> return ret;
>> }
>>
>>
>> --
>> 2.53.0
>>
>
> FYI, the driver is in a restructuring process. The ksz88xx_switch_ops
> will be split out of the common ksz_switch_ops. This will conflict with
> your series, so you should rebase on top of that much larger set.
> You can coordinate with Bastien Curutchet to see what is the status:
> https://lore.kernel.org/netdev/20260313153849.qkfzv5c2u6fepjku@skbuf
Indeed, I'm currently polishing a first iteration for it. I'll send it
ASAP.
Best regards,
Bastien
On 3/27/26 3:47 PM, Bastien Curutchet wrote: Hello Bastien, >> FYI, the driver is in a restructuring process. The ksz88xx_switch_ops >> will be split out of the common ksz_switch_ops. This will conflict with >> your series, so you should rebase on top of that much larger set. >> You can coordinate with Bastien Curutchet to see what is the status: >> https://lore.kernel.org/netdev/20260313153849.qkfzv5c2u6fepjku@skbuf > > Indeed, I'm currently polishing a first iteration for it. I'll send it > ASAP. I would argue that this low-loss cable errata is a bugfix, so maybe that should have a priority over restructuring ?
Hi Marek, On 3/27/26 3:55 PM, Marek Vasut wrote: > On 3/27/26 3:47 PM, Bastien Curutchet wrote: > > Hello Bastien, > >>> FYI, the driver is in a restructuring process. The ksz88xx_switch_ops >>> will be split out of the common ksz_switch_ops. This will conflict with >>> your series, so you should rebase on top of that much larger set. >>> You can coordinate with Bastien Curutchet to see what is the status: >>> https://lore.kernel.org/netdev/20260313153849.qkfzv5c2u6fepjku@skbuf >> >> Indeed, I'm currently polishing a first iteration for it. I'll send it >> ASAP. > I would argue that this low-loss cable errata is a bugfix, so maybe that > should have a priority over restructuring ? Yes, I'm fine with that. These three patches aren't big so I shouldn't encounter much difficulty to fix the conflicts on my side. Best regards, Bastien
© 2016 - 2026 Red Hat, Inc.