mirror of
https://github.com/apache/nuttx.git
synced 2026-10-02 03:46:25 +00:00
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 <danieloak@gmail.com> Assisted-by: Claude:claude-sonnet-5
This commit is contained in:
parent
4e6b805a0d
commit
e3dfc9875d
3 changed files with 166 additions and 3 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue