From: Rohan G Thomas <rohan.g.thomas@altera.com>
Cross timestamping is supported on Agilex5 platform with Synchronized
Multidrop Timestamp Gathering(SMTG) IP. The hardware cross-timestamp
result is made available the applications through the ioctl call
PTP_SYS_OFFSET_PRECISE, which inturn calls stmmac_getcrosststamp().
Device time is stored in the MAC Auxiliary register. The 64-bit System
time (ARM_ARCH_COUNTER) is stored in SMTG IP. SMTG IP is an MDIO device
with 0xC - 0xF MDIO register space holds 64-bit system time.
This commit is similar to following commit for Intel platforms:
Commit 341f67e424e5 ("net: stmmac: Add hardware supported cross-timestamp")
Signed-off-by: Rohan G Thomas <rohan.g.thomas@altera.com>
---
.../net/ethernet/stmicro/stmmac/dwmac-socfpga.c | 125 +++++++++++++++++++++
drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h | 5 +
2 files changed, 130 insertions(+)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
index 37fcf272a46920d1d97a4b651a469767609373b4..d36c9b77003ef4ad3ac598929fee3f7a8b94b9bc 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
@@ -5,6 +5,7 @@
*/
#include <linux/mfd/altera-sysmgr.h>
+#include <linux/clocksource_ids.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_net.h>
@@ -15,8 +16,10 @@
#include <linux/reset.h>
#include <linux/stmmac.h>
+#include "dwxgmac2.h"
#include "stmmac.h"
#include "stmmac_platform.h"
+#include "stmmac_ptp.h"
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1
@@ -41,6 +44,13 @@
#define SGMII_ADAPTER_ENABLE 0x0000
#define SGMII_ADAPTER_DISABLE 0x0001
+#define SMTG_MDIO_ADDR 0x15
+#define SMTG_TSC_WORD0 0xC
+#define SMTG_TSC_WORD1 0xD
+#define SMTG_TSC_WORD2 0xE
+#define SMTG_TSC_WORD3 0xF
+#define SMTG_TSC_SHIFT 16
+
struct socfpga_dwmac;
struct socfpga_dwmac_ops {
int (*set_phy_mode)(struct socfpga_dwmac *dwmac_priv);
@@ -269,6 +279,117 @@ static int socfpga_set_phy_mode_common(int phymode, u32 *val)
return 0;
}
+static void get_smtgtime(struct mii_bus *mii, int smtg_addr, u64 *smtg_time)
+{
+ u64 ns;
+
+ ns = mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD3);
+ ns <<= SMTG_TSC_SHIFT;
+ ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD2);
+ ns <<= SMTG_TSC_SHIFT;
+ ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD1);
+ ns <<= SMTG_TSC_SHIFT;
+ ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD0);
+
+ *smtg_time = ns;
+}
+
+static int dwxgmac_cross_ts_isr(struct stmmac_priv *priv)
+{
+ return (readl(priv->ioaddr + XGMAC_INT_STATUS) & XGMAC_INT_TSIS);
+}
+
+static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
+ void *ctx)
+{
+ struct stmmac_priv *priv = (struct stmmac_priv *)ctx;
+ u32 num_snapshot, gpio_value, acr_value;
+ void __iomem *ptpaddr = priv->ptpaddr;
+ void __iomem *ioaddr = priv->hw->pcsr;
+ unsigned long flags;
+ u64 smtg_time = 0;
+ u64 ptp_time = 0;
+ int i, ret;
+
+ /* Both internal crosstimestamping and external triggered event
+ * timestamping cannot be run concurrently.
+ */
+ if (priv->plat->flags & STMMAC_FLAG_EXT_SNAPSHOT_EN)
+ return -EBUSY;
+
+ mutex_lock(&priv->aux_ts_lock);
+ /* Enable Internal snapshot trigger */
+ acr_value = readl(ptpaddr + PTP_ACR);
+ acr_value &= ~PTP_ACR_MASK;
+ switch (priv->plat->int_snapshot_num) {
+ case AUX_SNAPSHOT0:
+ acr_value |= PTP_ACR_ATSEN0;
+ break;
+ case AUX_SNAPSHOT1:
+ acr_value |= PTP_ACR_ATSEN1;
+ break;
+ case AUX_SNAPSHOT2:
+ acr_value |= PTP_ACR_ATSEN2;
+ break;
+ case AUX_SNAPSHOT3:
+ acr_value |= PTP_ACR_ATSEN3;
+ break;
+ default:
+ mutex_unlock(&priv->aux_ts_lock);
+ return -EINVAL;
+ }
+ writel(acr_value, ptpaddr + PTP_ACR);
+
+ /* Clear FIFO */
+ 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);
+
+ /* Trigger Internal snapshot signal. Create a rising edge by just toggle
+ * the GPO0 to low and back to high.
+ */
+ gpio_value = readl(ioaddr + XGMAC_GPIO_STATUS);
+ gpio_value &= ~XGMAC_GPIO_GPO0;
+ writel(gpio_value, ioaddr + XGMAC_GPIO_STATUS);
+ gpio_value |= XGMAC_GPIO_GPO0;
+ writel(gpio_value, ioaddr + XGMAC_GPIO_STATUS);
+
+ /* Time sync done Indication - Interrupt method */
+ if (!wait_event_interruptible_timeout(priv->tstamp_busy_wait,
+ dwxgmac_cross_ts_isr(priv),
+ HZ / 100)) {
+ priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
+ return -ETIMEDOUT;
+ }
+
+ *system = (struct system_counterval_t) {
+ .cycles = 0,
+ .cs_id = CSID_ARM_ARCH_COUNTER,
+ .use_nsecs = true,
+ };
+
+ num_snapshot = (readl(ioaddr + XGMAC_TIMESTAMP_STATUS) &
+ XGMAC_TIMESTAMP_ATSNS_MASK) >>
+ XGMAC_TIMESTAMP_ATSNS_SHIFT;
+
+ /* Repeat until the timestamps are from the FIFO last segment */
+ for (i = 0; i < num_snapshot; i++) {
+ read_lock_irqsave(&priv->ptp_lock, flags);
+ stmmac_get_ptptime(priv, ptpaddr, &ptp_time);
+ *device = ns_to_ktime(ptp_time);
+ read_unlock_irqrestore(&priv->ptp_lock, flags);
+ }
+
+ get_smtgtime(priv->mii, SMTG_MDIO_ADDR, &smtg_time);
+ system->cycles = smtg_time;
+
+ priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
+
+ return ret;
+}
+
static int socfpga_gen5_set_phy_mode(struct socfpga_dwmac *dwmac)
{
struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr;
@@ -489,6 +610,10 @@ static void socfpga_agilex5_setup_plat_dat(struct socfpga_dwmac *dwmac)
/* Tx Queues 0 - 5 doesn't support TBS on Agilex5 */
break;
}
+
+ /* Hw supported cross-timestamp */
+ plat_dat->int_snapshot_num = AUX_SNAPSHOT0;
+ plat_dat->crosststamp = smtg_crosststamp;
}
static int socfpga_dwmac_probe(struct platform_device *pdev)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
index 0d408ee17f337851502cbcba8e82d2b839b9db02..e48cfa05000c07ed9194de786efa530a61a9dbfa 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
@@ -79,6 +79,7 @@
#define XGMAC_PSRQ(x) GENMASK((x) * 8 + 7, (x) * 8)
#define XGMAC_PSRQ_SHIFT(x) ((x) * 8)
#define XGMAC_INT_STATUS 0x000000b0
+#define XGMAC_INT_TSIS BIT(12)
#define XGMAC_LPIIS BIT(5)
#define XGMAC_PMTIS BIT(4)
#define XGMAC_INT_EN 0x000000b4
@@ -173,6 +174,8 @@
#define XGMAC_MDIO_ADDR 0x00000200
#define XGMAC_MDIO_DATA 0x00000204
#define XGMAC_MDIO_C22P 0x00000220
+#define XGMAC_GPIO_STATUS 0x0000027c
+#define XGMAC_GPIO_GPO0 BIT(16)
#define XGMAC_ADDRx_HIGH(x) (0x00000300 + (x) * 0x8)
#define XGMAC_ADDR_MAX 32
#define XGMAC_AE BIT(31)
@@ -220,6 +223,8 @@
#define XGMAC_OB BIT(0)
#define XGMAC_RSS_DATA 0x00000c8c
#define XGMAC_TIMESTAMP_STATUS 0x00000d20
+#define XGMAC_TIMESTAMP_ATSNS_MASK GENMASK(29, 25)
+#define XGMAC_TIMESTAMP_ATSNS_SHIFT 25
#define XGMAC_TXTSC BIT(15)
#define XGMAC_TXTIMESTAMP_NSEC 0x00000d30
#define XGMAC_TXTSSTSLO GENMASK(30, 0)
--
2.43.7
Hi Rohan,
kernel test robot noticed the following build warnings:
[auto build test WARNING on a8abe8e210c175b1d5a7e53df069e107b65c13cb]
url: https://github.com/intel-lab-lkp/linux/commits/Rohan-G-Thomas-via-B4-Relay/net-stmmac-socfpga-Agilex5-EMAC-platform-configuration/20251029-162502
base: a8abe8e210c175b1d5a7e53df069e107b65c13cb
patch link: https://lore.kernel.org/r/20251029-agilex5_ext-v1-4-1931132d77d6%40altera.com
patch subject: [PATCH net-next 4/4] net: stmmac: socfpga: Add hardware supported cross-timestamp
config: loongarch-allmodconfig (https://download.01.org/0day-ci/archive/20251030/202510301322.f0J41mwI-lkp@intel.com/config)
compiler: clang version 19.1.7 (https://github.com/llvm/llvm-project cd708029e0b2869e80abe31ddb175f7c35361f90)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251030/202510301322.f0J41mwI-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/202510301322.f0J41mwI-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c:390:9: warning: variable 'ret' is uninitialized when used here [-Wuninitialized]
390 | return ret;
| ^~~
drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c:312:12: note: initialize the variable 'ret' to silence this warning
312 | int i, ret;
| ^
| = 0
1 warning generated.
vim +/ret +390 drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
301
302 static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
303 void *ctx)
304 {
305 struct stmmac_priv *priv = (struct stmmac_priv *)ctx;
306 u32 num_snapshot, gpio_value, acr_value;
307 void __iomem *ptpaddr = priv->ptpaddr;
308 void __iomem *ioaddr = priv->hw->pcsr;
309 unsigned long flags;
310 u64 smtg_time = 0;
311 u64 ptp_time = 0;
312 int i, ret;
313
314 /* Both internal crosstimestamping and external triggered event
315 * timestamping cannot be run concurrently.
316 */
317 if (priv->plat->flags & STMMAC_FLAG_EXT_SNAPSHOT_EN)
318 return -EBUSY;
319
320 mutex_lock(&priv->aux_ts_lock);
321 /* Enable Internal snapshot trigger */
322 acr_value = readl(ptpaddr + PTP_ACR);
323 acr_value &= ~PTP_ACR_MASK;
324 switch (priv->plat->int_snapshot_num) {
325 case AUX_SNAPSHOT0:
326 acr_value |= PTP_ACR_ATSEN0;
327 break;
328 case AUX_SNAPSHOT1:
329 acr_value |= PTP_ACR_ATSEN1;
330 break;
331 case AUX_SNAPSHOT2:
332 acr_value |= PTP_ACR_ATSEN2;
333 break;
334 case AUX_SNAPSHOT3:
335 acr_value |= PTP_ACR_ATSEN3;
336 break;
337 default:
338 mutex_unlock(&priv->aux_ts_lock);
339 return -EINVAL;
340 }
341 writel(acr_value, ptpaddr + PTP_ACR);
342
343 /* Clear FIFO */
344 acr_value = readl(ptpaddr + PTP_ACR);
345 acr_value |= PTP_ACR_ATSFC;
346 writel(acr_value, ptpaddr + PTP_ACR);
347 /* Release the mutex */
348 mutex_unlock(&priv->aux_ts_lock);
349
350 /* Trigger Internal snapshot signal. Create a rising edge by just toggle
351 * the GPO0 to low and back to high.
352 */
353 gpio_value = readl(ioaddr + XGMAC_GPIO_STATUS);
354 gpio_value &= ~XGMAC_GPIO_GPO0;
355 writel(gpio_value, ioaddr + XGMAC_GPIO_STATUS);
356 gpio_value |= XGMAC_GPIO_GPO0;
357 writel(gpio_value, ioaddr + XGMAC_GPIO_STATUS);
358
359 /* Time sync done Indication - Interrupt method */
360 if (!wait_event_interruptible_timeout(priv->tstamp_busy_wait,
361 dwxgmac_cross_ts_isr(priv),
362 HZ / 100)) {
363 priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
364 return -ETIMEDOUT;
365 }
366
367 *system = (struct system_counterval_t) {
368 .cycles = 0,
369 .cs_id = CSID_ARM_ARCH_COUNTER,
370 .use_nsecs = true,
371 };
372
373 num_snapshot = (readl(ioaddr + XGMAC_TIMESTAMP_STATUS) &
374 XGMAC_TIMESTAMP_ATSNS_MASK) >>
375 XGMAC_TIMESTAMP_ATSNS_SHIFT;
376
377 /* Repeat until the timestamps are from the FIFO last segment */
378 for (i = 0; i < num_snapshot; i++) {
379 read_lock_irqsave(&priv->ptp_lock, flags);
380 stmmac_get_ptptime(priv, ptpaddr, &ptp_time);
381 *device = ns_to_ktime(ptp_time);
382 read_unlock_irqrestore(&priv->ptp_lock, flags);
383 }
384
385 get_smtgtime(priv->mii, SMTG_MDIO_ADDR, &smtg_time);
386 system->cycles = smtg_time;
387
388 priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
389
> 390 return ret;
391 }
392
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Rohan,
On 29/10/2025 09:06, Rohan G Thomas via B4 Relay wrote:
> From: Rohan G Thomas <rohan.g.thomas@altera.com>
>
> Cross timestamping is supported on Agilex5 platform with Synchronized
> Multidrop Timestamp Gathering(SMTG) IP. The hardware cross-timestamp
> result is made available the applications through the ioctl call
> PTP_SYS_OFFSET_PRECISE, which inturn calls stmmac_getcrosststamp().
>
> Device time is stored in the MAC Auxiliary register. The 64-bit System
> time (ARM_ARCH_COUNTER) is stored in SMTG IP. SMTG IP is an MDIO device
> with 0xC - 0xF MDIO register space holds 64-bit system time.
>
> This commit is similar to following commit for Intel platforms:
> Commit 341f67e424e5 ("net: stmmac: Add hardware supported cross-timestamp")
>
> Signed-off-by: Rohan G Thomas <rohan.g.thomas@altera.com>
> ---
> .../net/ethernet/stmicro/stmmac/dwmac-socfpga.c | 125 +++++++++++++++++++++
> drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h | 5 +
> 2 files changed, 130 insertions(+)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
> index 37fcf272a46920d1d97a4b651a469767609373b4..d36c9b77003ef4ad3ac598929fee3f7a8b94b9bc 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
> @@ -5,6 +5,7 @@
> */
>
> #include <linux/mfd/altera-sysmgr.h>
> +#include <linux/clocksource_ids.h>
> #include <linux/of.h>
> #include <linux/of_address.h>
> #include <linux/of_net.h>
> @@ -15,8 +16,10 @@
> #include <linux/reset.h>
> #include <linux/stmmac.h>
>
> +#include "dwxgmac2.h"
> #include "stmmac.h"
> #include "stmmac_platform.h"
> +#include "stmmac_ptp.h"
>
> #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0
> #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1
> @@ -41,6 +44,13 @@
> #define SGMII_ADAPTER_ENABLE 0x0000
> #define SGMII_ADAPTER_DISABLE 0x0001
>
> +#define SMTG_MDIO_ADDR 0x15
> +#define SMTG_TSC_WORD0 0xC
> +#define SMTG_TSC_WORD1 0xD
> +#define SMTG_TSC_WORD2 0xE
> +#define SMTG_TSC_WORD3 0xF
> +#define SMTG_TSC_SHIFT 16
> +
> struct socfpga_dwmac;
> struct socfpga_dwmac_ops {
> int (*set_phy_mode)(struct socfpga_dwmac *dwmac_priv);
> @@ -269,6 +279,117 @@ static int socfpga_set_phy_mode_common(int phymode, u32 *val)
> return 0;
> }
>
> +static void get_smtgtime(struct mii_bus *mii, int smtg_addr, u64 *smtg_time)
> +{
> + u64 ns;
> +
> + ns = mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD3);
> + ns <<= SMTG_TSC_SHIFT;
> + ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD2);
> + ns <<= SMTG_TSC_SHIFT;
> + ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD1);
> + ns <<= SMTG_TSC_SHIFT;
> + ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD0);
> +
> + *smtg_time = ns;
> +}
> +
> +static int dwxgmac_cross_ts_isr(struct stmmac_priv *priv)
> +{
> + return (readl(priv->ioaddr + XGMAC_INT_STATUS) & XGMAC_INT_TSIS);
> +}
> +
> +static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
> + void *ctx)
> +{
> + struct stmmac_priv *priv = (struct stmmac_priv *)ctx;
> + u32 num_snapshot, gpio_value, acr_value;
> + void __iomem *ptpaddr = priv->ptpaddr;
> + void __iomem *ioaddr = priv->hw->pcsr;
> + unsigned long flags;
> + u64 smtg_time = 0;
> + u64 ptp_time = 0;
> + int i, ret;
> +
> + /* Both internal crosstimestamping and external triggered event
> + * timestamping cannot be run concurrently.
> + */
> + if (priv->plat->flags & STMMAC_FLAG_EXT_SNAPSHOT_EN)
> + return -EBUSY;
> +
> + mutex_lock(&priv->aux_ts_lock);
> + /* Enable Internal snapshot trigger */
> + acr_value = readl(ptpaddr + PTP_ACR);
> + acr_value &= ~PTP_ACR_MASK;
> + switch (priv->plat->int_snapshot_num) {
> + case AUX_SNAPSHOT0:
> + acr_value |= PTP_ACR_ATSEN0;
> + break;
> + case AUX_SNAPSHOT1:
> + acr_value |= PTP_ACR_ATSEN1;
> + break;
> + case AUX_SNAPSHOT2:
> + acr_value |= PTP_ACR_ATSEN2;
> + break;
> + case AUX_SNAPSHOT3:
> + acr_value |= PTP_ACR_ATSEN3;
> + break;
> + default:
> + mutex_unlock(&priv->aux_ts_lock);
> + return -EINVAL;
> + }
> + writel(acr_value, ptpaddr + PTP_ACR);
> +
> + /* Clear FIFO */
> + 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);
> +
> + /* Trigger Internal snapshot signal. Create a rising edge by just toggle
> + * the GPO0 to low and back to high.
> + */
> + gpio_value = readl(ioaddr + XGMAC_GPIO_STATUS);
> + gpio_value &= ~XGMAC_GPIO_GPO0;
> + writel(gpio_value, ioaddr + XGMAC_GPIO_STATUS);
> + gpio_value |= XGMAC_GPIO_GPO0;
> + writel(gpio_value, ioaddr + XGMAC_GPIO_STATUS);
> +
> + /* Time sync done Indication - Interrupt method */
> + if (!wait_event_interruptible_timeout(priv->tstamp_busy_wait,
> + dwxgmac_cross_ts_isr(priv),
> + HZ / 100)) {
> + priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
> + return -ETIMEDOUT;
Don't you need to set priv->plat->flags |= STMMAC_FLAG_INT_SNAPSHOT_EN first?
Otherwise, timestamp_interrupt() in stmmac_hwtstamp() won't call wake_up()
on the wait_queue.
> + }
> +
> + *system = (struct system_counterval_t) {
> + .cycles = 0,
> + .cs_id = CSID_ARM_ARCH_COUNTER,
> + .use_nsecs = true,
> + };
> +
> + num_snapshot = (readl(ioaddr + XGMAC_TIMESTAMP_STATUS) &
> + XGMAC_TIMESTAMP_ATSNS_MASK) >>
> + XGMAC_TIMESTAMP_ATSNS_SHIFT;
> +
> + /* Repeat until the timestamps are from the FIFO last segment */
> + for (i = 0; i < num_snapshot; i++) {
> + read_lock_irqsave(&priv->ptp_lock, flags);
> + stmmac_get_ptptime(priv, ptpaddr, &ptp_time);
> + *device = ns_to_ktime(ptp_time);
> + read_unlock_irqrestore(&priv->ptp_lock, flags);
> + }
> +
> + get_smtgtime(priv->mii, SMTG_MDIO_ADDR, &smtg_time);
> + system->cycles = smtg_time;
> +
> + priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
> +
> + return ret;
> +}
Maxime
Hi Maxime,
On 10/29/2025 3:20 PM, Maxime Chevallier wrote:
> Hi Rohan,
>
> On 29/10/2025 09:06, Rohan G Thomas via B4 Relay wrote:
>> From: Rohan G Thomas <rohan.g.thomas@altera.com>
>>
>> Cross timestamping is supported on Agilex5 platform with Synchronized
>> Multidrop Timestamp Gathering(SMTG) IP. The hardware cross-timestamp
>> result is made available the applications through the ioctl call
>> PTP_SYS_OFFSET_PRECISE, which inturn calls stmmac_getcrosststamp().
>>
>> Device time is stored in the MAC Auxiliary register. The 64-bit System
>> time (ARM_ARCH_COUNTER) is stored in SMTG IP. SMTG IP is an MDIO device
>> with 0xC - 0xF MDIO register space holds 64-bit system time.
>>
>> This commit is similar to following commit for Intel platforms:
>> Commit 341f67e424e5 ("net: stmmac: Add hardware supported cross-timestamp")
>>
>> Signed-off-by: Rohan G Thomas <rohan.g.thomas@altera.com>
>> ---
>> .../net/ethernet/stmicro/stmmac/dwmac-socfpga.c | 125 +++++++++++++++++++++
>> drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h | 5 +
>> 2 files changed, 130 insertions(+)
>>
>> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
>> index 37fcf272a46920d1d97a4b651a469767609373b4..d36c9b77003ef4ad3ac598929fee3f7a8b94b9bc 100644
>> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
>> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
>> @@ -5,6 +5,7 @@
>> */
>>
>> #include <linux/mfd/altera-sysmgr.h>
>> +#include <linux/clocksource_ids.h>
>> #include <linux/of.h>
>> #include <linux/of_address.h>
>> #include <linux/of_net.h>
>> @@ -15,8 +16,10 @@
>> #include <linux/reset.h>
>> #include <linux/stmmac.h>
>>
>> +#include "dwxgmac2.h"
>> #include "stmmac.h"
>> #include "stmmac_platform.h"
>> +#include "stmmac_ptp.h"
>>
>> #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0
>> #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1
>> @@ -41,6 +44,13 @@
>> #define SGMII_ADAPTER_ENABLE 0x0000
>> #define SGMII_ADAPTER_DISABLE 0x0001
>>
>> +#define SMTG_MDIO_ADDR 0x15
>> +#define SMTG_TSC_WORD0 0xC
>> +#define SMTG_TSC_WORD1 0xD
>> +#define SMTG_TSC_WORD2 0xE
>> +#define SMTG_TSC_WORD3 0xF
>> +#define SMTG_TSC_SHIFT 16
>> +
>> struct socfpga_dwmac;
>> struct socfpga_dwmac_ops {
>> int (*set_phy_mode)(struct socfpga_dwmac *dwmac_priv);
>> @@ -269,6 +279,117 @@ static int socfpga_set_phy_mode_common(int phymode, u32 *val)
>> return 0;
>> }
>>
>> +static void get_smtgtime(struct mii_bus *mii, int smtg_addr, u64 *smtg_time)
>> +{
>> + u64 ns;
>> +
>> + ns = mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD3);
>> + ns <<= SMTG_TSC_SHIFT;
>> + ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD2);
>> + ns <<= SMTG_TSC_SHIFT;
>> + ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD1);
>> + ns <<= SMTG_TSC_SHIFT;
>> + ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD0);
>> +
>> + *smtg_time = ns;
>> +}
>> +
>> +static int dwxgmac_cross_ts_isr(struct stmmac_priv *priv)
>> +{
>> + return (readl(priv->ioaddr + XGMAC_INT_STATUS) & XGMAC_INT_TSIS);
>> +}
>> +
>> +static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
>> + void *ctx)
>> +{
>> + struct stmmac_priv *priv = (struct stmmac_priv *)ctx;
>> + u32 num_snapshot, gpio_value, acr_value;
>> + void __iomem *ptpaddr = priv->ptpaddr;
>> + void __iomem *ioaddr = priv->hw->pcsr;
>> + unsigned long flags;
>> + u64 smtg_time = 0;
>> + u64 ptp_time = 0;
>> + int i, ret;
>> +
>> + /* Both internal crosstimestamping and external triggered event
>> + * timestamping cannot be run concurrently.
>> + */
>> + if (priv->plat->flags & STMMAC_FLAG_EXT_SNAPSHOT_EN)
>> + return -EBUSY;
>> +
>> + mutex_lock(&priv->aux_ts_lock);
>> + /* Enable Internal snapshot trigger */
>> + acr_value = readl(ptpaddr + PTP_ACR);
>> + acr_value &= ~PTP_ACR_MASK;
>> + switch (priv->plat->int_snapshot_num) {
>> + case AUX_SNAPSHOT0:
>> + acr_value |= PTP_ACR_ATSEN0;
>> + break;
>> + case AUX_SNAPSHOT1:
>> + acr_value |= PTP_ACR_ATSEN1;
>> + break;
>> + case AUX_SNAPSHOT2:
>> + acr_value |= PTP_ACR_ATSEN2;
>> + break;
>> + case AUX_SNAPSHOT3:
>> + acr_value |= PTP_ACR_ATSEN3;
>> + break;
>> + default:
>> + mutex_unlock(&priv->aux_ts_lock);
>> + return -EINVAL;
>> + }
>> + writel(acr_value, ptpaddr + PTP_ACR);
>> +
>> + /* Clear FIFO */
>> + 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);
>> +
>> + /* Trigger Internal snapshot signal. Create a rising edge by just toggle
>> + * the GPO0 to low and back to high.
>> + */
>> + gpio_value = readl(ioaddr + XGMAC_GPIO_STATUS);
>> + gpio_value &= ~XGMAC_GPIO_GPO0;
>> + writel(gpio_value, ioaddr + XGMAC_GPIO_STATUS);
>> + gpio_value |= XGMAC_GPIO_GPO0;
>> + writel(gpio_value, ioaddr + XGMAC_GPIO_STATUS);
>> +
>> + /* Time sync done Indication - Interrupt method */
>> + if (!wait_event_interruptible_timeout(priv->tstamp_busy_wait,
>> + dwxgmac_cross_ts_isr(priv),
>> + HZ / 100)) {
>> + priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
>> + return -ETIMEDOUT;
>
> Don't you need to set priv->plat->flags |= STMMAC_FLAG_INT_SNAPSHOT_EN first?
> Otherwise, timestamp_interrupt() in stmmac_hwtstamp() won't call wake_up()
> on the wait_queue.
>
Thanks for pointing this out. My intention was to use the polling
method, but I accidentally left behind some code from experimenting with
the interrupt method. While reverting those changes, I missed updating
this part of the code. Will fix this in the next revision. Sorry for the
error. Currently not seeing any timeout issues with polling method on
XGMAC IP. Also some spurios interrupts causing stall when using
the interrupt method in XGMAC.
>> + }
>> +
>> + *system = (struct system_counterval_t) {
>> + .cycles = 0,
>> + .cs_id = CSID_ARM_ARCH_COUNTER,
>> + .use_nsecs = true,
>> + };
>> +
>> + num_snapshot = (readl(ioaddr + XGMAC_TIMESTAMP_STATUS) &
>> + XGMAC_TIMESTAMP_ATSNS_MASK) >>
>> + XGMAC_TIMESTAMP_ATSNS_SHIFT;
>> +
>> + /* Repeat until the timestamps are from the FIFO last segment */
>> + for (i = 0; i < num_snapshot; i++) {
>> + read_lock_irqsave(&priv->ptp_lock, flags);
>> + stmmac_get_ptptime(priv, ptpaddr, &ptp_time);
>> + *device = ns_to_ktime(ptp_time);
>> + read_unlock_irqrestore(&priv->ptp_lock, flags);
>> + }
>> +
>> + get_smtgtime(priv->mii, SMTG_MDIO_ADDR, &smtg_time);
>> + system->cycles = smtg_time;
>> +
>> + priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
>> +
>> + return ret;
>> +}
>
> Maxime
Best Regards,
Rohan
Hi Rohan,
On 29/10/2025 15:41, G Thomas, Rohan wrote:
> Hi Maxime,
>
> On 10/29/2025 3:20 PM, Maxime Chevallier wrote:
>> Hi Rohan,
>>
>> On 29/10/2025 09:06, Rohan G Thomas via B4 Relay wrote:
>>> From: Rohan G Thomas <rohan.g.thomas@altera.com>
>>>
>>> Cross timestamping is supported on Agilex5 platform with Synchronized
>>> Multidrop Timestamp Gathering(SMTG) IP. The hardware cross-timestamp
>>> result is made available the applications through the ioctl call
>>> PTP_SYS_OFFSET_PRECISE, which inturn calls stmmac_getcrosststamp().
>>>
>>> Device time is stored in the MAC Auxiliary register. The 64-bit System
>>> time (ARM_ARCH_COUNTER) is stored in SMTG IP. SMTG IP is an MDIO device
>>> with 0xC - 0xF MDIO register space holds 64-bit system time.
>>>
>>> This commit is similar to following commit for Intel platforms:
>>> Commit 341f67e424e5 ("net: stmmac: Add hardware supported cross-timestamp")
>>>
>>> Signed-off-by: Rohan G Thomas <rohan.g.thomas@altera.com>
[...]
>>> + /* Time sync done Indication - Interrupt method */
>>> + if (!wait_event_interruptible_timeout(priv->tstamp_busy_wait,
>>> + dwxgmac_cross_ts_isr(priv),
>>> + HZ / 100)) {
>>> + priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
>>> + return -ETIMEDOUT;
>>
>> Don't you need to set priv->plat->flags |= STMMAC_FLAG_INT_SNAPSHOT_EN first?
>> Otherwise, timestamp_interrupt() in stmmac_hwtstamp() won't call wake_up()
>> on the wait_queue.
>>
>
> Thanks for pointing this out. My intention was to use the polling
> method, but I accidentally left behind some code from experimenting with
> the interrupt method. While reverting those changes, I missed updating
> this part of the code. Will fix this in the next revision. Sorry for the
> error. Currently not seeing any timeout issues with polling method on
> XGMAC IP. Also some spurios interrupts causing stall when using
> the interrupt method in XGMAC.
So, if you use the polling method, this will likely bring this code
even closer to what's implemented in the intel dwmac wrapper. Is this
the same IP ?
To me it looks like the only difference will be a few
register offsets (XGMAC vs GMAC), some clock id and the mdio accesses,
maybe it could be worth considering re-using what's been done on the
Intel side and avoid duplication...
That could be all moved to stmmac_ptp for instance, using some flag
in the plat data to indicate if cross timestamping is supported, and
use the core type (xgmac, gmac, etc.) for the offsets ?
Of course with the risk of regressing dwmac-intel.c :(
Maxime
Hi Maxime,
On 10/29/2025 8:36 PM, Maxime Chevallier wrote:
> Hi Rohan,
>
> On 29/10/2025 15:41, G Thomas, Rohan wrote:
>> Hi Maxime,
>>
>> On 10/29/2025 3:20 PM, Maxime Chevallier wrote:
>>> Hi Rohan,
>>>
>>> On 29/10/2025 09:06, Rohan G Thomas via B4 Relay wrote:
>>>> From: Rohan G Thomas <rohan.g.thomas@altera.com>
>>>>
>>>> Cross timestamping is supported on Agilex5 platform with Synchronized
>>>> Multidrop Timestamp Gathering(SMTG) IP. The hardware cross-timestamp
>>>> result is made available the applications through the ioctl call
>>>> PTP_SYS_OFFSET_PRECISE, which inturn calls stmmac_getcrosststamp().
>>>>
>>>> Device time is stored in the MAC Auxiliary register. The 64-bit System
>>>> time (ARM_ARCH_COUNTER) is stored in SMTG IP. SMTG IP is an MDIO device
>>>> with 0xC - 0xF MDIO register space holds 64-bit system time.
>>>>
>>>> This commit is similar to following commit for Intel platforms:
>>>> Commit 341f67e424e5 ("net: stmmac: Add hardware supported cross-timestamp")
>>>>
>>>> Signed-off-by: Rohan G Thomas <rohan.g.thomas@altera.com>
>
> [...]
>
>>>> + /* Time sync done Indication - Interrupt method */
>>>> + if (!wait_event_interruptible_timeout(priv->tstamp_busy_wait,
>>>> + dwxgmac_cross_ts_isr(priv),
>>>> + HZ / 100)) {
>>>> + priv->plat->flags &= ~STMMAC_FLAG_INT_SNAPSHOT_EN;
>>>> + return -ETIMEDOUT;
>>>
>>> Don't you need to set priv->plat->flags |= STMMAC_FLAG_INT_SNAPSHOT_EN first?
>>> Otherwise, timestamp_interrupt() in stmmac_hwtstamp() won't call wake_up()
>>> on the wait_queue.
>>>
>>
>> Thanks for pointing this out. My intention was to use the polling
>> method, but I accidentally left behind some code from experimenting with
>> the interrupt method. While reverting those changes, I missed updating
>> this part of the code. Will fix this in the next revision. Sorry for the
>> error. Currently not seeing any timeout issues with polling method on
>> XGMAC IP. Also some spurios interrupts causing stall when using
>> the interrupt method in XGMAC.
>
> So, if you use the polling method, this will likely bring this code
> even closer to what's implemented in the intel dwmac wrapper. Is this
> the same IP ?
>
AFAIK, this is an Altera specific IP. Altera was part of Intel and may
be the SMTG IP implementation was inspired by ART timer on x86 platforms
:). But for later platforms this may diverge.
Also, latest intel dwmac wrapper is using interrupt method.
> To me it looks like the only difference will be a few
> register offsets (XGMAC vs GMAC), some clock id and the mdio accesses,
> maybe it could be worth considering re-using what's been done on the
> Intel side and avoid duplication...
>
> That could be all moved to stmmac_ptp for instance, using some flag
> in the plat data to indicate if cross timestamping is supported, and
> use the core type (xgmac, gmac, etc.) for the offsets ?
>
Since SMTG or ART is a vendor specific IP, I'm not sure it is good to
move this to the stmmac_ptp. Also, not sure other ways (other than mdio
acess, gpo toggling) to implement hw cross timestamping. Open to
suggestions.
> Of course with the risk of regressing dwmac-intel.c :(
>
> Maxime
Best Regards,
Rohan
© 2016 - 2025 Red Hat, Inc.