This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 3589fceab3edc81228a829ea32cd01bcf38a2196
Author: Daniel P. Carvalho <[email protected]>
AuthorDate: Thu Sep 17 17:33:32 2026 -0300

    arch/arm/stm32: implement PTP hardware clock driver (/dev/ptp0)
    
    Implement lower-half PTP hardware clock operations (struct ptp_lowerhalf_s
    and struct ptp_ops_s) in the STM32 Ethernet driver and register it with the
    generic PTP clock framework (drivers/timers/ptp_clock.c) to expose 
/dev/ptp0.
    
    Supported operations:
    - adjfine: adjust PTP clock frequency in parts per billion (ppb)
    - adjphase: adjust PTP clock phase via hardware TSSTU
    - adjtime: shift PTP clock time by signed delta in nanoseconds
    - gettime: atomic double-read of hardware timestamp registers
    - settime: initialize hardware timestamp counter via TSSTI
    - getres: return 1 ns clock resolution
    
    Also fix a sign bug in stm32_eth_ptp_adjust() where uint64_t addend
    promoted negative ppb adjustments to unsigned, corrupting frequency trim
    for crystals running faster than nominal.
    
    Follow-up to #20148 per review recommendation to use the standard POSIX
    /dev/ptp0 character driver instead of custom socket ioctls.
    
    Assisted-by: Gemini:gemini-3.8-pro
    Signed-off-by: Daniel P. Carvalho <[email protected]>
---
 arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c | 261 ++++++++++++++++++++++++--
 1 file changed, 249 insertions(+), 12 deletions(-)

diff --git a/arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c 
b/arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c
index 90fc38586da..56947eb3a2a 100644
--- a/arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c
+++ b/arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c
@@ -31,6 +31,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include <time.h>
+#include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 #include <nuttx/debug.h>
@@ -54,6 +55,10 @@
 #  include <nuttx/net/pkt.h>
 #endif
 
+#if defined(CONFIG_PTP_CLOCK) || defined(CONFIG_STM32_ETH_PTP)
+#  include <nuttx/timers/ptp_clock.h>
+#endif
+
 #include "arm_internal.h"
 #include "chip.h"
 #include "stm32_gpio.h"
@@ -630,6 +635,9 @@ struct stm32_ethmac_s
   uint32_t             rxtimelow;   /* Received packet timestamp subsecond */
   uint32_t             rxtimehigh;  /* Received packet timestamp seconds */
 #endif
+#if defined(CONFIG_STM32_ETH_PTP) && defined(CONFIG_PTP_CLOCK)
+  struct ptp_lowerhalf_s ptp_lower; /* PTP hardware clock lower half */
+#endif
 };
 
 /****************************************************************************
@@ -772,7 +780,10 @@ static int  stm32_ethconfig(struct stm32_ethmac_s *priv);
 /* PTP initialization and access */
 
 #ifdef CONFIG_STM32_ETH_PTP
-static int stm32_eth_ptp_adjust(long ppb);
+static int stm32_ptp_adjfine(struct ptp_lowerhalf_s *lower, long ppb);
+#ifdef CONFIG_PTP_CLOCK
+static int stm32_ptp_adjtime(struct ptp_lowerhalf_s *lower, int64_t delta);
+#endif
 static void stm32_eth_ptp_init(uint64_t timestamp);
 #ifdef CONFIG_STM32_ETH_PTP_RTC_HIRES
 static uint64_t stm32_eth_ptp_gettime(void);
@@ -3567,15 +3578,19 @@ static inline void stm32_ethgpioconfig(struct 
stm32_ethmac_s *priv)
 #ifdef CONFIG_STM32_ETH_PTP
 
 /****************************************************************************
- * Function: stm32_eth_ptp_adjust
+ * Name: stm32_ptp_adjfine
  *
  * Description:
- *   Adjust PTP timer run rate.
+ *   Adjust the PTP clock frequency in parts per billion (ppb).
  *
  * Input Parameters:
- *   ppb - Adjustment in parts per billion (nanoseconds per second).
- *         Zero is default rate, positive value makes clock run faster
- *         and negative value slower.
+ *   lower - Pointer to the PTP clock lower-half instance (unused; this
+ *           implementation also serves as the PTP timer's own internal
+ *           rate setter, called with lower == NULL from
+ *           stm32_eth_ptp_init() and up_rtc_adjtime()).
+ *   ppb   - Adjustment in parts per billion (nanoseconds per second).
+ *           Zero is default rate, positive value makes clock run faster
+ *           and negative value slower.
  *
  * Returned Value:
  *   OK on success, negated errno on failure.
@@ -3585,12 +3600,14 @@ static inline void stm32_ethgpioconfig(struct 
stm32_ethmac_s *priv)
  *
  ****************************************************************************/
 
