linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net-next v2 1/1] phy: micrel: add Signal Quality Indicator (SQI) support for KSZ9477 switch PHYs
@ 2025-06-25 12:41 Oleksij Rempel
  2025-06-25 15:33 ` Maxime Chevallier
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Oleksij Rempel @ 2025-06-25 12:41 UTC (permalink / raw)
  To: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni
  Cc: Oleksij Rempel, kernel, linux-kernel, Russell King, netdev

Add support for the Signal Quality Index (SQI) feature on KSZ9477 family
switches. This feature provides a relative measure of receive signal
quality.

The KSZ9477 PHY provides four separate SQI values for a 1000BASE-T link,
one for each differential pair (Channel A-D). Since the current get_sqi
UAPI only supports returning a single value per port, this
implementation reads the SQI from Channel A as a representative metric.
This can be extended to provide per-channel readings once the UAPI is
enhanced for multi-channel support.

The hardware provides a raw 7-bit SQI value (0-127), where lower is
better. This raw value is converted to the standard 0-7 scale to provide
a usable, interoperable metric for userspace tools, abstracting away
hardware-specifics. The mapping to the standard 0-7 SQI scale was
determined empirically by injecting a 30MHz sine wave into the receive
pair with a signal generator. It was observed that the link becomes
unstable and drops when the raw SQI value reaches 8. This
implementation is based on these test results.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v2:
- Reword commit message
- Fix SQI value inversion
- Implement an empirically-derived, non-linear mapping to the standard
  0-7 SQI scale
---
 drivers/net/phy/micrel.c | 124 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)

diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index d0429dc8f561..6422d9a7c09f 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -2173,6 +2173,128 @@ static void kszphy_get_phy_stats(struct phy_device *phydev,
 	stats->rx_errors = priv->phy_stats.rx_err_pkt_cnt;
 }
 
+/* Base register for Signal Quality Indicator (SQI) - Channel A
+ *
+ * MMD Address: MDIO_MMD_PMAPMD (0x01)
+ * Register:    0xAC (Channel A)
+ * Each channel (pair) has its own register:
+ *   Channel A: 0xAC
+ *   Channel B: 0xAD
+ *   Channel C: 0xAE
+ *   Channel D: 0xAF
+ */
+#define KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A	0xAC
+
+/* SQI field mask for bits [14:8]
+ *
+ * SQI indicates relative quality of the signal.
+ * A lower value indicates better signal quality.
+ */
+#define KSZ9477_MMD_SQI_MASK			GENMASK(14, 8)
+
+#define KSZ9477_SQI_MAX				7
+
+/* Delay between consecutive SQI register reads in microseconds.
+ *
+ * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
+ * The register is updated every 2 µs. Use 3 µs to avoid redundant reads.
+ */
+#define KSZ9477_MMD_SQI_READ_DELAY_US		3
+
+/* Number of SQI samples to average for a stable result.
+ *
+ * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
+ * For noisy environments, a minimum of 30–50 readings is recommended.
+ */
+#define KSZ9477_SQI_SAMPLE_COUNT		40
+
+/* The hardware SQI register provides a raw value from 0-127, where a lower
+ * value indicates better signal quality. However, empirical testing has
+ * shown that only the 0-7 range is relevant for a functional link. A raw
+ * value of 8 or higher was measured directly before link drop. This aligns
+ * with the OPEN Alliance recommendation that SQI=0 should represent the
+ * pre-failure state.
+ *
+ * This table provides a non-linear mapping from the useful raw hardware
+ * values (0-7) to the standard 0-7 SQI scale, where higher is better.
+ */
+static const u8 ksz_sqi_mapping[] = {
+	7, /* raw 0 -> SQI 7 */
+	7, /* raw 1 -> SQI 7 */
+	6, /* raw 2 -> SQI 6 */
+	5, /* raw 3 -> SQI 5 */
+	4, /* raw 4 -> SQI 4 */
+	3, /* raw 5 -> SQI 3 */
+	2, /* raw 6 -> SQI 2 */
+	1, /* raw 7 -> SQI 1 */
+};
+
+/**
+ * kszphy_get_sqi - Read, average, and map Signal Quality Index (SQI)
+ * @phydev: the PHY device
+ *
+ * This function reads and processes the raw Signal Quality Index from the
+ * PHY. Based on empirical testing, a raw value of 8 or higher indicates a
+ * pre-failure state and is mapped to SQI 0. Raw values from 0-7 are
+ * mapped to the standard 0-7 SQI scale via a lookup table.
+ *
+ * Return: SQI value (0–7), or a negative errno on failure.
+ */
+static int kszphy_get_sqi(struct phy_device *phydev)
+{
+	int sum = 0;
+	int i, val, raw_sqi, avg_raw_sqi;
+	u8 channels;
+
+	/* Determine applicable channels based on link speed */
+	if (phydev->speed == SPEED_1000)
+		/* TODO: current SQI API only supports 1 channel. */
+		channels = 1;
+	else if (phydev->speed == SPEED_100)
+		channels = 1;
+	else
+		return -EOPNOTSUPP;
+
+	/*
+	 * Sample and accumulate SQI readings for each pair (currently only one).
+	 *
+	 * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
+	 * - The SQI register is updated every 2 µs.
+	 * - Values may fluctuate significantly, even in low-noise environments.
+	 * - For reliable estimation, average a minimum of 30–50 samples
+	 *   (recommended for noisy environments)
+	 * - In noisy environments, individual readings are highly unreliable.
+	 *
+	 * We use 40 samples per pair with a delay of 3 µs between each
+	 * read to ensure new values are captured (2 µs update interval).
+	 */
+	for (i = 0; i < KSZ9477_SQI_SAMPLE_COUNT; i++) {
+		val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
+				   KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A);
+		if (val < 0)
+			return val;
+
+		raw_sqi = FIELD_GET(KSZ9477_MMD_SQI_MASK, val);
+		sum += raw_sqi;
+
+		udelay(KSZ9477_MMD_SQI_READ_DELAY_US);
+	}
+
+	avg_raw_sqi = sum / KSZ9477_SQI_SAMPLE_COUNT;
+
+	/* Handle the pre-fail/failed state first. */
+	if (avg_raw_sqi >= ARRAY_SIZE(ksz_sqi_mapping))
+		return 0;
+
+	/* Use the lookup table for the good signal range. */
+	return ksz_sqi_mapping[avg_raw_sqi];
+}
+
+static int kszphy_get_sqi_max(struct phy_device *phydev)
+{
+	return KSZ9477_SQI_MAX;
+}
+
 static void kszphy_enable_clk(struct phy_device *phydev)
 {
 	struct kszphy_priv *priv = phydev->priv;
@@ -5801,6 +5923,8 @@ static struct phy_driver ksphy_driver[] = {
 	.update_stats	= kszphy_update_stats,
 	.cable_test_start	= ksz9x31_cable_test_start,
 	.cable_test_get_status	= ksz9x31_cable_test_get_status,
+	.get_sqi	= kszphy_get_sqi,
+	.get_sqi_max	= kszphy_get_sqi_max,
 } };
 
 module_phy_driver(ksphy_driver);
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH net-next v2 1/1] phy: micrel: add Signal Quality Indicator (SQI) support for KSZ9477 switch PHYs
  2025-06-25 12:41 [PATCH net-next v2 1/1] phy: micrel: add Signal Quality Indicator (SQI) support for KSZ9477 switch PHYs Oleksij Rempel
@ 2025-06-25 15:33 ` Maxime Chevallier
  2025-06-25 16:36   ` Oleksij Rempel
  2025-06-25 18:06 ` Lucas Stach
  2025-06-26  0:07 ` kernel test robot
  2 siblings, 1 reply; 7+ messages in thread
From: Maxime Chevallier @ 2025-06-25 15:33 UTC (permalink / raw)
  To: Oleksij Rempel
  Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, kernel, linux-kernel, Russell King,
	netdev

Hi Oleksij,

On Wed, 25 Jun 2025 14:41:26 +0200
Oleksij Rempel <o.rempel@pengutronix.de> wrote:

> Add support for the Signal Quality Index (SQI) feature on KSZ9477 family
> switches. This feature provides a relative measure of receive signal
> quality.
> 
> The KSZ9477 PHY provides four separate SQI values for a 1000BASE-T link,
> one for each differential pair (Channel A-D). Since the current get_sqi
> UAPI only supports returning a single value per port, this
> implementation reads the SQI from Channel A as a representative metric.
> This can be extended to provide per-channel readings once the UAPI is
> enhanced for multi-channel support.
> 
> The hardware provides a raw 7-bit SQI value (0-127), where lower is
> better. This raw value is converted to the standard 0-7 scale to provide
> a usable, interoperable metric for userspace tools, abstracting away
> hardware-specifics. The mapping to the standard 0-7 SQI scale was
> determined empirically by injecting a 30MHz sine wave into the receive
> pair with a signal generator. It was observed that the link becomes
> unstable and drops when the raw SQI value reaches 8. This
> implementation is based on these test results.

[...]

> +/**
> + * kszphy_get_sqi - Read, average, and map Signal Quality Index (SQI)
> + * @phydev: the PHY device
> + *
> + * This function reads and processes the raw Signal Quality Index from the
> + * PHY. Based on empirical testing, a raw value of 8 or higher indicates a
> + * pre-failure state and is mapped to SQI 0. Raw values from 0-7 are
> + * mapped to the standard 0-7 SQI scale via a lookup table.
> + *
> + * Return: SQI value (0–7), or a negative errno on failure.
> + */
> +static int kszphy_get_sqi(struct phy_device *phydev)
> +{
> +	int sum = 0;
> +	int i, val, raw_sqi, avg_raw_sqi;
> +	u8 channels;
> +
> +	/* Determine applicable channels based on link speed */
> +	if (phydev->speed == SPEED_1000)
> +		/* TODO: current SQI API only supports 1 channel. */
> +		channels = 1;
> +	else if (phydev->speed == SPEED_100)
> +		channels = 1;

I understand the placeholder logic waiting for some improved uAPI, but
this triggers an unused variable warning :( I think the commit log and
the comment below are enough to explain that this can be improved later
on.
	
> +	else
> +		return -EOPNOTSUPP;
> +
> +	/*
> +	 * Sample and accumulate SQI readings for each pair (currently only one).

Maxime

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH net-next v2 1/1] phy: micrel: add Signal Quality Indicator (SQI) support for KSZ9477 switch PHYs
  2025-06-25 15:33 ` Maxime Chevallier
