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

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

commit e3dfc9875d71b2bf0cdb7d340a5c80fe1917d08c
Author: Daniel P. Carvalho <[email protected]>
AuthorDate: Sun Sep 20 20:51:45 2026 -0300

    arch/arm/stm32h7: timestamp the received PTP frames.
    
    Add STM32_ETH_TIMESTAMP_RX for the STM32H7, as the one of the legacy
    STM32 that provides the timestamp of the frames received.
    
    Timestamp the PTP version 2 messages, over Ethernet and over UDP, except
    for the announce, management and signaling messages. The MAC writes the
    timestamp in a context descriptor after the last descriptor of the frame.
    The driver reads it before giving the frame to the network stack and
    passes it in d_rxtime, in the time of the system time of the MAC, the same
    as /dev/ptp0. A frame that is not timestamped has a time of zero.
    
    The timestamp goes over the address of the buffer of the context
    descriptor, and the code that dropped the context descriptors used a
    pointer that was never set. Keep the address of the buffer of each RX
    descriptor, and restore it when a context descriptor is given back.
    
    The timestamps of the PTP frames of a grandmaster clock were checked on
    hardware against the system time of the MAC, and were within the delay of
    the reads.
    
    Signed-off-by: Daniel P. Carvalho <[email protected]>
    Assisted-by: Claude:claude-sonnet-5
---
 arch/arm/src/common/stm32/Kconfig.eth          |   2 +-
 arch/arm/src/stm32h7/hardware/stm32_ethernet.h |   1 +
 arch/arm/src/stm32h7/stm32_ethernet.c          | 166 ++++++++++++++++++++++++-
 3 files changed, 166 insertions(+), 3 deletions(-)

diff --git a/arch/arm/src/common/stm32/Kconfig.eth 
b/arch/arm/src/common/stm32/Kconfig.eth
index 827b721a269..ea8a9cf7c57 100644
--- a/arch/arm/src/common/stm32/Kconfig.eth
+++ b/arch/arm/src/common/stm32/Kconfig.eth
@@ -187,7 +187,7 @@ config STM32_ETH_PTP_RTC_HIRES
 
 config STM32_ETH_TIMESTAMP_RX
        bool "Hardware timestamping of received packets"
-       depends on STM32_COMMON_LEGACY && STM32_ETH_PTP && NET_TIMESTAMP && 
STM32_ETH_ENHANCEDDESC
+       depends on STM32_ETH_PTP && NET_TIMESTAMP && (STM32_COMMON_H7_H5 || 
(STM32_COMMON_LEGACY && STM32_ETH_ENHANCEDDESC))
        default n
        ---help---
                Timestamp all received Ethernet packets.
diff --git a/arch/arm/src/stm32h7/hardware/stm32_ethernet.h 
b/arch/arm/src/stm32h7/hardware/stm32_ethernet.h
index c8b04b4c475..5d4fca932e7 100644
--- a/arch/arm/src/stm32h7/hardware/stm32_ethernet.h
+++ b/arch/arm/src/stm32h7/hardware/stm32_ethernet.h
@@ -363,6 +363,7 @@
 #define ETH_MACTSCR_TSMSTRENA       (1 << 15) /* Bit 15: Enable snapshot for 
messages relevant to master */
 #define ETH_MACTSCR_SNAPTYPSEL_SHIFT (16)     /* Bits 16-17: Select PTP 
packets for taking snapshots */
 #define ETH_MACTSCR_SNAPTYPSEL_MASK (0x3 << ETH_MACTSCR_SNAPTYPSEL_SHIFT)
+#  define ETH_MACTSCR_SNAPTYPSEL_1 (1 << ETH_MACTSCR_SNAPTYPSEL_SHIFT)
 #define ETH_MACTSCR_TSENMACADDR     (1 << 18) /* Bit 18: Enable MAC address 
for PTP packet filtering */
 #define ETH_MACTSCR_CSC             (1 << 19) /* Bit 19: Checksum correction 
for PTP over UDP/IPv4 (one-step) */
 #define ETH_MACTSCR_TXTSSTSM        (1 << 24) /* Bit 24: Transmit timestamp 
status mode */
diff --git a/arch/arm/src/stm32h7/stm32_ethernet.c 
b/arch/arm/src/stm32h7/stm32_ethernet.c
index 388ca220666..0e454806170 100644
--- a/arch/arm/src/stm32h7/stm32_ethernet.c
+++ b/arch/arm/src/stm32h7/stm32_ethernet.c
@@ -275,6 +275,15 @@
 
 #  define STM32_PTP_UPDATE_USTIMEOUT  (1000)
 