-static int stm32_eth_ptp_adjust(long ppb)
+static int stm32_ptp_adjfine(struct ptp_lowerhalf_s *lower, long ppb)
 {
   uint32_t regval;
-  uint64_t addend;
+  int64_t addend;
   uint32_t increment;
 
+  UNUSED(lower);
+
   /* Compute addend value to achieve nominal timer rate.
    * Increment is set by stm32_eth_ptp_init() and remains constants after
    * that.
@@ -3608,9 +3625,9 @@ static int stm32_eth_ptp_adjust(long ppb)
 
   /* Check for overflows */
 
-  if (addend == 0 || (uint32_t)addend != addend)
+  if (addend <= 0 || addend > UINT32_MAX)
     {
-      nerr("PTP adjustment out of range: ppb=%ld, addend=%lld\n",
+      nerr("PTP adjustment out of range: ppb=%ld, addend=%" PRId64 "\n",
            ppb, addend);
       return -EINVAL;
     }
@@ -3632,6 +3649,72 @@ static int stm32_eth_ptp_adjust(long ppb)
   return OK;
 }
 
+#ifdef CONFIG_PTP_CLOCK
+/****************************************************************************
+ * Name: stm32_ptp_adjtime
+ *
+ * Description:
+ *   Nudge the PTP hardware counter's phase by a signed delta, in
+ *   nanoseconds, via the System Time Update (TSSTU) mechanism. Unlike
+ *   stm32_eth_ptp_init(), this does not reset the rate (addend) that
+ *   stm32_ptp_adjfine() may already have applied.
+ *
+ * Input Parameters:
+ *   lower - Pointer to the PTP clock lower-half instance (unused; this
+ *           implementation also serves as the PTP timer's own internal
+ *           phase setter, called with lower == NULL from
+ *           stm32_ptp_adjphase()).
+ *   delta - Amount to add to (positive) or subtract from (negative) the
+ *           current counter value, in nanoseconds.
+ *
+ * Returned Value:
+ *   OK on success, negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_ptp_adjtime(struct ptp_lowerhalf_s *lower, int64_t delta)
+{
+  uint32_t regval;
+  uint32_t sec;
+  uint32_t subsec;
+  uint32_t abs_nsec;
+  uint64_t abs_ns;
+  bool negative;
+
+  UNUSED(lower);
+
+  negative = (delta < 0);
+  abs_ns   = llabs(delta);
+
+  sec      = abs_ns / NSEC_PER_SEC;
+  abs_nsec = abs_ns % NSEC_PER_SEC;
+
+  /* Convert the nanosecond remainder to the same 32-bit binary fraction
+   * of a second used by ptp_to_timespec()/stm32_eth_ptp_init(), then
+   * halve it to fit the 31-bit TSUSS field (mirrors the >>1 done in
+   * stm32_eth_ptp_init()).
+   */
+
+  subsec = (((uint64_t)abs_nsec << 32) / NSEC_PER_SEC) >> 1;
+  subsec &= ETH_PTPTSLR_MASK;
+
+  stm32_putreg(sec, STM32_ETH_PTPTSHUR);
+  stm32_putreg(subsec | (negative ? ETH_PTPTSLU_TSUPNS : 0),
+               STM32_ETH_PTPTSLUR);
+
+  regval = stm32_getreg(STM32_ETH_PTPTSCR);
+  stm32_putreg(regval | ETH_PTPTSCR_TSSTU, STM32_ETH_PTPTSCR);
+  up_udelay(1);
+  if (stm32_getreg(STM32_ETH_PTPTSCR) & ETH_PTPTSCR_TSSTU)
+    {
+      nerr("PTP phase update failed\n");
+      return -EBUSY;
+    }
+
+  return OK;
+}
+#endif /* CONFIG_PTP_CLOCK */
+
 /****************************************************************************
  * Function: stm32_eth_ptp_init
  *
@@ -3678,7 +3761,7 @@ static void stm32_eth_ptp_init(uint64_t timestamp)
 
   /* Update addend value to default rate */
 
-  stm32_eth_ptp_adjust(0);
+  stm32_ptp_adjfine(NULL, 0);
 
   /* Enable fine update mode */
 
@@ -3709,6 +3792,148 @@ static void stm32_eth_ptp_init(uint64_t timestamp)
 #endif
 }
 
