* [PATCH net-next v7 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix
@ 2026-10-04 12:05 Zxyan Zhu
2026-10-04 12:05 ` [PATCH net-next v7 1/3] net: stmmac: dwmac-socfpga: complete cross-timestamp on ATSNS Zxyan Zhu
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Zxyan Zhu @ 2026-10-04 12:05 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni
Cc: mcoquelin.stm32, alexandre.torgue, richardcochran,
maxime.chevallier, muhammad.nazim.amirul.nazle.asmade,
rohan.g.thomas, netdev, linux-stm32, linux-arm-kernel,
linux-kernel, Zxyan Zhu
This series adds auxiliary snapshot (EXTTS) interrupt support to
DWXGMAC2/DWXLGMAC2, fixes a stale TSIS race on the Agilex5
cross-timestamp path that the new handler would otherwise expose, and
guards the shared aux snapshot handler against a zero channel mask.
Patch 1 makes smtg_crosststamp() complete on the persistent ATSNS
count instead of the transient TSIS bit, waits for the ATSFC FIFO
clear to complete, and holds aux_ts_lock across the whole
trigger/poll/drain sequence so a concurrent PTP_CLK_REQ_EXTTS
request cannot flush the snapshot FIFO mid-flight.
Patch 2 guards the shared aux snapshot handler against a zero
PTP_ACR channel mask: ilog2() is applied to the mask without
checking for zero, and ilog2(0) yields an out-of-range event index
that ptp_clock_event() feeds to test_bit() unchecked from hard IRQ
context.
Patch 3 wires up a dedicated DWXGMAC2 timestamp interrupt handler,
following the guarded pattern of the shared one. Before this change
the XGMAC hwif entries used the generic stmmac_ptp ops, whose
timestamp_interrupt callback read the dwmac4 offset
GMAC_TIMESTAMP_STATUS (0xb20) instead of the XGMAC register at 0xd20.
The PTP clock advertised the aux snapshot channels, so
PTP_EXTTS_REQUEST succeeded but no event was ever delivered.
Following 30300d9f9150 ("net: stmmac: xgmac: Disable the Timestamp
interrupt by default"), XGMAC_TSIE is not added back to
XGMAC_INT_DEFAULT_EN. Instead it is armed on demand from the
PTP_CLK_REQ_EXTTS enable/disable path via a new optional
timestamp_interrupt_cfg mac callback (mirroring dwmac1000). The
interrupt is only touched after the ATSFC FIFO clear has completed,
and the handler refuses to drain entries while that clear is still
in flight; it also leaves the snapshot FIFO alone while an internal
cross-timestamp owns it (STMMAC_FLAG_INT_SNAPSHOT_EN), is disarmed
when the PTP clock is unregistered, and is re-armed on resume by
stmmac_rearm_timestamp_irq(), which redoes the EXTTS programming
under aux_ts_lock before re-arming, when a channel was left enabled
across suspend.
v1: https://lore.kernel.org/netdev/20260806-dwxgmac2-timestamp-irq-v1-1-c051c79c9d90@gmail.com/
v2: https://lore.kernel.org/netdev/20260810100221.9166-1-zxyan0222@gmail.com/
v3: https://lore.kernel.org/netdev/20260818132722.1852876-1-zxyan0222@gmail.com/
v4: https://lore.kernel.org/netdev/20260902131441.322167-1-zxyan0222@gmail.com/
v5: https://lore.kernel.org/netdev/20260910081020.86227-1-zxyan0222@gmail.com/
v6: https://lore.kernel.org/netdev/20260929073553.4136336-1-zxyan0222@gmail.com/
v7:
- Rebase onto the current net-next/main: stmmac_resume() now only
re-initialises timestamping under priv->ptp_enabled (8181678a92f0),
so stmmac_rearm_timestamp_irq() moved inside that block.
- Raise and drop STMMAC_FLAG_INT_SNAPSHOT_EN under aux_ts_lock in
smtg_crosststamp(), so concurrent cross-timestamp requests cannot
lose the flag.
- Restore the enabled auxiliary snapshot trigger on resume: the
platform init resets the MAC, so stmmac_rearm_timestamp_irq() now
flushes the FIFO and re-programs the PTP_ACR ATSEN bit (channel
recorded by stmmac_enable()) before re-arming the interrupt.
Zxyan Zhu (3):
net: stmmac: dwmac-socfpga: complete cross-timestamp on ATSNS
net: stmmac: guard against a zero channel in the aux snapshot handler
net: stmmac: dwxgmac2: add XGMAC timestamp interrupt support
.../ethernet/stmicro/stmmac/dwmac-socfpga.c | 36 ++++++++---
.../ethernet/stmicro/stmmac/dwxgmac2_core.c | 59 +++++++++++++++++++
drivers/net/ethernet/stmicro/stmmac/hwif.c | 4 +-
drivers/net/ethernet/stmicro/stmmac/hwif.h | 5 ++
.../ethernet/stmicro/stmmac/stmmac_hwtstamp.c | 17 +++++-
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 51 ++++++++++++++++
.../net/ethernet/stmicro/stmmac/stmmac_ptp.c | 23 +++++++-
.../net/ethernet/stmicro/stmmac/stmmac_ptp.h | 2 +
include/linux/stmmac.h | 1 +
9 files changed, 187 insertions(+), 11 deletions(-)
base-commit: 62d7b9186cad08324d6b9d262631104bb215e67d
--
2.34.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next v7 1/3] net: stmmac: dwmac-socfpga: complete cross-timestamp on ATSNS
2026-10-04 12:05 [PATCH net-next v7 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix Zxyan Zhu
@ 2026-10-04 12:05 ` Zxyan Zhu
2026-10-04 12:05 ` [PATCH net-next v7 2/3] net: stmmac: guard against a zero channel in the aux snapshot handler Zxyan Zhu
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: Zxyan Zhu @ 2026-10-04 12:05 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni
Cc: mcoquelin.stm32, alexandre.torgue, richardcochran,
maxime.chevallier, muhammad.nazim.amirul.nazle.asmade,
rohan.g.thomas, netdev, linux-stm32, linux-arm-kernel,
linux-kernel, Zxyan Zhu
The Agilex5 smtg_crosststamp() handler arms an internal auxiliary
snapshot, toggles GPO0 and then polls XGMAC_INT_STATUS for TSIS to
learn that the snapshot is ready. TSIS is a transient, read-to-clear
status bit: it is set by any MAC timestamp event and cleared the moment
XGMAC_TIMESTAMP_STATUS is read.
That makes the TSIS poll racy in two ways. A stale TSIS latched by an
unrelated event satisfies the poll immediately, before the auxiliary
snapshot is latched, so the FIFO comes back empty and *device is never
written even though the call returns 0. Conversely a concurrent reader
of XGMAC_TIMESTAMP_STATUS, such as the TX timestamp completion path, can
clear TSIS while the poll is waiting and make it time out with "Wait for
time sync operation timeout".
The auxiliary snapshot FIFO level is also reported by the ATSNS count
in XGMAC_TIMESTAMP_STATUS. Reading XGMAC_TIMESTAMP_STATUS does not
affect ATSNS, so the destructive reads above cannot disturb it. Poll
ATSNS instead of TSIS, and wait for the PTP_ACR_ATSFC FIFO clear to
complete first so a stale ATSNS from a previous snapshot cannot satisfy
the poll before the new snapshot is latched.
Hold aux_ts_lock across the whole sequence instead of dropping it
right after arming, so a concurrent PTP_CLK_REQ_EXTTS request cannot
set PTP_ACR_ATSFC and flush the FIFO between the poll and the drain
loop, which would leave *device filled from an empty FIFO.
Fixes: fd8c4f645496 ("net: stmmac: socfpga: Add hardware supported cross-timestamp")
Signed-off-by: Zxyan Zhu <zxyan0222@gmail.com>
---
.../ethernet/stmicro/stmmac/dwmac-socfpga.c | 31 ++++++++++++++-----
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
index 1d7f0a57d288..c5f71bfc7cf4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
@@ -337,8 +337,18 @@ static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
acr_value = readl(ptpaddr + PTP_ACR);
acr_value |= PTP_ACR_ATSFC;
writel(acr_value, ptpaddr + PTP_ACR);
- /* Release the mutex */
- mutex_unlock(&priv->aux_ts_lock);
+
+ /* Wait for the FIFO clear to complete, so the poll below only
+ * observes snapshots latched by this trigger.
+ */
+ ret = readl_poll_timeout(ptpaddr + PTP_ACR, acr_value,
+ !(acr_value & PTP_ACR_ATSFC), 10, 10000);
+ if (ret) {
+ mutex_unlock(&priv->aux_ts_lock);
+ netdev_err(priv->dev, "%s: Failed to clear snapshot FIFO\n",
+ __func__);
+ return ret;
+ }
/* Trigger Internal snapshot signal. Create a rising edge by just toggle
* the GPO0 to low and back to high.
@@ -349,10 +359,16 @@ static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
gpio_value |= XGMAC_GPIO_GPO0;
writel(gpio_value, ioaddr + XGMAC_GPIO_STATUS);
- /* Poll for time sync operation done */
- ret = readl_poll_timeout(priv->ioaddr + XGMAC_INT_STATUS, v,
- (v & XGMAC_INT_TSIS), 100, 10000);
+ /* Wait for the auxiliary snapshot to be latched: the ATSNS count
+ * is the FIFO level and is not affected by reading
+ * XGMAC_TIMESTAMP_STATUS, so concurrent readers cannot disturb
+ * the poll.
+ */
+ ret = readl_poll_timeout(ioaddr + XGMAC_TIMESTAMP_STATUS, v,
+ FIELD_GET(XGMAC_TIMESTAMP_ATSNS_MASK, v),
+ 100, 10000);
if (ret) {
+ mutex_unlock(&priv->aux_ts_lock);
netdev_err(priv->dev, "%s: Wait for time sync operation timeout\n",
__func__);
return ret;
@@ -364,8 +380,7 @@ static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
.use_nsecs = false,
};
- num_snapshot = FIELD_GET(XGMAC_TIMESTAMP_ATSNS_MASK,
- readl(ioaddr + XGMAC_TIMESTAMP_STATUS));
+ num_snapshot = FIELD_GET(XGMAC_TIMESTAMP_ATSNS_MASK, v);
/* Repeat until the timestamps are from the FIFO last segment */
for (i = 0; i < num_snapshot; i++) {
@@ -375,6 +390,8 @@ static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
read_unlock_irqrestore(&priv->ptp_lock, flags);
}
+ mutex_unlock(&priv->aux_ts_lock);
+
get_smtgtime(priv->mii, SMTG_MDIO_ADDR, &smtg_time);
system->cycles = smtg_time;
--
2.34.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next v7 2/3] net: stmmac: guard against a zero channel in the aux snapshot handler
2026-10-04 12:05 [PATCH net-next v7 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix Zxyan Zhu
2026-10-04 12:05 ` [PATCH net-next v7 1/3] net: stmmac: dwmac-socfpga: complete cross-timestamp on ATSNS Zxyan Zhu
@ 2026-10-04 12:05 ` Zxyan Zhu
2026-10-04 12:05 ` [PATCH net-next v7 3/3] net: stmmac: dwxgmac2: add XGMAC timestamp interrupt support Zxyan Zhu
2026-10-04 12:08 ` [PATCH net-next v7 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix netdev-bot+sinfo
3 siblings, 0 replies; 7+ messages in thread
From: Zxyan Zhu @ 2026-10-04 12:05 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni
Cc: mcoquelin.stm32, alexandre.torgue, richardcochran,
maxime.chevallier, muhammad.nazim.amirul.nazle.asmade,
rohan.g.thomas, netdev, linux-stm32, linux-arm-kernel,
linux-kernel, Zxyan Zhu, stable
The generic timestamp_interrupt() handler derives the EXTTS channel
index with ilog2() applied directly to the PTP_ACR channel mask, with
no check for a zero mask. ilog2(0) yields -1, which ends up in
event.index as 0xffffffff, and ptp_clock_event() uses that index in
test_bit() against a PTP_MAX_CHANNELS bitmap without range validation,
reading far past the allocation from hard IRQ context.
The zero-mask window is reachable: stmmac_enable() sets
STMMAC_FLAG_EXT_SNAPSHOT_EN before it programs PTP_ACR, so an EXTTS
interrupt arriving in between passes the flag check while the mask is
still clear (snapshots can also linger in the FIFO from a previous
enable, as only PTP_ACR_ATSFC clears them).
Check the mask before applying the ilog2().
Fixes: 8851346912a1 ("net: stmmac: Assign configured channel value to EXTTS event")
Cc: stable@vger.kernel.org
Signed-off-by: Zxyan Zhu <zxyan0222@gmail.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
index b9a985fa772c..2a076e228e9a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
@@ -242,7 +242,10 @@ static void timestamp_interrupt(struct stmmac_priv *priv)
GMAC_TIMESTAMP_ATSNS_SHIFT;
acr_value = readl(priv->ptpaddr + PTP_ACR);
- channel = ilog2(FIELD_GET(PTP_ACR_MASK, acr_value));
+ channel = FIELD_GET(PTP_ACR_MASK, acr_value);
+ if (!channel)
+ return;
+ channel = ilog2(channel);
for (i = 0; i < num_snapshot; i++) {
read_lock_irqsave(&priv->ptp_lock, flags);
--
2.34.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next v7 3/3] net: stmmac: dwxgmac2: add XGMAC timestamp interrupt support
2026-10-04 12:05 [PATCH net-next v7 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix Zxyan Zhu
2026-10-04 12:05 ` [PATCH net-next v7 1/3] net: stmmac: dwmac-socfpga: complete cross-timestamp on ATSNS Zxyan Zhu
2026-10-04 12:05 ` [PATCH net-next v7 2/3] net: stmmac: guard against a zero channel in the aux snapshot handler Zxyan Zhu
@ 2026-10-04 12:05 ` Zxyan Zhu
2026-10-05 12:17 ` netdev-bot+sashiko
2026-10-04 12:08 ` [PATCH net-next v7 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix netdev-bot+sinfo
3 siblings, 1 reply; 7+ messages in thread
From: Zxyan Zhu @ 2026-10-04 12:05 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni
Cc: mcoquelin.stm32, alexandre.torgue, richardcochran,
maxime.chevallier, muhammad.nazim.amirul.nazle.asmade,
rohan.g.thomas, netdev, linux-stm32, linux-arm-kernel,
linux-kernel, Zxyan Zhu
The DWXGMAC2 hwif entries use the generic stmmac_ptp hwtimestamp ops,
whose timestamp_interrupt callback reads the dwmac4 offset
GMAC_TIMESTAMP_STATUS (0xb20) instead of the XGMAC register at 0xd20,
and whose interrupt enable mask XGMAC_INT_DEFAULT_EN omits XGMAC_TSIE.
The PTP clock nevertheless advertises the auxiliary snapshot channels
on XGMAC, so PTP_EXTTS_REQUEST succeeds but no PTP_CLOCK_EXTTS event is
ever delivered.
Fix this with a dedicated DWXGMAC2 timestamp interrupt handler that
reads XGMAC_TIMESTAMP_STATUS and reports the pending auxiliary
snapshots as PTP_CLOCK_EXTTS events. The handler deliberately does
not gate its status read on XGMAC_INT_TSIS: TSIS is an aggregate,
read-to-clear bit that the TX timestamp completion path (which polls
the same register for TXTSC) clears before the handler can observe
it, while ATSNS and the snapshot FIFO still hold the aux events. A
dwmac4-style TSIS gate would silently drop them.
XGMAC_TSIE is not added back to XGMAC_INT_DEFAULT_EN, which
30300d9f9150 ("net: stmmac: xgmac: Disable the Timestamp interrupt by
default") deliberately keeps clear. Instead it is armed on demand from
the PTP_CLK_REQ_EXTTS enable/disable path of stmmac_enable(), through a
new optional stmmac_ops->timestamp_interrupt_cfg() callback implemented
only for DWXGMAC2/DWXLGMAC2 (on top of dwxgmac2_irq_modify()), like
dwmac1000 does with dwmac1000_timestamp_interrupt_cfg(). This keeps
platforms that do not use EXTTS at their current interrupt load and
leaves the other cores untouched: stmmac_enable() is shared with
dwmac4/dwmac5, whose timestamp interrupt stays always-enabled and is
relied upon by intel_crosststamp(), so a direct irq_modify() call
there is not an option; cores that do not implement the callback keep
their current behaviour.
The interrupt is only touched after the PTP_ACR_ATSFC FIFO clear has
completed, and the handler refuses to drain entries while that clear
is still in flight, so a stale snapshot is never reported as an event;
if the clear times out, the error is returned without changing the
interrupt state. The handler also leaves the snapshot FIFO alone
while an internal cross-timestamp owns it: smtg_crosststamp() sets
STMMAC_FLAG_INT_SNAPSHOT_EN for the duration of the cross-timestamp,
raised and dropped under aux_ts_lock so concurrent requests cannot
lose it, and the handler then only clears the interrupt source,
mirroring the dwmac4 handler's treatment of intel_crosststamp().
The timestamp interrupt is also disarmed after ptp_clock_unregister(),
which drops STMMAC_FLAG_EXT_SNAPSHOT_EN as well.
On resume stmmac_rearm_timestamp_irq() redoes the EXTTS programming
stmmac_enable() performed on XGMAC: stmmac_hw_setup() reprograms
XGMAC_INT_EN from XGMAC_INT_DEFAULT_EN, which drops XGMAC_TSIE, and a
platform init callback may have reset the MAC and dropped the PTP_ACR
trigger as well. It flushes the FIFO and re-programs the enabled
auxiliary snapshot trigger (recorded in ext_snapshot_num by
stmmac_enable()) under aux_ts_lock, re-validates the channel there,
and re-arms the interrupt only if the flush completed.
Fixes: 4bb7aff9e6d0 ("net: stmmac: Add PTP support for XGMAC2")
Signed-off-by: Zxyan Zhu <zxyan0222@gmail.com>
---
.../ethernet/stmicro/stmmac/dwmac-socfpga.c | 5 ++
.../ethernet/stmicro/stmmac/dwxgmac2_core.c | 59 +++++++++++++++++++
drivers/net/ethernet/stmicro/stmmac/hwif.c | 4 +-
drivers/net/ethernet/stmicro/stmmac/hwif.h | 5 ++
.../ethernet/stmicro/stmmac/stmmac_hwtstamp.c | 12 ++++
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 51 ++++++++++++++++
.../net/ethernet/stmicro/stmmac/stmmac_ptp.c | 23 +++++++-
.../net/ethernet/stmicro/stmmac/stmmac_ptp.h | 2 +
include/linux/stmmac.h | 1 +
9 files changed, 159 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
index c5f71bfc7cf4..9030cc1cc677 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
@@ -311,6 +311,7 @@ static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
return -EBUSY;
mutex_lock(&priv->aux_ts_lock);
+ priv->plat->flags |= STMMAC_FLAG_INT_SNAPSHOT_EN;
/* Enable Internal snapshot trigger */
acr_value = readl(ptpaddr + PTP_ACR);
acr_value &= ~PTP_ACR_MASK;
@@ -328,6 +329,7 @@ static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
acr_value |= PTP_ACR_ATSEN3;
break;
default:
+ priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
mutex_unlock(&priv->aux_ts_lock);
return -EINVAL;
}
@@ -344,6 +346,7 @@ static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
ret = readl_poll_timeout(ptpaddr + PTP_ACR, acr_value,
!(acr_value & PTP_ACR_ATSFC), 10, 10000);
if (ret) {
+ priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
mutex_unlock(&priv->aux_ts_lock);
netdev_err(priv->dev, "%s: Failed to clear snapshot FIFO\n",
__func__);
@@ -368,6 +371,7 @@ static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
FIELD_GET(XGMAC_TIMESTAMP_ATSNS_MASK, v),
100, 10000);
if (ret) {
+ priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
mutex_unlock(&priv->aux_ts_lock);
netdev_err(priv->dev, "%s: Wait for time sync operation timeout\n",
__func__);
@@ -390,6 +394,7 @@ static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
read_unlock_irqrestore(&priv->ptp_lock, flags);
}
+ priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
mutex_unlock(&priv->aux_ts_lock);
get_smtgtime(priv->mii, SMTG_MDIO_ADDR, &smtg_time);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
index 1a88cbaed70c..313c24e9a49d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
@@ -42,6 +42,12 @@ static void dwxgmac2_irq_modify(struct mac_device_info *hw, u32 disable,
spin_unlock_irqrestore(&hw->irq_ctrl_lock, flags);
}
+static void dwxgmac2_timestamp_interrupt_cfg(struct stmmac_priv *priv, bool en)
+{
+ stmmac_mac_irq_modify(priv, en ? 0 : XGMAC_TSIE,
+ en ? XGMAC_TSIE : 0);
+}
+
static void dwxgmac2_update_caps(struct stmmac_priv *priv)
{
if (!priv->dma_cap.mbps_10_100)
@@ -1154,6 +1160,57 @@ static int dwxgmac2_get_mac_tx_timestamp(struct mac_device_info *hw, u64 *ts)
return 0;
}
+void dwxgmac2_timestamp_interrupt(struct stmmac_priv *priv)
+{
+ u32 ts_status, pending_snapshots, acr_value, channel;
+ struct ptp_clock_event event;
+ unsigned long flags;
+ u64 ptp_time;
+ int i;
+
+ if (priv->plat->flags & STMMAC_FLAG_INT_SNAPSHOT_EN) {
+ /* Read the status to clear the timestamp interrupt source;
+ * the FIFO belongs to the cross-timestamp path.
+ */
+ readl(priv->ioaddr + XGMAC_TIMESTAMP_STATUS);
+ return;
+ }
+
+ /* Reading XGMAC_TIMESTAMP_STATUS clears the TSIS and AUXTSTRIG
+ * bits, so the ATSNS count is the only reliable indication of
+ * pending auxiliary snapshots. TXTSC is cleared by
+ * XGMAC_TXTIMESTAMP_SEC and is not affected by this read.
+ */
+ ts_status = readl(priv->ioaddr + XGMAC_TIMESTAMP_STATUS);
+
+ if (!(priv->plat->flags & STMMAC_FLAG_EXT_SNAPSHOT_EN) || !priv->ptp_clock)
+ return;
+
+ pending_snapshots = FIELD_GET(XGMAC_TIMESTAMP_ATSNS_MASK, ts_status);
+ if (!pending_snapshots)
+ return;
+
+ acr_value = readl(priv->ptpaddr + PTP_ACR);
+ /* Entries observed while the FIFO is being flushed are stale. */
+ if (acr_value & PTP_ACR_ATSFC)
+ return;
+ channel = FIELD_GET(PTP_ACR_MASK, acr_value);
+ if (!channel)
+ return;
+ channel = ilog2(channel);
+
+ for (i = 0; i < pending_snapshots; i++) {
+ read_lock_irqsave(&priv->ptp_lock, flags);
+ stmmac_get_ptptime(priv, priv->ptpaddr, &ptp_time);
+ read_unlock_irqrestore(&priv->ptp_lock, flags);
+
+ event.type = PTP_CLOCK_EXTTS;
+ event.index = channel;
+ event.timestamp = ptp_time;
+ ptp_clock_event(priv->ptp_clock, &event);
+ }
+}
+
static int dwxgmac2_flex_pps_config(void __iomem *ioaddr, int index,
struct stmmac_pps_cfg *cfg, bool enable,
u32 sub_second_inc, u32 systime_flags)
@@ -1413,6 +1470,7 @@ static int dwxgmac2_config_l4_filter(struct mac_device_info *hw, u32 filter_no,
const struct stmmac_ops dwxgmac210_ops = {
.core_init = dwxgmac2_core_init,
.irq_modify = dwxgmac2_irq_modify,
+ .timestamp_interrupt_cfg = dwxgmac2_timestamp_interrupt_cfg,
.update_caps = dwxgmac2_update_caps,
.set_mac = dwxgmac2_set_mac,
.rx_ipc = dwxgmac2_rx_ipc,
@@ -1468,6 +1526,7 @@ static void dwxlgmac2_rx_queue_enable(struct mac_device_info *hw, u8 mode,
const struct stmmac_ops dwxlgmac2_ops = {
.core_init = dwxgmac2_core_init,
.irq_modify = dwxgmac2_irq_modify,
+ .timestamp_interrupt_cfg = dwxgmac2_timestamp_interrupt_cfg,
.set_mac = dwxgmac2_set_mac,
.rx_ipc = dwxgmac2_rx_ipc,
.rx_queue_enable = dwxlgmac2_rx_queue_enable,
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.c b/drivers/net/ethernet/stmicro/stmmac/hwif.c
index 265671170bf6..eba87410f985 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.c
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.c
@@ -258,7 +258,7 @@ static const struct stmmac_hwif_entry {
.dma = &dwxgmac210_dma_ops,
.mac = &dwxgmac210_ops,
.vlan = &dwxgmac210_vlan_ops,
- .hwtimestamp = &stmmac_ptp,
+ .hwtimestamp = &dwxgmac2_ptp,
.ptp = &stmmac_ptp_clock_ops,
.mode = NULL,
.tc = &dwmac510_tc_ops,
@@ -280,7 +280,7 @@ static const struct stmmac_hwif_entry {
.dma = &dwxgmac210_dma_ops,
.mac = &dwxlgmac2_ops,
.vlan = &dwxlgmac2_vlan_ops,
- .hwtimestamp = &stmmac_ptp,
+ .hwtimestamp = &dwxgmac2_ptp,
.ptp = &stmmac_ptp_clock_ops,
.mode = NULL,
.tc = &dwmac510_tc_ops,
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index a8a5c8fdd5ed..14a47d68399f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -317,6 +317,8 @@ struct stmmac_ops {
void (*update_caps)(struct stmmac_priv *priv);
/* Change the interrupt enable setting. Enable takes precedence. */
void (*irq_modify)(struct mac_device_info *hw, u32 disable, u32 enable);
+ /* Arm or disarm the timestamp interrupt on demand (optional) */
+ void (*timestamp_interrupt_cfg)(struct stmmac_priv *priv, bool en);
/* Enable the MAC RX/TX */
void (*set_mac)(void __iomem *ioaddr, bool enable);
/* Enable and verify that the IPC module is supported */
@@ -420,6 +422,8 @@ struct stmmac_ops {
stmmac_do_void_callback(__priv, mac, update_caps, __priv)
#define stmmac_mac_irq_modify(__priv, __args...) \
stmmac_do_void_callback(__priv, mac, irq_modify, (__priv)->hw, __args)
+#define stmmac_mac_timestamp_interrupt_cfg(__priv, __args...) \
+ stmmac_do_void_callback(__priv, mac, timestamp_interrupt_cfg, __priv, __args)
#define stmmac_mac_set(__priv, __args...) \
stmmac_do_void_callback(__priv, mac, set_mac, __args)
#define stmmac_rx_ipc(__priv, __args...) \
@@ -672,6 +676,7 @@ extern const struct stmmac_desc_ops ndesc_ops;
extern const struct stmmac_hwtimestamp stmmac_ptp;
extern const struct stmmac_hwtimestamp dwmac1000_ptp;
+extern const struct stmmac_hwtimestamp dwxgmac2_ptp;
extern const struct stmmac_mode_ops ring_mode_ops;
extern const struct stmmac_mode_ops chain_mode_ops;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
index 2a076e228e9a..4b906bc33d26 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
@@ -280,3 +280,15 @@ const struct stmmac_hwtimestamp dwmac1000_ptp = {
.get_ptptime = dwmac1000_get_ptptime,
.timestamp_interrupt = dwmac1000_timestamp_interrupt,
};
+
+const struct stmmac_hwtimestamp dwxgmac2_ptp = {
+ .config_hw_tstamping = config_hw_tstamping,
+ .init_systime = init_systime,
+ .config_sub_second_increment = config_sub_second_increment,
+ .config_addend = config_addend,
+ .adjust_systime = adjust_systime,
+ .get_systime = get_systime,
+ .get_ptptime = get_ptptime,
+ .timestamp_interrupt = dwxgmac2_timestamp_interrupt,
+ .hwtstamp_correct_latency = hwtstamp_correct_latency,
+};
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 3ad9252bf6ae..53aaf2716c46 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -18,6 +18,7 @@
#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
+#include <linux/iopoll.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
@@ -894,6 +895,54 @@ static int stmmac_init_ptp_clk_freq(struct stmmac_priv *priv)
return 0;
}
+/**
+ * stmmac_rearm_timestamp_irq - re-arm the timestamp interrupt
+ * @priv: driver private structure
+ * Description: this re-arms the on-demand timestamp interrupt if an
+ * auxiliary snapshot channel was left enabled.
+ */
+static void stmmac_rearm_timestamp_irq(struct stmmac_priv *priv)
+{
+ u32 acr_value;
+ int ret, num;
+
+ if (!(priv->plat->flags & STMMAC_FLAG_EXT_SNAPSHOT_EN) ||
+ !priv->ptp_clock)
+ return;
+
+ /* Flush the FIFO, re-program the enabled auxiliary snapshot
+ * trigger and re-arm the interrupt only if the flush completed.
+ */
+ if (priv->plat->core_type != DWMAC_CORE_XGMAC)
+ return;
+
+ mutex_lock(&priv->aux_ts_lock);
+ /* Snapshot the channel under the lock: a concurrent disable may
+ * have dropped it since the gate check above.
+ */
+ num = priv->plat->ext_snapshot_num;
+ if (num < 0) {
+ mutex_unlock(&priv->aux_ts_lock);
+ return;
+ }
+ acr_value = readl(priv->ptpaddr + PTP_ACR);
+ acr_value &= ~PTP_ACR_MASK;
+ acr_value |= PTP_ACR_ATSFC;
+ writel(acr_value, priv->ptpaddr + PTP_ACR);
+ ret = readl_poll_timeout(priv->ptpaddr + PTP_ACR, acr_value,
+ !(acr_value & PTP_ACR_ATSFC), 10, 10000);
+ if (!ret) {
+ acr_value |= PTP_ACR_ATSEN(num);
+ writel(acr_value, priv->ptpaddr + PTP_ACR);
+ stmmac_mac_timestamp_interrupt_cfg(priv, true);
+ } else {
+ netdev_err(priv->dev,
+ "%s: Failed to restore auxiliary snapshot channel\n",
+ __func__);
+ }
+ mutex_unlock(&priv->aux_ts_lock);
+}
+
/**
* stmmac_init_timestamping - initialise timestamping
* @priv: driver private structure
@@ -8418,6 +8467,8 @@ int stmmac_resume(struct device *dev)
ret = stmmac_init_timestamping(priv);
if (ret)
goto error_stop_dma;
+
+ stmmac_rearm_timestamp_irq(priv);
}
init_coalesce:
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
index 3bfcc9760dce..19fa79839e46 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
@@ -239,13 +239,20 @@ static int stmmac_enable(struct ptp_clock_info *ptp,
return -EBUSY;
}
+ if (rq->extts.index >= PTP_ACR_ATSEN_NUM) {
+ mutex_unlock(&priv->aux_ts_lock);
+ return -EINVAL;
+ }
+
priv->plat->flags |= STMMAC_FLAG_EXT_SNAPSHOT_EN;
+ priv->plat->ext_snapshot_num = rq->extts.index;
/* Enable External snapshot trigger */
acr_value |= PTP_ACR_ATSEN(rq->extts.index);
acr_value |= PTP_ACR_ATSFC;
} else {
priv->plat->flags &= ~STMMAC_FLAG_EXT_SNAPSHOT_EN;
+ priv->plat->ext_snapshot_num = -1;
}
netdev_dbg(priv->dev, "Auxiliary Snapshot %d %s.\n",
rq->extts.index, on ? "enabled" : "disabled");
@@ -255,6 +262,17 @@ static int stmmac_enable(struct ptp_clock_info *ptp,
ret = readl_poll_timeout(ptpaddr + PTP_ACR, acr_value,
!(acr_value & PTP_ACR_ATSFC),
10, 10000);
+ /* Arm or disarm the timestamp interrupt only once the FIFO
+ * clear has completed, so the handler does not observe a
+ * snapshot that the clear is about to discard.
+ */
+ if (!ret) {
+ stmmac_mac_timestamp_interrupt_cfg(priv, on);
+ } else if (on) {
+ mutex_lock(&priv->aux_ts_lock);
+ priv->plat->ext_snapshot_num = -1;
+ mutex_unlock(&priv->aux_ts_lock);
+ }
break;
}
@@ -355,7 +373,7 @@ void stmmac_ptp_register(struct stmmac_priv *priv)
if (pps_out_num)
priv->ptp_clock_ops.n_per_out = pps_out_num;
- n_ext_ts = priv->dma_cap.aux_snapshot_n;
+ n_ext_ts = min(priv->dma_cap.aux_snapshot_n, PTP_ACR_ATSEN_NUM);
if (n_ext_ts)
priv->ptp_clock_ops.n_ext_ts = n_ext_ts;
@@ -394,6 +412,9 @@ void stmmac_ptp_unregister(struct stmmac_priv *priv)
pr_debug("Removed PTP HW clock successfully on %s\n",
priv->dev->name);
+ stmmac_mac_timestamp_interrupt_cfg(priv, false);
+ priv->plat->flags &= ~STMMAC_FLAG_EXT_SNAPSHOT_EN;
+
mutex_destroy(&priv->aux_ts_lock);
}
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h
index 3fe0e3a80e80..a2082e1299b8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h
@@ -81,6 +81,7 @@
#define PTP_ACR_ATSEN3 BIT(7) /* Auxiliary Snapshot 3 Enable */
#define PTP_ACR_ATSEN(index) (PTP_ACR_ATSEN0 << (index))
#define PTP_ACR_MASK GENMASK(7, 4) /* Aux Snapshot Mask */
+#define PTP_ACR_ATSEN_NUM 4 /* Aux Snapshot 0-3 */
#define PMC_ART_VALUE0 0x01 /* PMC_ART[15:0] timer value */
#define PMC_ART_VALUE1 0x02 /* PMC_ART[31:16] timer value */
#define PMC_ART_VALUE2 0x03 /* PMC_ART[47:32] timer value */
@@ -103,6 +104,7 @@ int dwmac1000_ptp_enable(struct ptp_clock_info *ptp,
void dwmac1000_get_ptptime(void __iomem *ptpaddr, u64 *ptp_time);
void dwmac1000_timestamp_interrupt(struct stmmac_priv *priv);
+void dwxgmac2_timestamp_interrupt(struct stmmac_priv *priv);
extern const struct ptp_clock_info stmmac_ptp_clock_ops;
extern const struct ptp_clock_info dwmac1000_ptp_clock_ops;
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index 00be2df63d22..487dd492c4b1 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -351,6 +351,7 @@ struct plat_stmmacenet_data {
u8 vlan_fail_q;
bool provide_bus_info;
int int_snapshot_num;
+ int ext_snapshot_num;
int msi_mac_vec;
int msi_wol_vec;
int msi_sfty_ce_vec;
--
2.34.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v7 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix
2026-10-04 12:05 [PATCH net-next v7 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix Zxyan Zhu
` (2 preceding siblings ...)
2026-10-04 12:05 ` [PATCH net-next v7 3/3] net: stmmac: dwxgmac2: add XGMAC timestamp interrupt support Zxyan Zhu
@ 2026-10-04 12:08 ` netdev-bot+sinfo
2026-10-04 12:22 ` zhu xin
3 siblings, 1 reply; 7+ messages in thread
From: netdev-bot+sinfo @ 2026-10-04 12:08 UTC (permalink / raw)
To: Zxyan Zhu
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32,
alexandre.torgue, richardcochran, maxime.chevallier,
muhammad.nazim.amirul.nazle.asmade, rohan.g.thomas, netdev,
linux-stm32, linux-arm-kernel, linux-kernel
Hi!
This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:
- How the issue was discovered, e.g. hit in production, hit during
development, syzbot report, manual code inspection, LLM or static
analysis tool scan.
- Whether the issue was actually triggered, or is only theoretical
(e.g. found by code inspection). If it was triggered please include
the symptoms, like the stack trace or error messages.
- What hardware the change was tested on. For driver fixes please
mention the device (and if relevant firmware version) used for
testing, or say that the change was not tested on real hardware.
Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.
The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v7 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix
2026-10-04 12:08 ` [PATCH net-next v7 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix netdev-bot+sinfo
@ 2026-10-04 12:22 ` zhu xin
0 siblings, 0 replies; 7+ messages in thread
From: zhu xin @ 2026-10-04 12:22 UTC (permalink / raw)
To: netdev-bot+sinfo
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32,
alexandre.torgue, richardcochran, maxime.chevallier,
muhammad.nazim.amirul.nazle.asmade, rohan.g.thomas, netdev,
linux-stm32, linux-arm-kernel, linux-kernel
On Sun, Oct 4, 2026 at 8:08 PM <netdev-bot+sinfo@kernel.org> wrote:
>
> Hi!
>
> This is an automated message. This series looks like a fix, but its
> commit messages seem to be missing some information:
>
> - How the issue was discovered, e.g. hit in production, hit during
> development, syzbot report, manual code inspection, LLM or static
> analysis tool scan.
>
- How discovered: Patch 1 and Patch 2 were found by code inspection
during review. Patch 3 was found during development testing on a
local DWXGMAC2 board: the PTP clock advertised the aux snapshot
channels and PTP_EXTTS_REQUEST succeeded, but no PTP_CLOCK_EXTTS
event ever arrived - inspection then showed the XGMAC hwif pointed
at the generic stmmac_ptp ops, which read the dwmac4 offset instead
of the XGMAC register.
> - Whether the issue was actually triggered, or is only theoretical
> (e.g. found by code inspection). If it was triggered please include
> the symptoms, like the stack trace or error messages.
>
- Actually triggered: the EXTTS failure of Patch 3 was hit on real
hardware as described above. The races fixed by Patch 1 and the
OOB index of Patch 2 were found by inspection and have not been
observed in isolation.
> - What hardware the change was tested on. For driver fixes please
> mention the device (and if relevant firmware version) used for
> testing, or say that the change was not tested on real hardware.
>
- Tested on: local DWXGMAC2 hardware (not Agilex5), for the Patch EXTTS
issue and basic PTP operation. Agilex5 has not been tested locally.
> Please do not repost the series just to address the above. Instead,
> reply to this email with the missing information, so that reviewers
> can take it into account. If the series needs another revision for
> other reasons, please include the information in the commit messages
> then.
>
> The evaluation is done by an LLM so it may be wrong, if you think
> that is the case please reply and explain.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v7 3/3] net: stmmac: dwxgmac2: add XGMAC timestamp interrupt support
2026-10-04 12:05 ` [PATCH net-next v7 3/3] net: stmmac: dwxgmac2: add XGMAC timestamp interrupt support Zxyan Zhu
@ 2026-10-05 12:17 ` netdev-bot+sashiko
0 siblings, 0 replies; 7+ messages in thread
From: netdev-bot+sashiko @ 2026-10-05 12:17 UTC (permalink / raw)
To: zxyan0222
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32,
alexandre.torgue, richardcochran, maxime.chevallier,
muhammad.nazim.amirul.nazle.asmade, rohan.g.thomas, netdev,
linux-stm32, linux-arm-kernel, linux-kernel
Thank you for your contribution! Sashiko AI review found 5 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 5 (2) · Low: 0
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Medium] When the FIFO-clear poll times out in the EXTTS 'on' path of
stmmac_enable() (stmmac_ptp.c:262-275), the new rollback only sets…
- [Medium] Auxiliary snapshots are only drained by
dwxgmac2_timestamp_interrupt(), called from stmmac_common_interrupt()…
- [Medium] dwxgmac2_timestamp_interrupt() (dwxgmac2_core.c:1163-1212)
reads STMMAC_FLAG_INT_SNAPSHOT_EN (line 1171),…
- [Medium] In stmmac_enable(), the new XGMAC_TSIE arm/disarm call
(stmmac_ptp.c:269-270, stmmac_mac_timestamp_interrupt_cfg(priv, on))…
- [Medium] The patch arms XGMAC_TSIE on demand
(dwxgmac2_timestamp_interrupt_cfg, dwxgmac2_core.c:45-49) and re-arms…
Pre-existing issues:
- [Medium] In the PTP_CLK_REQ_EXTTS case of stmmac_enable(), line 231
clears the ATSEN bits (`acr_value &= ~PTP_ACR_MASK`) before line 234…
- [Medium] smtg_crosststamp() checks STMMAC_FLAG_EXT_SNAPSHOT_EN (dwmac-
socfpga.c:310) before it takes aux_ts_lock (line 313), and…
--
net: stmmac: dwxgmac2: add XGMAC timestamp interrupt support
This commit adds a DWXGMAC2-specific timestamp interrupt handler. The
handler reads XGMAC_TIMESTAMP_STATUS and reports pending auxiliary
snapshots as PTP_CLOCK_EXTTS events.
XGMAC_TSIE is armed on demand from the EXTTS path of stmmac_enable()
through a new timestamp_interrupt_cfg() callback. It is re-armed on
resume by stmmac_rearm_timestamp_irq(). smtg_crosststamp() now sets
STMMAC_FLAG_INT_SNAPSHOT_EN while it owns the snapshot FIFO.
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
> index c5f71bfc7cf42..9030cc1cc6778 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
> @@ -311,6 +311,7 @@ static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
> return -EBUSY;
>
> mutex_lock(&priv->aux_ts_lock);
> + priv->plat->flags |= STMMAC_FLAG_INT_SNAPSHOT_EN;
[Severity: Medium]
This is a pre-existing issue, but smtg_crosststamp() checks
STMMAC_FLAG_EXT_SNAPSHOT_EN before it takes aux_ts_lock:
if (priv->plat->flags & STMMAC_FLAG_EXT_SNAPSHOT_EN)
return -EBUSY;
mutex_lock(&priv->aux_ts_lock);
Also, the enable path of stmmac_enable() never checks
STMMAC_FLAG_INT_SNAPSHOT_EN. ptp_sys_offset_precise() does not take
pincfg_mux, so a concurrent PTP_EXTTS_REQUEST can set
STMMAC_FLAG_EXT_SNAPSHOT_EN and ATSEN(idx) inside that window.
smtg_crosststamp() then overwrites PTP_ACR with the internal channel.
Does that leave STMMAC_FLAG_EXT_SNAPSHOT_EN set, ext_snapshot_num = idx
and TSIE armed, while the user's channel is no longer enabled in
PTP_ACR? Later cross-timestamps would then return -EBUSY until the
channel is disabled and enabled again.
[ ... ]
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
> index 1a88cbaed70ca..313c24e9a49d7 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
[ ... ]
> @@ -1154,6 +1160,57 @@ static int dwxgmac2_get_mac_tx_timestamp(struct mac_device_info *hw, u64 *ts)
> return 0;
> }
>
> +void dwxgmac2_timestamp_interrupt(struct stmmac_priv *priv)
> +{
> + u32 ts_status, pending_snapshots, acr_value, channel;
> + struct ptp_clock_event event;
> + unsigned long flags;
> + u64 ptp_time;
> + int i;
> +
> + if (priv->plat->flags & STMMAC_FLAG_INT_SNAPSHOT_EN) {
> + /* Read the status to clear the timestamp interrupt source;
> + * the FIFO belongs to the cross-timestamp path.
> + */
> + readl(priv->ioaddr + XGMAC_TIMESTAMP_STATUS);
> + return;
> + }
> +
> + /* Reading XGMAC_TIMESTAMP_STATUS clears the TSIS and AUXTSTRIG
> + * bits, so the ATSNS count is the only reliable indication of
> + * pending auxiliary snapshots. TXTSC is cleared by
> + * XGMAC_TXTIMESTAMP_SEC and is not affected by this read.
> + */
> + ts_status = readl(priv->ioaddr + XGMAC_TIMESTAMP_STATUS);
[Severity: Medium]
The commit message says TSIS is "an aggregate, read-to-clear bit that
the TX timestamp completion path (which polls the same register for
TXTSC) clears before the handler can observe it". That covers the case
where this handler runs and finds TSIS already clear. What happens if
the handler never runs?
dwxgmac2_get_mac_tx_timestamp() polls the same register:
if (readl_poll_timeout_atomic(ioaddr + XGMAC_TIMESTAMP_STATUS,
value, value & XGMAC_TXTSC, 100, 10000))
Suppose one of those reads lands after an aux snapshot has set TSIS but
before the CPU takes the MAC interrupt. TSIS was the only enabled
source, so a level-triggered MAC interrupt line would deassert before
stmmac_common_interrupt() runs.
Only stmmac_common_interrupt() calls this handler, and nothing else
checks ATSNS. Would the snapshot then stay in the FIFO until some
unrelated MAC interrupt arrives? With a 1PPS source that could be about
a second later. In multi-vector/MSI mode, DMA interrupts do not go
through stmmac_common_interrupt(), so it could take much longer.
The trigger is EXTTS enabled together with TX hardware timestamping.
stmmac_get_tx_hwtstamp() falls back to stmmac_get_mac_tx_timestamp()
when the descriptor has no timestamp. Whether the interrupt is really
lost depends on how the interrupt line is triggered.
> +
> + if (!(priv->plat->flags & STMMAC_FLAG_EXT_SNAPSHOT_EN) || !priv->ptp_clock)
> + return;
> +
> + pending_snapshots = FIELD_GET(XGMAC_TIMESTAMP_ATSNS_MASK, ts_status);
> + if (!pending_snapshots)
> + return;
> +
> + acr_value = readl(priv->ptpaddr + PTP_ACR);
> + /* Entries observed while the FIFO is being flushed are stale. */
> + if (acr_value & PTP_ACR_ATSFC)
> + return;
> + channel = FIELD_GET(PTP_ACR_MASK, acr_value);
> + if (!channel)
> + return;
> + channel = ilog2(channel);
> +
> + for (i = 0; i < pending_snapshots; i++) {
> + read_lock_irqsave(&priv->ptp_lock, flags);
> + stmmac_get_ptptime(priv, priv->ptpaddr, &ptp_time);
> + read_unlock_irqrestore(&priv->ptp_lock, flags);
[Severity: Medium]
Are these checks enough to keep this loop off the FIFO? Each of
STMMAC_FLAG_INT_SNAPSHOT_EN, STMMAC_FLAG_EXT_SNAPSHOT_EN and
PTP_ACR_ATSFC is read once without a lock. The loop then drains a
pending_snapshots count that was sampled earlier.
Three process-context paths flush or consume the same FIFO:
smtg_crosststamp(), the ATSFC flush in stmmac_enable(), and
stmmac_rearm_timestamp_irq(). They serialize on aux_ts_lock, which this
handler cannot take, and none of them calls synchronize_irq().
ptp_lock does not help, because this loop and smtg_crosststamp() both
take it as readers. Arming TSIE only after the flush does not
synchronize anything either. stmmac_common_interrupt() calls this
handler on every common interrupt whatever the TSIE state.
stmmac_request_irq_single() also registers stmmac_interrupt on the
separate WoL line, so two copies of this drain can run at the same time.
Two windows look possible:
(A) The handler samples a stale ATSNS. A concurrent enable then sets
STMMAC_FLAG_EXT_SNAPSHOT_EN and its flush completes. The handler sees
EXT set and ATSFC clear, and pops entries from the flushed FIFO as
EXTTS events.
(B) The handler is already past the STMMAC_FLAG_INT_SNAPSHOT_EN check
and inside this loop when an EXTTS disable and then smtg_crosststamp()
run. The loop pops the internal cross-timestamp snapshot and reports it
as an EXTTS event, and smtg_crosststamp() gets the wrong device time.
Doesn't this contradict two claims in the commit message, that "a
stale snapshot is never reported as an event" and that the handler
"leaves the snapshot FIFO alone while an internal cross-timestamp owns
it"?
smtg_crosststamp() sets STMMAC_FLAG_INT_SNAPSHOT_EN on entry and clears
it on exit to keep the IRQ handler out, so the flag is acting as a
lock. Would a spinlock shared by the handler and the flush/drain paths,
or a synchronize_irq() after raising the flag, be more robust?
The windows are narrow on non-RT kernels. They are wider on PREEMPT_RT,
where the handler runs as a preemptible thread.
> +
> + event.type = PTP_CLOCK_EXTTS;
> + event.index = channel;
> + event.timestamp = ptp_time;
> + ptp_clock_event(priv->ptp_clock, &event);
> + }
> +}
[ ... ]
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index 3ad9252bf6aea..53aaf2716c467 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
[ ... ]
> @@ -8418,6 +8467,8 @@ int stmmac_resume(struct device *dev)
> ret = stmmac_init_timestamping(priv);
> if (ret)
> goto error_stop_dma;
> +
> + stmmac_rearm_timestamp_irq(priv);
> }
[Severity: Medium]
TSIE is re-armed here on resume. Is there a matching disarm on suspend?
With Wake-on-LAN configured, stmmac_suspend() does:
if (priv->wolopts) {
stmmac_pmt(priv, priv->hw, priv->wolopts);
priv->irq_wake = 1;
It leaves XGMAC_INT_EN and PTP_ACR as they are, and dwxgmac2_pmt() only
writes XGMAC_PMT and RX_CONFIG. Clocks stay on for WoL, so each
external trigger edge, for example a GNSS 1PPS, sets TSIS and asserts
the MAC interrupt. stmmac_common_interrupt() then does:
if (priv->irq_wake)
pm_wakeup_event(priv->device, 0);
Could this abort system suspend? Could it also wake the system after
the noirq phase when the MAC IRQ is the wake IRQ? stmmac_pci uses
pdev->irq, and stmmac_platform falls back to the MAC IRQ.
Before this patch XGMAC_TSIE was never set on XGMAC, so this would be a
new wake source. Whether the wake after noirq happens depends on how
the platform wires the wake IRQ and gates clocks.
[ ... ]
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
> index 3bfcc9760dce7..19fa79839e465 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
> @@ -239,13 +239,20 @@ static int stmmac_enable(struct ptp_clock_info *ptp,
> return -EBUSY;
> }
>
> + if (rq->extts.index >= PTP_ACR_ATSEN_NUM) {
> + mutex_unlock(&priv->aux_ts_lock);
> + return -EINVAL;
> + }
> +
> priv->plat->flags |= STMMAC_FLAG_EXT_SNAPSHOT_EN;
> + priv->plat->ext_snapshot_num = rq->extts.index;
>
> /* Enable External snapshot trigger */
> acr_value |= PTP_ACR_ATSEN(rq->extts.index);
> acr_value |= PTP_ACR_ATSFC;
> } else {
> priv->plat->flags &= ~STMMAC_FLAG_EXT_SNAPSHOT_EN;
> + priv->plat->ext_snapshot_num = -1;
> }
[Severity: Medium]
This isn't a bug introduced by this patch, but can the -EBUSY branch
above ever be taken? Earlier in this case:
acr_value = readl(ptpaddr + PTP_ACR);
channel = ilog2(FIELD_GET(PTP_ACR_MASK, acr_value));
acr_value &= ~PTP_ACR_MASK;
if (on) {
if (FIELD_GET(PTP_ACR_MASK, acr_value)) {
The ATSEN bits are cleared before they are tested, so the test is
always false.
The disable branch also ignores rq->extts.index. ptp_extts_request()
and extts_enable_store() only check index < n_ext_ts. So an enable or
disable request for one channel can silently retarget or tear down a
different active channel.
After this patch that teardown also resets ext_snapshot_num and
disarms XGMAC_TSIE.
> netdev_dbg(priv->dev, "Auxiliary Snapshot %d %s.\n",
> rq->extts.index, on ? "enabled" : "disabled");
> @@ -255,6 +262,17 @@ static int stmmac_enable(struct ptp_clock_info *ptp,
> ret = readl_poll_timeout(ptpaddr + PTP_ACR, acr_value,
> !(acr_value & PTP_ACR_ATSFC),
> 10, 10000);
> + /* Arm or disarm the timestamp interrupt only once the FIFO
> + * clear has completed, so the handler does not observe a
> + * snapshot that the clear is about to discard.
> + */
> + if (!ret) {
> + stmmac_mac_timestamp_interrupt_cfg(priv, on);
[Severity: Medium]
This call runs after aux_ts_lock was dropped just before the poll:
writel(acr_value, ptpaddr + PTP_ACR);
mutex_unlock(&priv->aux_ts_lock);
extts_enable_store() calls ops->enable() without pincfg_mux, so it can
race with the ioctl path or with another sysfs writer. Can a stale on
or off value be applied here?
disable request A enable request B
clear flag and ATSEN
mutex_unlock()
<preempted>
mutex_lock()
set flag, ATSEN|ATSFC
mutex_unlock()
poll completes
timestamp_interrupt_cfg(true)
poll completes
timestamp_interrupt_cfg(false)
That would leave the channel B enabled with XGMAC_TSIE disarmed.
The timeout branch below has the same problem. It re-takes the lock and
sets ext_snapshot_num = -1 unconditionally, which could overwrite the
channel recorded by a newer successful enable.
> + } else if (on) {
> + mutex_lock(&priv->aux_ts_lock);
> + priv->plat->ext_snapshot_num = -1;
> + mutex_unlock(&priv->aux_ts_lock);
> + }
[Severity: Medium]
When the ATSFC poll times out on enable, only ext_snapshot_num is
rolled back. STMMAC_FLAG_EXT_SNAPSHOT_EN and the PTP_ACR ATSEN(index)
bit that were set earlier stay set. Is that intended?
The readers of this state now disagree with each other:
dwxgmac2_timestamp_interrupt() gates on the flag and on PTP_ACR. It runs
from every stmmac_common_interrupt() even with TSIE disarmed. If ATSFC
clears later, it can report PTP_CLOCK_EXTTS events for a request that
returned an error.
stmmac_rearm_timestamp_irq() sees the flag set but ext_snapshot_num < 0
and skips the restore, so the behaviour changes across suspend and
resume.
smtg_crosststamp() keeps returning -EBUSY until an explicit disable or
close.
Should the failure path roll back the flag, the ATSEN bit and
ext_snapshot_num together?
> break;
> }
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261004120526.2728564-1-zxyan0222%40gmail.com
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-05 12:17 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-04 12:05 [PATCH net-next v7 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix Zxyan Zhu
2026-10-04 12:05 ` [PATCH net-next v7 1/3] net: stmmac: dwmac-socfpga: complete cross-timestamp on ATSNS Zxyan Zhu
2026-10-04 12:05 ` [PATCH net-next v7 2/3] net: stmmac: guard against a zero channel in the aux snapshot handler Zxyan Zhu
2026-10-04 12:05 ` [PATCH net-next v7 3/3] net: stmmac: dwxgmac2: add XGMAC timestamp interrupt support Zxyan Zhu
2026-10-05 12:17 ` netdev-bot+sashiko
2026-10-04 12:08 ` [PATCH net-next v7 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix netdev-bot+sinfo
2026-10-04 12:22 ` zhu xin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®