+#  ifdef CONFIG_STM32_ETH_TIMESTAMP_RX
+
+/* Time to wait for the DMA to write the context descriptor that follows
+ * the last descriptor of a timestamped frame, in microseconds.
+ */
+
+#    define STM32_PTP_CTX_USTIMEOUT   (10)
+#  endif
+
 /* The addend can be trimmed by up to 50% each way from its nominal value,
  * in parts per billion.
  */
@@ -756,6 +765,8 @@ struct stm32_ethmac_s
   struct eth_desc_s *txchbase;      /* TX descriptor ring base address */
   struct eth_desc_s *rxchbase;      /* RX descriptor ring base address */
 
+  uint32_t rxbuf[CONFIG_STM32_ETH_NRXDESC]; /* Buffer of each RX descriptor */
+
   struct eth_desc_s *txtail;        /* First "in_flight" TX descriptor */
   struct eth_desc_s *rxcurr;        /* First RX descriptor of the segment */
   uint16_t             segments;    /* RX segment count */
@@ -1762,6 +1773,124 @@ static void stm32_freesegment(struct stm32_ethmac_s 
*priv,
     }
 }
 
+/****************************************************************************
+ * Function: stm32_rxindex
+ *
+ * Description:
+ *   Get the position of an RX descriptor in the ring. The descriptors of
+ *   the ring are the size of the union that also holds a TX descriptor,
+ *   not of struct eth_desc_s.
+ *
+ ****************************************************************************/
+
+static inline int stm32_rxindex(struct stm32_ethmac_s *priv,
+                                struct eth_desc_s *rxdesc)
+{
+  return (union stm32_desc_u *)rxdesc - (union stm32_desc_u *)priv->rxchbase;
+}
+
+/****************************************************************************
+ * Function: stm32_freectxdesc
+ *
+ * Description:
+ *   Give a context descriptor back to the DMA. The DMA writes the
+ *   timestamp over the buffer address, so the address is restored from the
+ *   copy that the driver keeps.
+ *
+ * Parameters:
+ *   priv    - Reference to the driver state structure
+ *   ctxdesc - The context descriptor
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void stm32_freectxdesc(struct stm32_ethmac_s *priv,
+                              struct eth_desc_s *ctxdesc)
+{
+  ctxdesc->des0 = priv->rxbuf[stm32_rxindex(priv, ctxdesc)];
+  ctxdesc->des1 = 0;
+  ctxdesc->des2 = 0;
+  stm32_freesegment(priv, ctxdesc, 1);
+}
+
+#ifdef CONFIG_STM32_ETH_TIMESTAMP_RX
+/****************************************************************************
+ * Function: stm32_rxtimestamp
+ *
+ * Description:
+ *   Get the hardware timestamp of a received frame, and put it in d_rxtime.
+ *   The timestamp is in the context descriptor that the DMA writes right
+ *   after the last descriptor of the frame. A frame without a timestamp
+ *   gets a time of zero.
+ *
+ * Parameters:
+ *   priv - Reference to the driver state structure
+ *   last - The last descriptor of the frame
+ *
+ * Returned Value:
+ *   The context descriptor, that the caller has to give back to the DMA
+ *   after the frame, or NULL if the frame has none. A context descriptor
+ *   that the DMA does not write in time is found later by the scan of the
+ *   descriptors, and dropped.
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static struct eth_desc_s *stm32_rxtimestamp(struct stm32_ethmac_s *priv,
+                                            struct eth_desc_s *last)
+{
+  struct net_driver_s *dev = &priv->dev;
+  struct eth_desc_s *ctxdesc;
+  int i;
+
+  dev->d_rxtime.tv_sec  = 0;
+  dev->d_rxtime.tv_nsec = 0;
+
+  if ((last->des3 & ETH_RDES3_WB_RS1V) == 0 ||
+      (last->des1 & ETH_RDES1_WB_TSA) == 0)
+    {
+      return NULL;
+    }
+
+  ctxdesc = stm32_get_next_rxdesc(priv, last);
+
+  for (i = 0; i < STM32_PTP_CTX_USTIMEOUT; i++)
+    {
+      up_invalidate_dcache((uintptr_t)ctxdesc,
+                           (uintptr_t)ctxdesc + sizeof(struct eth_desc_s));
+
+      if ((ctxdesc->des3 & ETH_RDES3_WB_OWN) == 0)
+        {
+          break;
+        }
+
+      up_udelay(1);
+    }
+
+  if ((ctxdesc->des3 & ETH_RDES3_WB_OWN) != 0 ||
+      (ctxdesc->des3 & ETH_RDES3_WB_CTXT) == 0)
+    {
+      return NULL;
+    }
+
+  /* The nanoseconds are in the first word and the seconds in the second,
+   * and all ones is a timestamp that is not valid.
+   */
+
+  if (ctxdesc->des0 != UINT32_MAX || ctxdesc->des1 != UINT32_MAX)
+    {
+      dev->d_rxtime.tv_sec  = ctxdesc->des1;
+      dev->d_rxtime.tv_nsec = ctxdesc->des0;
+    }
+
+  return ctxdesc;
+}
+#endif /* CONFIG_STM32_ETH_TIMESTAMP_RX */
+
 /****************************************************************************
  * Function: stm32_recvframe
  *
@@ -1787,6 +1916,9 @@ static int stm32_recvframe(struct stm32_ethmac_s *priv)
 {
   struct eth_desc_s *rxdesc;
   struct eth_desc_s *rxcurr = NULL;
+#ifdef CONFIG_STM32_ETH_TIMESTAMP_RX
+  struct eth_desc_s *ctxdesc;
+#endif
   uint8_t *buffer;
   int i;
 
@@ -1910,6 +2042,8 @@ static int stm32_recvframe(struct stm32_ethmac_s *priv)
                       DEBUGASSERT(dev->d_buf == NULL);
                       dev->d_buf    = (uint8_t *)rxcurr->des0;
                       rxcurr->des0 = (uint32_t)buffer;
+                      priv->rxbuf[stm32_rxindex(priv, rxcurr)] =
+                        (uint32_t)buffer;
 
                       /* Make sure that the modified RX descriptor is written
                        * to physical memory.
@@ -1924,8 +2058,22 @@ static int stm32_recvframe(struct stm32_ethmac_s *priv)
                        */
 
                       priv->rxhead   = stm32_get_next_rxdesc(priv, rxdesc);