@ 2025-06-25 16:36   ` Oleksij Rempel
  0 siblings, 0 replies; 7+ messages in thread
From: Oleksij Rempel @ 2025-06-25 16:36 UTC (permalink / raw)
  To: Maxime Chevallier
  Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, kernel, linux-kernel, Russell King,
	netdev

On Wed, Jun 25, 2025 at 05:33:23PM +0200, Maxime Chevallier wrote:
> Hi Oleksij,
> 
> On Wed, 25 Jun 2025 14:41:26 +0200
> Oleksij Rempel <o.rempel@pengutronix.de> wrote:
> 
> > Add support for the Signal Quality Index (SQI) feature on KSZ9477 family
> > switches. This feature provides a relative measure of receive signal
> > quality.
> > 
> > The KSZ9477 PHY provides four separate SQI values for a 1000BASE-T link,
> > one for each differential pair (Channel A-D). Since the current get_sqi
> > UAPI only supports returning a single value per port, this
> > implementation reads the SQI from Channel A as a representative metric.
> > This can be extended to provide per-channel readings once the UAPI is
> > enhanced for multi-channel support.
> > 
> > The hardware provides a raw 7-bit SQI value (0-127), where lower is
> > better. This raw value is converted to the standard 0-7 scale to provide
> > a usable, interoperable metric for userspace tools, abstracting away
> > hardware-specifics. The mapping to the standard 0-7 SQI scale was
> > determined empirically by injecting a 30MHz sine wave into the receive
> > pair with a signal generator. It was observed that the link becomes
> > unstable and drops when the raw SQI value reaches 8. This
> > implementation is based on these test results.
> 
> [...]
> 
> > +/**
> > + * kszphy_get_sqi - Read, average, and map Signal Quality Index (SQI)
> > + * @phydev: the PHY device
> > + *
> > + * This function reads and processes the raw Signal Quality Index from the
> > + * PHY. Based on empirical testing, a raw value of 8 or higher indicates a
> > + * pre-failure state and is mapped to SQI 0. Raw values from 0-7 are
> > + * mapped to the standard 0-7 SQI scale via a lookup table.
> > + *
> > + * Return: SQI value (0–7), or a negative errno on failure.
> > + */
> > +static int kszphy_get_sqi(struct phy_device *phydev)
> > +{
> > +	int sum = 0;
> > +	int i, val, raw_sqi, avg_raw_sqi;
> > +	u8 channels;
> > +
> > +	/* Determine applicable channels based on link speed */
> > +	if (phydev->speed == SPEED_1000)
> > +		/* TODO: current SQI API only supports 1 channel. */
> > +		channels = 1;
> > +	else if (phydev->speed == SPEED_100)
> > +		channels = 1;
> 
> I understand the placeholder logic waiting for some improved uAPI, but
> this triggers an unused variable warning :( I think the commit log and
> the comment below are enough to explain that this can be improved later
> on.

Grr.. sorry.. 

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH net-next v2 1/1] phy: micrel: add Signal Quality Indicator (SQI) support for KSZ9477 switch PHYs
  2025-06-25 12:41 [PATCH net-next v2 1/1] phy: micrel: add Signal Quality Indicator (SQI) support for KSZ9477 switch PHYs Oleksij Rempel
  2025-06-25 15:33 ` Maxime Chevallier
@ 2025-06-25 18:06 ` Lucas Stach
  2025-06-26  5:11   ` Oleksij Rempel
  2025-06-26  0:07 ` kernel test robot
  2 siblings, 1 reply; 7+ messages in thread
From: Lucas Stach @ 2025-06-25 18:06 UTC (permalink / raw)
  To: Oleksij Rempel, Andrew Lunn, Heiner Kallweit, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: netdev, linux-kernel, kernel, Russell King

Hi Oleksij,

Am Mittwoch, dem 25.06.2025 um 14:41 +0200 schrieb Oleksij Rempel:
> Add support for the Signal Quality Index (SQI) feature on KSZ9477 family
> switches. This feature provides a relative measure of receive signal
> quality.
> 
> The KSZ9477 PHY provides four separate SQI values for a 1000BASE-T link,
> one for each differential pair (Channel A-D). Since the current get_sqi
> UAPI only supports returning a single value per port, this
> implementation reads the SQI from Channel A as a representative metric.

I wonder if it wouldn't be more useful to report the worst SQI from all
the channels instead.

> This can be extended to provide per-channel readings once the UAPI is
> enhanced for multi-channel support.
> 
> The hardware provides a raw 7-bit SQI value (0-127), where lower is
> better. This raw value is converted to the standard 0-7 scale to provide
> a usable, interoperable metric for userspace tools, abstracting away
> hardware-specifics. The mapping to the standard 0-7 SQI scale was
> determined empirically by injecting a 30MHz sine wave into the receive
> pair with a signal generator. It was observed that the link becomes
> unstable and drops when the raw SQI value reaches 8. This
> implementation is based on these test results.
> 
> Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
> ---
> changes v2:
> - Reword commit message
> - Fix SQI value inversion
> - Implement an empirically-derived, non-linear mapping to the standard
>   0-7 SQI scale
> ---
>  drivers/net/phy/micrel.c | 124 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 124 insertions(+)
> 
> diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
> index d0429dc8f561..6422d9a7c09f 100644
> --- a/drivers/net/phy/micrel.c
> +++ b/drivers/net/phy/micrel.c
> @@ -2173,6 +2173,128 @@ static void kszphy_get_phy_stats(struct phy_device *phydev,
>  	stats->rx_errors = priv->phy_stats.rx_err_pkt_cnt;
>  }
>  
> +/* Base register for Signal Quality Indicator (SQI) - Channel A
> + *
> + * MMD Address: MDIO_MMD_PMAPMD (0x01)
> + * Register:    0xAC (Channel A)
> + * Each channel (pair) has its own register:
> + *   Channel A: 0xAC
> + *   Channel B: 0xAD
> + *   Channel C: 0xAE
> + *   Channel D: 0xAF
> + */
> +#define KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A	0xAC
> +
> +/* SQI field mask for bits [14:8]
> + *
> + * SQI indicates relative quality of the signal.
> + * A lower value indicates better signal quality.
> + */
> +#define KSZ9477_MMD_SQI_MASK			GENMASK(14, 8)
> +
> +#define KSZ9477_SQI_MAX				7
> +
> +/* Delay between consecutive SQI register reads in microseconds.
> + *
> + * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
> + * The register is updated every 2 µs. Use 3 µs to avoid redundant reads.
> + */
> +#define KSZ9477_MMD_SQI_READ_DELAY_US		3
> +
> +/* Number of SQI samples to average for a stable result.
> + *
> + * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
> + * For noisy environments, a minimum of 30–50 readings is recommended.
> + */
> +#define KSZ9477_SQI_SAMPLE_COUNT		40
> +
> +/* The hardware SQI register provides a raw value from 0-127, where a lower
> + * value indicates better signal quality. However, empirical testing has
> + * shown that only the 0-7 range is relevant for a functional link. A raw
> + * value of 8 or higher was measured directly before link drop. This aligns
> + * with the OPEN Alliance recommendation that SQI=0 should represent the
> + * pre-failure state.
> + *
> + * This table provides a non-linear mapping from the useful raw hardware
> + * values (0-7) to the standard 0-7 SQI scale, where higher is better.
> + */
> +static const u8 ksz_sqi_mapping[] = {
> +	7, /* raw 0 -> SQI 7 */
> +	7, /* raw 1 -> SQI 7 */
> +	6, /* raw 2 -> SQI 6 */
> +	5, /* raw 3 -> SQI 5 */
> +	4, /* raw 4 -> SQI 4 */
> +	3, /* raw 5 -> SQI 3 */
> +	2, /* raw 6 -> SQI 2 */
> +	1, /* raw 7 -> SQI 1 */
> +};
> +
> +/**
> + * kszphy_get_sqi - Read, average, and map Signal Quality Index (SQI)
> + * @phydev: the PHY device
> + *
> + * This function reads and processes the raw Signal Quality Index from the
> + * PHY. Based on empirical testing, a raw value of 8 or higher indicates a
> + * pre-failure state and is mapped to SQI 0. Raw values from 0-7 are
> + * mapped to the standard 0-7 SQI scale via a lookup table.
> + *
> + * Return: SQI value (0–7), or a negative errno on failure.
> + */
> +static int kszphy_get_sqi(struct phy_device *phydev)
> +{
> +	int sum = 0;
> +	int i, val, raw_sqi, avg_raw_sqi;
> +	u8 channels;
> +
> +	/* Determine applicable channels based on link speed */
> +	if (phydev->speed == SPEED_1000)
> +		/* TODO: current SQI API only supports 1 channel. */
> +		channels = 1;
> +	else if (phydev->speed == SPEED_100)
> +		channels = 1;
> +	else
> +		return -EOPNOTSUPP;
> +
> +	/*
> +	 * Sample and accumulate SQI readings for each pair (currently only one).
> +	 *
> +	 * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
> +	 * - The SQI register is updated every 2 µs.
> +	 * - Values may fluctuate significantly, even in low-noise environments.
> +	 * - For reliable estimation, average a minimum of 30–50 samples
> +	 *   (recommended for noisy environments)
> +	 * - In noisy environments, individual readings are highly unreliable.
> +	 *
> +	 * We use 40 samples per pair with a delay of 3 µs between each
> +	 * read to ensure new values are captured (2 µs update interval).
> +	 */
> +	for (i = 0; i < KSZ9477_SQI_SAMPLE_COUNT; i++) {
> +		val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
> +				   KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A);
> +		if (val < 0)
> +			return val;
> +
> +		raw_sqi = FIELD_GET(KSZ9477_MMD_SQI_MASK, val);
> +		sum += raw_sqi;
> +
> +		udelay(KSZ9477_MMD_SQI_READ_DELAY_US);

This ends up spending a sizable amount of time just spinning the CPU to
collect the samples for the averaging. Given that only very low values
seem to indicate a working link, I wonder how significant the
fluctuations in reported link quality are in reality. Is it really
worth spending 120us of CPU time to average those values?

Maybe a running average updated with a new sample each time this
function is called would be sufficient?

Regards,
Lucas

> +	}
> +
> +	avg_raw_sqi = sum / KSZ9477_SQI_SAMPLE_COUNT;
> +
> +	/* Handle the pre-fail/failed state first. */
> +	if (avg_raw_sqi >= ARRAY_SIZE(ksz_sqi_mapping))
> +		return 0;
> +
> +	/* Use the lookup table for the good signal range. */
> +	return ksz_sqi_mapping[avg_raw_sqi];
> +}
> +
> +static int kszphy_get_sqi_max(struct phy_device *phydev)
> +{
> +	return KSZ9477_SQI_MAX;
> +}
> +
>  static void kszphy_enable_clk(struct phy_device *phydev)
>  {
>  	struct kszphy_priv *priv = phydev->priv;
> @@ -5801,6 +5923,8 @@ static struct phy_driver ksphy_driver[] = {
>  	.update_stats	= kszphy_update_stats,
>  	.cable_test_start	= ksz9x31_cable_test_start,
>  	.cable_test_get_status	= ksz9x31_cable_test_get_status,
> +	.get_sqi	= kszphy_get_sqi,
> +	.get_sqi_max	= kszphy_get_sqi_max,
>  } };
>  
>  module_phy_driver(ksphy_driver);


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH net-next v2 1/1] phy: micrel: add Signal Quality Indicator (SQI) support for KSZ9477 switch PHYs
  2025-06-25 12:41 [PATCH net-next v2 1/1] phy: micrel: add Signal Quality Indicator (SQI) support for KSZ9477 switch PHYs Oleksij Rempel
  2025-06-25 15:33 ` Maxime Chevallier
  2025-06-25 18:06 ` Lucas Stach
@ 2025-06-26  0:07 ` kernel test robot
  2 siblings, 0 replies; 7+ messages in thread
From: kernel test robot @ 2025-06-26  0:07 UTC (permalink / raw)
  To: Oleksij Rempel, Andrew Lunn, Heiner Kallweit, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: llvm, oe-kbuild-all, netdev, Oleksij Rempel, kernel, linux-kernel,
	Russell King

Hi Oleksij,

kernel test robot noticed the following build warnings:

[auto build test WARNING on net-next/main]

url:    https://github.com/intel-lab-lkp/linux/commits/Oleksij-Rempel/phy-micrel-add-Signal-Quality-Indicator-SQI-support-for-KSZ9477-switch-PHYs/20250625-204330
base:   net-next/main
patch link:    https://lore.kernel.org/r/20250625124127.4176960-1-o.rempel%40pengutronix.de
patch subject: [PATCH net-next v2 1/1] phy: micrel: add Signal Quality Indicator (SQI) support for KSZ9477 switch PHYs
config: i386-buildonly-randconfig-004-20250626 (https://download.01.org/0day-ci/archive/20250626/202506260756.KhOdmLCy-lkp@intel.com/config)
compiler: clang version 20.1.7 (https://github.com/llvm/llvm-project 6146a88f60492b520a36f8f8f3231e15f3cc6082)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250626/202506260756.KhOdmLCy-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202506260756.KhOdmLCy-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/net/phy/micrel.c:2247:5: warning: variable 'channels' set but not used [-Wunused-but-set-variable]
    2247 |         u8 channels;
         |            ^
   1 warning generated.


vim +/channels +2247 drivers/net/phy/micrel.c

  2231	
  2232	/**
  2233	 * kszphy_get_sqi - Read, average, and map Signal Quality Index (SQI)
  2234	 * @phydev: the PHY device
  2235	 *
  2236	 * This function reads and processes the raw Signal Quality Index from the
  2237	 * PHY. Based on empirical testing, a raw value of 8 or higher indicates a
  2238	 * pre-failure state and is mapped to SQI 0. Raw values from 0-7 are
  2239	 * mapped to the standard 0-7 SQI scale via a lookup table.
  2240	 *
  2241	 * Return: SQI value (0–7), or a negative errno on failure.
  2242	 */
  2243	static int kszphy_get_sqi(struct phy_device *phydev)
  2244	{
  2245		int sum = 0;
  2246		int i, val, raw_sqi, avg_raw_sqi;
> 2247		u8 channels;
  2248	
  2249		/* Determine applicable channels based on link speed */
  2250		if (phydev->speed == SPEED_1000)
  2251			/* TODO: current SQI API only supports 1 channel. */
  2252			channels = 1;
  2253		else if (phydev->speed == SPEED_100)
  2254			channels = 1;
  2255		else
  2256			return -EOPNOTSUPP;
  2257	
  2258		/*
  2259		 * Sample and accumulate SQI readings for each pair (currently only one).
  2260		 *
  2261		 * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
  2262		 * - The SQI register is updated every 2 µs.
  2263		 * - Values may fluctuate significantly, even in low-noise environments.
  2264		 * - For reliable estimation, average a minimum of 30–50 samples
  2265		 *   (recommended for noisy environments)
  2266		 * - In noisy environments, individual readings are highly unreliable.
  2267		 *
  2268		 * We use 40 samples per pair with a delay of 3 µs between each
  2269		 * read to ensure new values are captured (2 µs update interval).
  2270		 */
  2271		for (i = 0; i < KSZ9477_SQI_SAMPLE_COUNT; i++) {
  2272			val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
  2273					   KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A);
  2274			if (val < 0)
  2275				return val;
  2276	
  2277			raw_sqi = FIELD_GET(KSZ9477_MMD_SQI_MASK, val);
  2278			sum += raw_sqi;
  2279	
  2280			udelay(KSZ9477_MMD_SQI_READ_DELAY_US);
  2281		}
  2282	
  2283		avg_raw_sqi = sum / KSZ9477_SQI_SAMPLE_COUNT;
  2284	
  2285		/* Handle the pre-fail/failed state first. */
  2286		if (avg_raw_sqi >= ARRAY_SIZE(ksz_sqi_mapping))
  2287			return 0;
  2288	
  2289		/* Use the lookup table for the good signal range. */
  2290		return ksz_sqi_mapping[avg_raw_sqi];
  2291	}
  2292	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH net-next v2 1/1] phy: micrel: add Signal Quality Indicator (SQI) support for KSZ9477 switch PHYs
  2025-06-25 18:06 ` Lucas Stach
@ 2025-06-26  5:11   ` Oleksij Rempel
  2025-06-26 12:20     ` Oleksij Rempel
  0 siblings, 1 reply; 7+ messages in thread
From: Oleksij Rempel @ 2025-06-26  5:11 UTC (permalink / raw)
  To: Lucas Stach
  Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, netdev, linux-kernel, kernel,
	Russell King

On Wed, Jun 25, 2025 at 08:06:32PM +0200, Lucas Stach wrote:
> Hi Oleksij,
> 
> Am Mittwoch, dem 25.06.2025 um 14:41 +0200 schrieb Oleksij Rempel:
> > Add support for the Signal Quality Index (SQI) feature on KSZ9477 family
> > switches. This feature provides a relative measure of receive signal
> > quality.
> > 
> > The KSZ9477 PHY provides four separate SQI values for a 1000BASE-T link,
> > one for each differential pair (Channel A-D). Since the current get_sqi
> > UAPI only supports returning a single value per port, this
> > implementation reads the SQI from Channel A as a representative metric.
> 
> I wonder if it wouldn't be more useful to report the worst SQI from all
> the channels instead.

It was my first idea too, just to report the worst SQI from all
channels. But this makes it impossible to report SQI for each pair
later. If we ever want to support SQI per pair, the current code would
suddenly start to show only SQI for pair A, not the worst one, so the
SQI interface would change meaning without warning.

There is another problem if we want to extend the SQI UAPI for per-pair
support: with 100Mbit/s links, we can't know which pair is used. The PHY
reports SQI only for the RX pair, which can change depending on MDI-X
resolution, and with auto MDI-X mode, this PHY doesn't tell us which
pair it is.

That means, at this point, we have hardware which in some modes can't
provide pair-related information. So, it is better to keep the already
existing UAPI explicitly per link instead of per pair. This matches the
current hardware limits and avoids confusion for users and developers.
If we want per-pair SQI in the future, the API must handle these cases
clearly.

> > This can be extended to provide per-channel readings once the UAPI is
> > enhanced for multi-channel support.
> > 
> > The hardware provides a raw 7-bit SQI value (0-127), where lower is
> > better. This raw value is converted to the standard 0-7 scale to provide
> > a usable, interoperable metric for userspace tools, abstracting away
> > hardware-specifics. The mapping to the standard 0-7 SQI scale was
> > determined empirically by injecting a 30MHz sine wave into the receive
> > pair with a signal generator. It was observed that the link becomes
> > unstable and drops when the raw SQI value reaches 8. This
> > implementation is based on these test results.
> > 
> > Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
> > ---
> > changes v2:
> > - Reword commit message
> > - Fix SQI value inversion
> > - Implement an empirically-derived, non-linear mapping to the standard
> >   0-7 SQI scale
> > ---
> >  drivers/net/phy/micrel.c | 124 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 124 insertions(+)
> > 
> > diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
> > index d0429dc8f561..6422d9a7c09f 100644
> > --- a/drivers/net/phy/micrel.c
> > +++ b/drivers/net/phy/micrel.c
> > @@ -2173,6 +2173,128 @@ static void kszphy_get_phy_stats(struct phy_device *phydev,
> >  	stats->rx_errors = priv->phy_stats.rx_err_pkt_cnt;
> >  }
> >  
> > +/* Base register for Signal Quality Indicator (SQI) - Channel A
> > + *
> > + * MMD Address: MDIO_MMD_PMAPMD (0x01)
> > + * Register:    0xAC (Channel A)
> > + * Each channel (pair) has its own register:
> > + *   Channel A: 0xAC
> > + *   Channel B: 0xAD
> > + *   Channel C: 0xAE
> > + *   Channel D: 0xAF
> > + */
> > +#define KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A	0xAC
> > +
> > +/* SQI field mask for bits [14:8]
> > + *
> > + * SQI indicates relative quality of the signal.
> > + * A lower value indicates better signal quality.
> > + */
> > +#define KSZ9477_MMD_SQI_MASK			GENMASK(14, 8)
> > +
> > +#define KSZ9477_SQI_MAX				7
> > +
> > +/* Delay between consecutive SQI register reads in microseconds.
> > + *
> > + * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
> > + * The register is updated every 2 µs. Use 3 µs to avoid redundant reads.
> > + */
> > +#define KSZ9477_MMD_SQI_READ_DELAY_US		3
> > +
> > +/* Number of SQI samples to average for a stable result.
> > + *
> > + * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
> > + * For noisy environments, a minimum of 30–50 readings is recommended.
> > + */
> > +#define KSZ9477_SQI_SAMPLE_COUNT		40
> > +
> > +/* The hardware SQI register provides a raw value from 0-127, where a lower
> > + * value indicates better signal quality. However, empirical testing has
> > + * shown that only the 0-7 range is relevant for a functional link. A raw
> > + * value of 8 or higher was measured directly before link drop. This aligns
> > + * with the OPEN Alliance recommendation that SQI=0 should represent the
> > + * pre-failure state.
> > + *
> > + * This table provides a non-linear mapping from the useful raw hardware
> > + * values (0-7) to the standard 0-7 SQI scale, where higher is better.
> > + */
> > +static const u8 ksz_sqi_mapping[] = {
> > +	7, /* raw 0 -> SQI 7 */
> > +	7, /* raw 1 -> SQI 7 */
> > +	6, /* raw 2 -> SQI 6 */
> > +	5, /* raw 3 -> SQI 5 */
> > +	4, /* raw 4 -> SQI 4 */
> > +	3, /* raw 5 -> SQI 3 */
> > +	2, /* raw 6 -> SQI 2 */
> > +	1, /* raw 7 -> SQI 1 */
> > +};
> > +
> > +/**
> > + * kszphy_get_sqi - Read, average, and map Signal Quality Index (SQI)
> > + * @phydev: the PHY device
> > + *
> > + * This function reads and processes the raw Signal Quality Index from the
> > + * PHY. Based on empirical testing, a raw value of 8 or higher indicates a
> > + * pre-failure state and is mapped to SQI 0. Raw values from 0-7 are
> > + * mapped to the standard 0-7 SQI scale via a lookup table.
> > + *
> > + * Return: SQI value (0–7), or a negative errno on failure.
> > + */
> > +static int kszphy_get_sqi(struct phy_device *phydev)
> > +{
> > +	int sum = 0;
> > +	int i, val, raw_sqi, avg_raw_sqi;
> > +	u8 channels;
> > +
> > +	/* Determine applicable channels based on link speed */
> > +	if (phydev->speed == SPEED_1000)
> > +		/* TODO: current SQI API only supports 1 channel. */
> > +		channels = 1;
> > +	else if (phydev->speed == SPEED_100)
> > +		channels = 1;
> > +	else
> > +		return -EOPNOTSUPP;
> > +
> > +	/*
> > +	 * Sample and accumulate SQI readings for each pair (currently only one).
> > +	 *
> > +	 * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
> > +	 * - The SQI register is updated every 2 µs.
> > +	 * - Values may fluctuate significantly, even in low-noise environments.
> > +	 * - For reliable estimation, average a minimum of 30–50 samples
> > +	 *   (recommended for noisy environments)
> > +	 * - In noisy environments, individual readings are highly unreliable.
> > +	 *
> > +	 * We use 40 samples per pair with a delay of 3 µs between each
> > +	 * read to ensure new values are captured (2 µs update interval).
> > +	 */
> > +	for (i = 0; i < KSZ9477_SQI_SAMPLE_COUNT; i++) {
> > +		val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
> > +				   KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A);
> > +		if (val < 0)
> > +			return val;
> > +
> > +		raw_sqi = FIELD_GET(KSZ9477_MMD_SQI_MASK, val);
> > +		sum += raw_sqi;
> > +
> > +		udelay(KSZ9477_MMD_SQI_READ_DELAY_US);
> 
> This ends up spending a sizable amount of time just spinning the CPU to
> collect the samples for the averaging. Given that only very low values
> seem to indicate a working link, I wonder how significant the
> fluctuations in reported link quality are in reality. Is it really
> worth spending 120us of CPU time to average those values?
> 
> Maybe a running average updated with a new sample each time this
> function is called would be sufficient?

Hm. Good point. I'l try it. We already have proper interface for this
case :)

Best Regards,
Oleksij
-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH net-next v2 1/1] phy: micrel: add Signal Quality Indicator (SQI) support for KSZ9477 switch PHYs
  2025-06-26  5:11   ` Oleksij Rempel
