* [PATCH net-next v8 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix
@ 2026-10-13 3:32 Zxyan Zhu
2026-10-13 3:32 ` [PATCH net-next v8 1/3] net: stmmac: dwmac-socfpga: complete cross-timestamp on ATSNS Zxyan Zhu
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Zxyan Zhu @ 2026-10-13 3:32 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, maxime.chevallier
Cc: mcoquelin.stm32, alexandre.torgue, richardcochran,
muhammad.nazim.amirul.nazle.asmade, rohan.g.thomas, netdev,
linux-stm32, linux-arm-kernel, linux-kernel
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 FIFO clear to
complete, and holds aux_ts_lock across the whole trigger/poll/drain
sequence so a concurrent EXTTS request cannot flush the snapshot FIFO
mid-flight.
Patch 2 guards the shared aux snapshot handler against a zero channel
mask: ilog2() is applied to the mask without a zero check, 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 adds a dedicated DWXGMAC2 timestamp interrupt handler. The
XGMAC hwif entries used the generic ops, whose callback read the wrong
register and whose enable mask omitted the timestamp interrupt, so the
clock advertised the aux snapshot channels but no event was ever
delivered. XGMAC_TSIE is not enabled by default; it is armed on demand
from the EXTTS enable/disable path through a new callback, and the
handler only touches the FIFO while the interrupt is armed and while no
cross-timestamp owns it. The interrupt is disarmed on suspend and
unregister and re-armed on resume.
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: https://lore.kernel.org/netdev/20261004120526.2728564-1-zxyan0222@gmail.com/
v8:
- Gate the handler's FIFO drain on XGMAC_TSIE being armed; the handler
runs for every MAC interrupt and must not touch the FIFO while the
timestamp interrupt is disarmed.
- Hold aux_ts_lock across the whole EXTTS enable, including the FIFO
clear poll, so the re-arm cannot interleave with a suspend.
- Drain an in-flight handler with stmmac_sync_timestamp_irq() when the
FIFO is handed to the cross-timestamp and on the EXTTS disable path.
- Roll a failed EXTTS enable back in hardware and software on the
FIFO-clear timeout, and drop the EXTTS state when the resume re-arm
cannot restore the channel.
- Disarm XGMAC_TSIE on suspend and unregister; re-arm it on resume.
- Initialise aux_ts_lock in probe and destroy it in remove, so it is
valid for the whole device lifetime.
- Only clear the software state in the resume-failure teardown, since
the auxiliary snapshot register differs on DWMAC_CORE_MAC100/GMAC.
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net-next v8 1/3] net: stmmac: dwmac-socfpga: complete cross-timestamp on ATSNS
2026-10-13 3:32 [PATCH net-next v8 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix Zxyan Zhu
@ 2026-10-13 3:32 ` Zxyan Zhu
2026-10-13 3:32 ` [PATCH net-next v8 2/3] net: stmmac: guard against a zero channel in the aux snapshot handler Zxyan Zhu
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Zxyan Zhu @ 2026-10-13 3:32 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, maxime.chevallier
Cc: mcoquelin.stm32, alexandre.torgue, richardcochran,
muhammad.nazim.amirul.nazle.asmade, rohan.g.thomas, netdev,
linux-stm32, linux-arm-kernel, linux-kernel, Zxyan Zhu
The Agilex5 smtg_crosststamp() handler polls XGMAC_INT_STATUS for TSIS
to learn that the auxiliary snapshot is latched. TSIS is a transient,
read-to-clear status bit, so the poll is racy in two ways: a stale TSIS
from an unrelated event satisfies it before the snapshot is latched,
leaving *device unwritten while the call reports success, and a
concurrent reader of XGMAC_TIMESTAMP_STATUS can clear it and make the
poll time out.
The FIFO level is also reported by the ATSNS count in
XGMAC_TIMESTAMP_STATUS, which reading the register does not disturb.
Poll ATSNS instead of TSIS, after waiting for the PTP_ACR_ATSFC FIFO
clear, so a stale ATSNS cannot satisfy the poll before the new snapshot
is latched.
Hold aux_ts_lock across the whole sequence so a concurrent
PTP_CLK_REQ_EXTTS request cannot flush the FIFO between the poll and
the drain loop.
Fixes: fd8c4f645496 ("net: stmmac: socfpga: Add hardware supported cross-timestamp")
Signed-off-by: Zxyan Zhu <zxyan0222@gmail.com>
---
.../ethernet/stmicro/stmmac/dwmac-socfpga.c | 29 ++++++++++++++-----
1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
index 6d4bc1fe8f75..ee79b74bf21b 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, so the poll only sees snapshots from
+ * 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,14 @@ 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);
+ /* Poll ATSNS instead of the transient TSIS: the ATSNS count is
+ * the FIFO level and is not cleared by reading the register.
+ */
+ 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 +378,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 +388,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 related [flat|nested] 5+ messages in thread
* [PATCH net-next v8 2/3] net: stmmac: guard against a zero channel in the aux snapshot handler
2026-10-13 3:32 [PATCH net-next v8 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix Zxyan Zhu
2026-10-13 3:32 ` [PATCH net-next v8 1/3] net: stmmac: dwmac-socfpga: complete cross-timestamp on ATSNS Zxyan Zhu
@ 2026-10-13 3:32 ` Zxyan Zhu
2026-10-13 3:32 ` [PATCH net-next v8 3/3] net: stmmac: dwxgmac2: add XGMAC timestamp interrupt support Zxyan Zhu
2026-10-13 3:35 ` [PATCH net-next v8 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix netdev-bot+sinfo
3 siblings, 0 replies; 5+ messages in thread
From: Zxyan Zhu @ 2026-10-13 3:32 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, maxime.chevallier
Cc: mcoquelin.stm32, alexandre.torgue, richardcochran,
muhammad.nazim.amirul.nazle.asmade, rohan.g.thomas, netdev,
linux-stm32, linux-arm-kernel, linux-kernel, Zxyan Zhu, stable
timestamp_interrupt() derives the EXTTS channel index with ilog2()
applied directly to the PTP_ACR channel mask, without a zero-mask
check. ilog2(0) yields -1, which lands 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 out of bounds
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 interrupt
arriving in between passes the flag check while the mask is still
clear.
Check the mask before applying 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 related [flat|nested] 5+ messages in thread
* [PATCH net-next v8 3/3] net: stmmac: dwxgmac2: add XGMAC timestamp interrupt support
2026-10-13 3:32 [PATCH net-next v8 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix Zxyan Zhu
2026-10-13 3:32 ` [PATCH net-next v8 1/3] net: stmmac: dwmac-socfpga: complete cross-timestamp on ATSNS Zxyan Zhu
2026-10-13 3:32 ` [PATCH net-next v8 2/3] net: stmmac: guard against a zero channel in the aux snapshot handler Zxyan Zhu
@ 2026-10-13 3:32 ` Zxyan Zhu
2026-10-13 3:35 ` [PATCH net-next v8 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix netdev-bot+sinfo
3 siblings, 0 replies; 5+ messages in thread
From: Zxyan Zhu @ 2026-10-13 3:32 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, maxime.chevallier
Cc: mcoquelin.stm32, alexandre.torgue, richardcochran,
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 register instead of
the XGMAC one and whose enable mask omits XGMAC_TSIE. The PTP clock
still advertises the auxiliary snapshot channels, so PTP_EXTTS_REQUEST
succeeds but no PTP_CLOCK_EXTTS event is ever delivered.
Add a DWXGMAC2 handler that reads XGMAC_TIMESTAMP_STATUS and reports
the pending auxiliary snapshots as PTP_CLOCK_EXTTS events. It
deliberately does not gate on XGMAC_INT_TSIS: TSIS is an aggregate,
read-to-clear bit that the TX timestamp completion path clears before
the handler can observe it, while the ATSNS count and the FIFO still
hold the events, so a dwmac4-style TSIS gate would drop them.
XGMAC_TSIE is not re-added to XGMAC_INT_DEFAULT_EN. It is armed on
demand from the PTP_CLK_REQ_EXTTS enable/disable path through a new
optional stmmac_ops->timestamp_interrupt_cfg() callback implemented
only for DWXGMAC2/DWXLGMAC2, since stmmac_enable() is shared with
dwmac4/dwmac5, whose timestamp interrupt stays always-enabled and is
relied upon by intel_crosststamp().
The handler drains the FIFO only while XGMAC_TSIE is enabled: it runs
on every MAC interrupt, and PTP_ACR_ATSFC is only ever set while
XGMAC_TSIE is disabled, so this both keeps it off the FIFO on unrelated
interrupts and stops it racing a FIFO flush. The interrupt is armed
only after the FIFO clear has completed, with aux_ts_lock held across
the whole enable so the arm cannot interleave with a concurrent suspend.
A failed enable is rolled back in hardware and software, leaving the
interrupt disarmed.
While an internal cross-timestamp owns the FIFO
(STMMAC_FLAG_INT_SNAPSHOT_EN, raised and dropped under aux_ts_lock),
the handler only clears the interrupt source. smtg_crosststamp() calls
stmmac_sync_timestamp_irq() after raising the flag and the disable path
calls it after disarming, so no handler that is already past its check
can still use the FIFO.
aux_ts_lock is initialised in probe and destroyed in remove, so it is
valid for the whole device lifetime. At unregister the clock is
invalidated under the lock before ptp_clock_unregister(), after which
the enable path and smtg_crosststamp() refuse to arm.
On resume stmmac_rearm_timestamp_irq() reprograms the enabled channel
under aux_ts_lock and re-arms the interrupt only if the FIFO flush
completed; otherwise, and whenever the timestamping setup cannot be
redone, the EXTTS state is dropped rather than advertised.
stmmac_suspend() disarms the interrupt and drains an in-flight handler
under aux_ts_lock before the clocks are gated.
Fixes: 4bb7aff9e6d0 ("net: stmmac: Add PTP support for XGMAC2")
Signed-off-by: Zxyan Zhu <zxyan0222@gmail.com>
---
.../ethernet/stmicro/stmmac/dwmac-socfpga.c | 16 +++
.../ethernet/stmicro/stmmac/dwxgmac2_core.c | 61 ++++++++++
drivers/net/ethernet/stmicro/stmmac/hwif.c | 4 +-
drivers/net/ethernet/stmicro/stmmac/hwif.h | 5 +
drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 +
.../ethernet/stmicro/stmmac/stmmac_hwtstamp.c | 12 ++
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 106 +++++++++++++++++-
.../net/ethernet/stmicro/stmmac/stmmac_ptp.c | 68 ++++++++---
.../net/ethernet/stmicro/stmmac/stmmac_ptp.h | 2 +
include/linux/stmmac.h | 1 +
10 files changed, 258 insertions(+), 18 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
index ee79b74bf21b..93c1092237fc 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
@@ -311,6 +311,18 @@ static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
return -EBUSY;
mutex_lock(&priv->aux_ts_lock);
+ /* Refuse to arm once the clock has been invalidated by
+ * unregister.
+ */
+ if (!priv->ptp_clock) {
+ mutex_unlock(&priv->aux_ts_lock);
+ return -ENODEV;
+ }
+ priv->plat->flags |= STMMAC_FLAG_INT_SNAPSHOT_EN;
+ /* Wait for a handler that is already past the flag check before
+ * taking over the snapshot FIFO.
+ */
+ stmmac_sync_timestamp_irq(priv);
/* Enable Internal snapshot trigger */
acr_value = readl(ptpaddr + PTP_ACR);
acr_value &= ~PTP_ACR_MASK;
@@ -328,6 +340,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 +357,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__);
@@ -366,6 +380,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__);
@@ -388,6 +403,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..3b98cd82ec2d 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,59 @@ 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;
+
+ /* Runs on every MAC interrupt: only touch the FIFO when the
+ * timestamp interrupt was armed, and leave it to the
+ * cross-timestamp path while that owns it.
+ */
+ if (priv->plat->flags & STMMAC_FLAG_INT_SNAPSHOT_EN) {
+ readl(priv->ioaddr + XGMAC_TIMESTAMP_STATUS);
+ return;
+ }
+
+ if (!(readl(priv->ioaddr + XGMAC_INT_EN) & XGMAC_TSIE))
+ return;
+
+ /* Reading XGMAC_TIMESTAMP_STATUS clears TSIS, so use the ATSNS
+ * count to see the pending snapshots.
+ */
+ 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 +1472,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 +1528,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 2e63614d93a4..498529433d2c 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.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 08b68441f9c8..06d687697e76 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -408,6 +408,7 @@ int stmmac_mdio_register(struct net_device *ndev);
int stmmac_mdio_reset(struct mii_bus *mii);
void stmmac_mdio_lock(struct stmmac_priv *priv);
void stmmac_mdio_unlock(struct stmmac_priv *priv);
+void stmmac_sync_timestamp_irq(struct stmmac_priv *priv);
int stmmac_pcs_setup(struct net_device *ndev);
void stmmac_pcs_clean(struct net_device *ndev);
void stmmac_set_ethtool_ops(struct net_device *netdev);
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 56d89a696272..c22f3dd40fca 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,88 @@ static int stmmac_init_ptp_clk_freq(struct stmmac_priv *priv)
return 0;
}
+/**
+ * stmmac_sync_timestamp_irq - wait for an in-flight timestamp handler
+ * @priv: driver private structure
+ * Description: called after the timestamp interrupt has been disarmed or
+ * handed over, to make sure a handler that is already past its checks has
+ * finished before the caller takes over the auxiliary snapshot FIFO.
+ */
+void stmmac_sync_timestamp_irq(struct stmmac_priv *priv)
+{
+ synchronize_irq(priv->dev->irq);
+ if (priv->wol_irq > 0 && priv->wol_irq != priv->dev->irq)
+ synchronize_irq(priv->wol_irq);
+}
+EXPORT_SYMBOL_GPL(stmmac_sync_timestamp_irq);
+
+/**
+ * stmmac_drop_extts_state - drop the recorded EXTTS channel
+ * @priv: driver private structure
+ * Description: clears the software EXTTS state so the PTP clock stops
+ * advertising a channel whose interrupt can no longer be armed.
+ */
+static void stmmac_drop_extts_state(struct stmmac_priv *priv)
+{
+ mutex_lock(&priv->aux_ts_lock);
+ priv->plat->flags &= ~STMMAC_FLAG_EXT_SNAPSHOT_EN;
+ priv->plat->ext_snapshot_num = -1;
+ mutex_unlock(&priv->aux_ts_lock);
+}
+
+/**
+ * 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 cleared it.
+ */
+ 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 {
+ /* The trigger could not be restored, so drop the software
+ * state rather than advertise a dead channel.
+ */
+ priv->plat->flags &= ~STMMAC_FLAG_EXT_SNAPSHOT_EN;
+ priv->plat->ext_snapshot_num = -1;
+ 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
@@ -8191,6 +8274,7 @@ static int __stmmac_dvr_probe(struct device *device,
stmmac_napi_add(ndev);
mutex_init(&priv->lock);
+ mutex_init(&priv->aux_ts_lock);
rwlock_init(&priv->ptp_lock);
stmmac_fpe_init(priv);
@@ -8324,6 +8408,7 @@ void stmmac_dvr_remove(struct device *dev)
destroy_workqueue(priv->wq);
mutex_destroy(&priv->lock);
+ mutex_destroy(&priv->aux_ts_lock);
bitmap_free(priv->af_xdp_zc_qps);
pm_runtime_disable(dev);
@@ -8378,6 +8463,17 @@ int stmmac_suspend(struct device *dev)
pinctrl_pm_select_sleep_state(priv->device);
}
+ /* Disarm the timestamp interrupt while suspended and drain an
+ * in-flight handler before the clocks are gated;
+ * stmmac_rearm_timestamp_irq() restores it on resume.
+ */
+ mutex_lock(&priv->aux_ts_lock);
+ if (priv->plat->flags & STMMAC_FLAG_EXT_SNAPSHOT_EN) {
+ stmmac_mac_timestamp_interrupt_cfg(priv, false);
+ stmmac_sync_timestamp_irq(priv);
+ }
+ mutex_unlock(&priv->aux_ts_lock);
+
mutex_unlock(&priv->lock);
rtnl_lock();
@@ -8497,12 +8593,18 @@ int stmmac_resume(struct device *dev)
}
if (priv->ptp_enabled) {
- if (stmmac_init_ptp_clk_freq(priv))
+ if (stmmac_init_ptp_clk_freq(priv)) {
+ stmmac_drop_extts_state(priv);
goto init_coalesce;
+ }
ret = stmmac_init_timestamping(priv);
- if (ret)
+ if (ret) {
+ stmmac_drop_extts_state(priv);
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..f41988f5927a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
@@ -226,6 +226,13 @@ static int stmmac_enable(struct ptp_clock_info *ptp,
u8 channel;
mutex_lock(&priv->aux_ts_lock);
+ /* Refuse to arm once the clock has been invalidated by
+ * unregister.
+ */
+ if (!priv->ptp_clock) {
+ mutex_unlock(&priv->aux_ts_lock);
+ return -ENODEV;
+ }
acr_value = readl(ptpaddr + PTP_ACR);
channel = ilog2(FIELD_GET(PTP_ACR_MASK, acr_value));
acr_value &= ~PTP_ACR_MASK;
@@ -239,22 +246,49 @@ 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");
writel(acr_value, ptpaddr + PTP_ACR);
- mutex_unlock(&priv->aux_ts_lock);
- /* wait for auxts fifo clear to finish */
+ /* Keep the lock across the FIFO clear so the arm below
+ * cannot interleave with a suspend.
+ */
ret = readl_poll_timeout(ptpaddr + PTP_ACR, acr_value,
!(acr_value & PTP_ACR_ATSFC),
10, 10000);
+ if (ret && on) {
+ /* Roll the failed enable back so no half-enabled
+ * channel is left.
+ */
+ acr_value = readl(ptpaddr + PTP_ACR);
+ acr_value &= ~PTP_ACR_ATSEN(rq->extts.index);
+ writel(acr_value, ptpaddr + PTP_ACR);
+ priv->plat->flags &= ~STMMAC_FLAG_EXT_SNAPSHOT_EN;
+ priv->plat->ext_snapshot_num = -1;
+ }
+ /* Arm the timestamp interrupt only after the FIFO clear,
+ * so the handler cannot see a snapshot that is about to be
+ * discarded.
+ */
+ stmmac_mac_timestamp_interrupt_cfg(priv, on && !ret);
+ /* Drain an in-flight handler before the channel is freed. */
+ if (!on)
+ stmmac_sync_timestamp_irq(priv);
+ mutex_unlock(&priv->aux_ts_lock);
break;
}
@@ -355,7 +389,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;
@@ -365,8 +399,6 @@ void stmmac_ptp_register(struct stmmac_priv *priv)
if (priv->plat->crosststamp)
priv->ptp_clock_ops.getcrosststamp = stmmac_getcrosststamp;
- mutex_init(&priv->aux_ts_lock);
-
priv->ptp_clock = ptp_clock_register(&priv->ptp_clock_ops,
priv->device);
if (IS_ERR(priv->ptp_clock)) {
@@ -376,8 +408,6 @@ void stmmac_ptp_register(struct stmmac_priv *priv)
if (priv->ptp_clock)
netdev_info(priv->dev, "registered PTP clock\n");
- else
- mutex_destroy(&priv->aux_ts_lock);
}
/**
@@ -388,12 +418,22 @@ void stmmac_ptp_register(struct stmmac_priv *priv)
*/
void stmmac_ptp_unregister(struct stmmac_priv *priv)
{
- if (priv->ptp_clock) {
- ptp_clock_unregister(priv->ptp_clock);
- priv->ptp_clock = NULL;
- pr_debug("Removed PTP HW clock successfully on %s\n",
- priv->dev->name);
+ struct ptp_clock *clock;
- mutex_destroy(&priv->aux_ts_lock);
- }
+ if (!priv->ptp_clock)
+ return;
+
+ /* Tear the hardware state down and invalidate the clock before
+ * releasing the lock, so a concurrent request cannot re-arm a
+ * clock that is going away.
+ */
+ mutex_lock(&priv->aux_ts_lock);
+ clock = priv->ptp_clock;
+ priv->ptp_clock = NULL;
+ stmmac_mac_timestamp_interrupt_cfg(priv, false);
+ priv->plat->flags &= ~STMMAC_FLAG_EXT_SNAPSHOT_EN;
+ mutex_unlock(&priv->aux_ts_lock);
+
+ ptp_clock_unregister(clock);
+ pr_debug("Removed PTP HW clock successfully on %s\n", priv->dev->name);
}
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 related [flat|nested] 5+ messages in thread
* Re: [PATCH net-next v8 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix
2026-10-13 3:32 [PATCH net-next v8 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix Zxyan Zhu
` (2 preceding siblings ...)
2026-10-13 3:32 ` [PATCH net-next v8 3/3] net: stmmac: dwxgmac2: add XGMAC timestamp interrupt support Zxyan Zhu
@ 2026-10-13 3:35 ` netdev-bot+sinfo
3 siblings, 0 replies; 5+ messages in thread
From: netdev-bot+sinfo @ 2026-10-13 3:35 UTC (permalink / raw)
To: Zxyan Zhu
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, maxime.chevallier,
mcoquelin.stm32, alexandre.torgue, richardcochran,
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] 5+ messages in thread
end of thread, other threads:[~2026-10-13 3:35 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13 3:32 [PATCH net-next v8 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix Zxyan Zhu
2026-10-13 3:32 ` [PATCH net-next v8 1/3] net: stmmac: dwmac-socfpga: complete cross-timestamp on ATSNS Zxyan Zhu
2026-10-13 3:32 ` [PATCH net-next v8 2/3] net: stmmac: guard against a zero channel in the aux snapshot handler Zxyan Zhu
2026-10-13 3:32 ` [PATCH net-next v8 3/3] net: stmmac: dwxgmac2: add XGMAC timestamp interrupt support Zxyan Zhu
2026-10-13 3:35 ` [PATCH net-next v8 0/3] net: stmmac: dwxgmac2: timestamp interrupt support + Agilex5 fix netdev-bot+sinfo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox