[PATCH net v3 2/6] net: stmmac: selftests: Validate EEE based on the actual LPI timer value
From: Maxime Chevallier
Date: Fri Sep 11 2026 - 17:24:08 EST
The EEE selftest is a 2-step test :
- It validates that we enter in LPI mode with the
irq_tx_path_in_lpi_mode_n counter
- It then validates that we exit LPI when sending a frame, with the
irq_tx_path_exit_lpi_mode_n counter.
The current state of the test lacks 2 main things :
- We don't know exactly when was the previous frame sent (it's from the
previous selftest)
- The timeout is hardcoded, while the LPI is entered after a
user-configurable delay. On top of that, the timeout loop uses a
pre-decrement iterator (--retries) that actually only iterate nine
times, so 900ms while the default LPI value is 1 second.
Let's therefore make it more deterministic :
- Send a frame at the beginning of the test
- Wait for more than the lpi timer value, we timeout after about twice
the value,
- Then send another frame, and verify that we do go out of LPI, also
with a timeout.
As LPI timer can get pretty high, bail out if LPI timer is over 5
seconds.
Note that the test's goal isn't to validate the LPI timer value itself,
only that we enter/leave LPI mode.
Fixes: 091810dbded9 ("net: stmmac: Introduce selftests support")
Reviewed-by: Nicolai Buchwitz <nb@xxxxxxxxxxx>
Signed-off-by: Maxime Chevallier <maxime.chevallier@xxxxxxxxxxx>
---
.../stmicro/stmmac/stmmac_selftests.c | 44 ++++++++++++++++---
1 file changed, 37 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
index 6503678b646e..df28741abf2b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
@@ -30,6 +30,7 @@ struct stmmachdr {
sizeof(struct stmmachdr))
#define STMMAC_TEST_PKT_MAGIC 0xdeadcafecafedeadULL
#define STMMAC_LB_TIMEOUT msecs_to_jiffies(200)
+#define STMMAC_SFT_MAX_LPI (5 * USEC_PER_SEC)
struct stmmac_packet_attrs {
int vlan;
@@ -462,12 +463,16 @@ static int stmmac_test_mmc(struct stmmac_priv *priv)
static int stmmac_test_eee(struct stmmac_priv *priv)
{
struct stmmac_extra_stats *initial, *final;
- int retries = 10;
+ unsigned long timeout, max_duration;
int ret;
if (!priv->dma_cap.eee || !priv->eee_active)
return -EOPNOTSUPP;
+ /* Bail out if the configured LPI timer is too long */
+ if (priv->tx_lpi_timer > STMMAC_SFT_MAX_LPI)
+ return -EOPNOTSUPP;
+
initial = kzalloc_obj(*initial);
if (!initial)
return -ENOMEM;
@@ -478,14 +483,21 @@ static int stmmac_test_eee(struct stmmac_priv *priv)
goto out_free_initial;
}
+ /* Snapshot stats, we want to count the in_lpi events. We may enter
+ * LPI just after the packet was sent.
+ */
memcpy(initial, &priv->xstats, sizeof(*initial));
+ /* Send a frame, then wait to enter LPI */
ret = stmmac_test_mac_loopback(priv);
if (ret)
goto out_free_final;
+ max_duration = usecs_to_jiffies(2 * priv->tx_lpi_timer);
+
/* We have no traffic in the line so, sooner or later it will go LPI */
- while (--retries) {
+ timeout = jiffies + max_duration;
+ while (!time_after(jiffies, timeout)) {
memcpy(final, &priv->xstats, sizeof(*final));
if (final->irq_tx_path_in_lpi_mode_n >
@@ -494,20 +506,38 @@ static int stmmac_test_eee(struct stmmac_priv *priv)
msleep(100);
}
- if (!retries) {
+ memcpy(final, &priv->xstats, sizeof(*final));
+ if (final->irq_tx_path_in_lpi_mode_n <=
+ initial->irq_tx_path_in_lpi_mode_n) {
ret = -ETIMEDOUT;
goto out_free_final;
}
- if (final->irq_tx_path_in_lpi_mode_n <=
- initial->irq_tx_path_in_lpi_mode_n) {
- ret = -EINVAL;
+ /* Re-snapshot, as we want to measure exit_lpi events. We should be
+ * in LPI right now.
+ */
+ memcpy(initial, &priv->xstats, sizeof(*initial));
+
+ /* TX something so we go out of LPI */
+ ret = stmmac_test_mac_loopback(priv);
+ if (ret)
goto out_free_final;
+
+ /* Wait for the exit LPI interrupt */
+ timeout = jiffies + max_duration;
+ while (!time_after(jiffies, timeout)) {
+ memcpy(final, &priv->xstats, sizeof(*final));
+
+ if (final->irq_tx_path_exit_lpi_mode_n >
+ initial->irq_tx_path_exit_lpi_mode_n)
+ break;
+ msleep(100);
}
+ memcpy(final, &priv->xstats, sizeof(*final));
if (final->irq_tx_path_exit_lpi_mode_n <=
initial->irq_tx_path_exit_lpi_mode_n) {
- ret = -EINVAL;
+ ret = -ETIMEDOUT;
goto out_free_final;
}
--
2.55.0