+#if defined(CONFIG_STM32_ETH_PTP) && defined(CONFIG_PTP_CLOCK)
+/****************************************************************************
+ * Name: stm32_ptp_adjphase
+ *
+ * Description:
+ *   Adjust the PTP clock phase by a signed offset in nanoseconds.
+ *
+ * Input Parameters:
+ *   lower - Pointer to the PTP clock lower-half instance
+ *   phase - Phase adjustment in nanoseconds
+ *
+ * Returned Value:
+ *   OK on success, negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_ptp_adjphase(struct ptp_lowerhalf_s *lower, int32_t phase)
+{
+  return stm32_ptp_adjtime(lower, phase);
+}
+
+/****************************************************************************
+ * Name: stm32_ptp_gettime
+ *
+ * Description:
+ *   Read the current time from the PTP hardware clock.
+ *
+ * Input Parameters:
+ *   lower - Pointer to the PTP clock lower-half instance
+ *   ts    - Location to store the current PTP time
+ *   sts   - System timestamp pair (unused, can be NULL)
+ *
+ * Returned Value:
+ *   OK on success, negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_ptp_gettime(struct ptp_lowerhalf_s *lower,
+                             struct timespec *ts,
+                             struct ptp_system_timestamp *sts)
+{
+  uint32_t high1;
+  uint32_t low;
+  uint32_t high2;
+  uint32_t subsec;
+
+  high1 = getreg32(STM32_ETH_PTPTSHR);
+  low   = getreg32(STM32_ETH_PTPTSLR);
+  high2 = getreg32(STM32_ETH_PTPTSHR);
+
+  if (high1 != high2)
+    {
+      low = getreg32(STM32_ETH_PTPTSLR);
+    }
+
+  ts->tv_sec = high2;
+  subsec = (low & ETH_PTPTSLR_MASK) << 1;
+  ts->tv_nsec = ((uint64_t)subsec * NSEC_PER_SEC) >> 32;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_ptp_settime
+ *
+ * Description:
+ *   Set the current time on the PTP hardware clock.
+ *
+ * Input Parameters:
+ *   lower - Pointer to the PTP clock lower-half instance
+ *   ts    - Time value to set
+ *
+ * Returned Value:
+ *   OK on success, negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_ptp_settime(struct ptp_lowerhalf_s *lower,
+                             const struct timespec *ts)
+{
+  uint32_t regval;
+  uint32_t subsec;
+
+  if (ts->tv_nsec < 0 || ts->tv_nsec >= NSEC_PER_SEC)
+    {
+      return -EINVAL;
+    }
+
+  subsec = (((uint64_t)ts->tv_nsec << 32) / NSEC_PER_SEC) >> 1;
+  subsec &= ETH_PTPTSLR_MASK;
+
+  stm32_putreg((uint32_t)ts->tv_sec, STM32_ETH_PTPTSHUR);
+  stm32_putreg(subsec, STM32_ETH_PTPTSLUR);
+
+  regval = stm32_getreg(STM32_ETH_PTPTSCR);
+  stm32_putreg(regval | ETH_PTPTSCR_TSSTI, STM32_ETH_PTPTSCR);
+  up_udelay(1);
+
+  if (stm32_getreg(STM32_ETH_PTPTSCR) & ETH_PTPTSCR_TSSTI)
+    {
+      nerr("PTP timestamp update failed\n");
+      return -EBUSY;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_ptp_getres
+ *
+ * Description:
+ *   Get the resolution of the PTP hardware clock (1 ns).
+ *
+ * Input Parameters:
+ *   lower - Pointer to the PTP clock lower-half instance
+ *   res   - Location to store the resolution
+ *
+ * Returned Value:
+ *   OK on success.
+ *
+ ****************************************************************************/
+
+static int stm32_ptp_getres(struct ptp_lowerhalf_s *lower,
+                            struct timespec *res)
+{
+  res->tv_sec  = 0;
+  res->tv_nsec = 1;
+  return OK;
+}
+
+static const struct ptp_ops_s g_stm32_ptp_ops =
+{
+  stm32_ptp_adjfine,  /* adjfine */
+  stm32_ptp_adjphase, /* adjphase */
+  stm32_ptp_adjtime,  /* adjtime */
+  stm32_ptp_gettime,  /* gettime */
+  NULL,               /* getcrosststamp */
+  stm32_ptp_settime,  /* settime */
+  stm32_ptp_getres,   /* getres */
+};
+#endif
+
 #ifdef CONFIG_STM32_ETH_PTP_RTC_HIRES
 /****************************************************************************
  * Name: stm32_eth_ptp_gettime
@@ -4242,6 +4467,18 @@ int stm32_ethinitialize(int intf)
   /* Register the device with the OS so that socket IOCTLs can be performed */
 
   netdev_register(&priv->dev, NET_LL_ETHERNET);
+
+#if defined(CONFIG_STM32_ETH_PTP) && defined(CONFIG_PTP_CLOCK)
+  /* Register the PTP clock character driver (/dev/ptp0) */
+
+  priv->ptp_lower.ops = &g_stm32_ptp_ops;
+  ret = ptp_clock_register(&priv->ptp_lower, 500000000, intf);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to register PTP clock: %d\n", ret);
+    }
+#endif
+
   return OK;
 }
 
@@ -4416,7 +4653,7 @@ int up_rtc_settime(const struct timespec *tp)
 
 int up_rtc_adjtime(long ppb)
 {
-  return stm32_eth_ptp_adjust(ppb);
+  return stm32_ptp_adjfine(NULL, ppb);
 }
 
 #endif /* CONFIG_STM32_ETH_PTP_RTC_HIRES */

Reply via email to