@ 2025-06-26 12:20     ` Oleksij Rempel
  0 siblings, 0 replies; 7+ messages in thread
From: Oleksij Rempel @ 2025-06-26 12:20 UTC (permalink / raw)
  To: Lucas Stach
  Cc: Andrew Lunn, Heiner Kallweit, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, netdev, linux-kernel, kernel,
	Russell King, Maxime Chevallier

On Thu, Jun 26, 2025 at 07:11:38AM +0200, Oleksij Rempel wrote:
> On Wed, Jun 25, 2025 at 08:06:32PM +0200, Lucas Stach wrote:
> > Hi Oleksij,
> > 
> > Am Mittwoch, dem 25.06.2025 um 14:41 +0200 schrieb Oleksij Rempel:
> > > Add support for the Signal Quality Index (SQI) feature on KSZ9477 family
> > > switches. This feature provides a relative measure of receive signal
> > > quality.
> > > 
> > > The KSZ9477 PHY provides four separate SQI values for a 1000BASE-T link,
> > > one for each differential pair (Channel A-D). Since the current get_sqi
> > > UAPI only supports returning a single value per port, this
> > > implementation reads the SQI from Channel A as a representative metric.
> > 
> > I wonder if it wouldn't be more useful to report the worst SQI from all
> > the channels instead.
> 
> It was my first idea too, just to report the worst SQI from all
> channels. But this makes it impossible to report SQI for each pair
> later. If we ever want to support SQI per pair, the current code would
> suddenly start to show only SQI for pair A, not the worst one, so the
> SQI interface would change meaning without warning.
> 
> There is another problem if we want to extend the SQI UAPI for per-pair
> support: with 100Mbit/s links, we can't know which pair is used. The PHY
> reports SQI only for the RX pair, which can change depending on MDI-X
> resolution, and with auto MDI-X mode, this PHY doesn't tell us which
> pair it is.
> 
> That means, at this point, we have hardware which in some modes can't
> provide pair-related information. So, it is better to keep the already
> existing UAPI explicitly per link instead of per pair. This matches the
> current hardware limits and avoids confusion for users and developers.
> If we want per-pair SQI in the future, the API must handle these cases
> clearly.

...

> > This ends up spending a sizable amount of time just spinning the CPU to
> > collect the samples for the averaging. Given that only very low values
> > seem to indicate a working link, I wonder how significant the
> > fluctuations in reported link quality are in reality. Is it really
> > worth spending 120us of CPU time to average those values?
> > 
> > Maybe a running average updated with a new sample each time this
> > function is called would be sufficient?
> 
> Hm. Good point. I'l try it. We already have proper interface for this
> case :)

After some more testing with a signal generator, I started to doubt the
usability of our SQI hardware implementation in this case.

The problem is: the signal issue can only be detected if data transfer is
ongoing - more specifically, if data is being received (for example, when
running iperf). The SQI register on this hardware is updated every 3 µs. This
means if any data is received within this window, we can detect noise on the
wire. But if there is no transfer, the SQI register always shows perfect link
quality.

On the other hand, as long as noise or a sine wave is injected into the twisted
pair, it is possible to see bandwidth drops in iperf, but no other error
counters indicate problems. Even the RxErr counter on the PHY side stays silent
(it seems to detect only other kinds of errors). If SQI is actively polled
during this time, it will show a worse value - so in general, SQI seems to be
usable.

At an early stage of the SQI implementation, I didn’t have a strong opinion
about the need to differentiate these interfaces. But now, based on practical
experience, I see that the difference is very important.

It looks like we have two types of SQI hardware implementations:

- Hardware which provides the worst value since last read

- Hardware which automatically updates the value every N microseconds

- Hardware which provides both values

Both types are recommended by the Open Alliance as:

- "worst case SQI value since last read"

- "current SQI value"

My question is: do we really need both interfaces? The "current SQI value"
seems impractical if it only reflects quality during active data transfer.

What do you think?

Best Regards,
Oleksij
--- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2025-06-26 12:21 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-25 12:41 [PATCH net-next v2 1/1] phy: micrel: add Signal Quality Indicator (SQI) support for KSZ9477 switch PHYs Oleksij Rempel
2025-06-25 15:33 ` Maxime Chevallier
2025-06-25 16:36   ` Oleksij Rempel
2025-06-25 18:06 ` Lucas Stach
2025-06-26  5:11   ` Oleksij Rempel
2025-06-26 12:20     ` Oleksij Rempel
2025-06-26  0:07 ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).