+
+#ifdef CONFIG_STM32_ETH_TIMESTAMP_RX
+                      ctxdesc = stm32_rxtimestamp(priv, rxdesc);
+#endif
+
                       stm32_freesegment(priv, rxcurr, priv->segments);
 
+#ifdef CONFIG_STM32_ETH_TIMESTAMP_RX
+                      if (ctxdesc != NULL)
+                        {
+                          priv->rxhead =
+                            stm32_get_next_rxdesc(priv, ctxdesc);
+                          stm32_freectxdesc(priv, ctxdesc);
+                        }
+#endif
+
                       /* Force the completed RX DMA buffer to be re-read from
                        * physical memory.
                        */
@@ -1960,8 +2108,7 @@ static int stm32_recvframe(struct stm32_ethmac_s *priv)
         {
           /* Drop the context descriptors, we are not interested */
 
-          DEBUGASSERT(rxcurr != NULL);
-          stm32_freesegment(priv, rxcurr, 1);
+          stm32_freectxdesc(priv, rxdesc);
         }
 
       /* Try the next descriptor */
@@ -3053,6 +3200,7 @@ static void stm32_rxdescinit(struct stm32_ethmac_s *priv,
       /* Set Buffer1 address pointer */
 
       rxdesc->des0 = (uint32_t)&rxbuffer[i * ALIGNED_BUFSIZE];
+      priv->rxbuf[i] = rxdesc->des0;
 
       /* Set Buffer1 address high bytes */
 
@@ -4044,6 +4192,16 @@ static int stm32_eth_ptp_init(void)
    */
 
   tscr = ETH_MACTSCR_TSENA | ETH_MACTSCR_TSCFUPDT | ETH_MACTSCR_TSCTRLSSR;
+
+#ifdef CONFIG_STM32_ETH_TIMESTAMP_RX
+  /* Timestamp the received PTP version 2 messages, over Ethernet and over
+   * UDP, except for the announce, management and signaling messages.
+   */
+
+  tscr |= ETH_MACTSCR_TSVER2ENA | ETH_MACTSCR_TSIPENA |
+          ETH_MACTSCR_TSIPV4ENA | ETH_MACTSCR_TSIPV6ENA |
+          ETH_MACTSCR_SNAPTYPSEL_1;
+#endif
   stm32_putreg(tscr, STM32_ETH_MACTSCR);
 
   /* Set the increment of the system time */
@@ -4831,6 +4989,10 @@ static inline int stm32_ethinitialize(int intf)
 
   stm32_ifdown(&priv->dev);
 
+#ifdef CONFIG_STM32_ETH_TIMESTAMP_RX
+  priv->dev.d_features |= NETDEV_RX_STAMP;
+#endif
+
   /* Register the device with the OS so that socket IOCTLs can be performed */
 
   netdev_register(&priv->dev, NET_LL_ETHERNET);

Reply via email to