Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions arch/arm/src/common/stm32/Kconfig.eth
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ config STM32_ETH_TIMESTAMP_RX
Timestamp all received Ethernet packets.
Timestamp is available to application through SO_TIMESTAMP socket option.

config STM32_ETH_TIMESTAMP_TX
bool "Hardware timestamping of transmitted packets"
depends on STM32_COMMON_LEGACY && STM32_ETH_PTP && NET_TIMESTAMP && STM32_ETH_ENHANCEDDESC
default n
---help---
Enables hardware timestamp capture on transmitted Ethernet frames.
Timestamp is looped back to userspace via MSG_ERRQUEUE with SO_TIMESTAMPING.

config STM32_RMII
bool
default !STM32_MII
Expand Down
154 changes: 146 additions & 8 deletions arch/arm/src/common/stm32/stm32_eth_m3m4_v1.c
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@
*/

#ifdef CONFIG_NET_PROMISCUOUS
# define MACFFR_SET_BITS (ETH_MACFFR_PCF_PAUSE | ETH_MACFFR_PM)
# define MACFFR_SET_BITS (ETH_MACFFR_PCF_ALL | ETH_MACFFR_PM)
#else
# define MACFFR_SET_BITS (ETH_MACFFR_PCF_PAUSE)
#endif
Expand Down Expand Up @@ -635,6 +635,13 @@ struct stm32_ethmac_s
uint32_t rxtimelow; /* Received packet timestamp subsecond */
uint32_t rxtimehigh; /* Received packet timestamp seconds */
#endif
#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX
struct iob_queue_s tx_tstampq; /* Pending TX timestamp loopback packets */

/* Per-descriptor TX clone */

struct iob_s *tx_meta[CONFIG_STM32_ETH_NTXDESC];
#endif
#if defined(CONFIG_STM32_ETH_PTP) && defined(CONFIG_PTP_CLOCK)
struct ptp_lowerhalf_s ptp_lower; /* PTP hardware clock lower half */
#endif
Expand Down Expand Up @@ -709,6 +716,9 @@ static int stm32_recvframe(struct stm32_ethmac_s *priv);
static void stm32_receive(struct stm32_ethmac_s *priv);
static void stm32_freeframe(struct stm32_ethmac_s *priv);
static void stm32_txdone(struct stm32_ethmac_s *priv);
#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX
static void stm32_tx_tstamp_flush(struct stm32_ethmac_s *priv);
#endif

static void stm32_interrupt_work(void *arg);
static int stm32_interrupt(int irq, void *context, void *arg);
Expand Down Expand Up @@ -798,6 +808,14 @@ static void stm32_eth_ptp_convert_rxtime(struct stm32_ethmac_s *priv);
* Private Functions
****************************************************************************/

#ifdef CONFIG_STM32_ETH_PTP
static inline void ptp_to_timespec(uint64_t timestamp, struct timespec *ts)
{
ts->tv_sec = (timestamp >> 32);
ts->tv_nsec = ((uint32_t)timestamp * (uint64_t)NSEC_PER_SEC) >> 32;
}
#endif

/****************************************************************************
* Name: stm32_getreg
*
Expand Down Expand Up @@ -1162,6 +1180,29 @@ static int stm32_transmit(struct stm32_ethmac_s *priv)

txdesc->tdes0 |= (ETH_TDES0_FS | ETH_TDES0_LS | ETH_TDES0_IC);

#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX
{
uint32_t txindex = txdesc - g_txtable;

priv->tx_meta[txindex] = NULL;
if (priv->dev.d_iob != NULL && priv->dev.d_iob->io_conn != NULL)
{
struct iob_s *clone = netdev_iob_clone(&priv->dev, false);

if (clone != NULL)
{
clone->io_conn = priv->dev.d_iob->io_conn;
priv->tx_meta[txindex] = clone;
txdesc->tdes0 |= ETH_TDES0_TTSE;
}
else
{
nerr("ERROR: Failed to clone IOB for TX timestamp\n");
}
}
}
#endif

/* Set frame size */

DEBUGASSERT(priv->dev.d_len <= CONFIG_STM32_ETH_BUFSIZE);
Expand Down Expand Up @@ -1700,6 +1741,46 @@ static int stm32_recvframe(struct stm32_ethmac_s *priv)
return -EAGAIN;
}

/****************************************************************************
* Function: stm32_tx_tstamp_flush
*
* Description:
* Deliver pending TX hardware timestamp loopback packets to the network
* stack.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/

#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX
static void stm32_tx_tstamp_flush(struct stm32_ethmac_s *priv)
{
struct net_driver_s *dev = &priv->dev;

while (!IOB_QEMPTY(&priv->tx_tstampq))
{
struct iob_s *iob = iob_remove_queue(&priv->tx_tstampq);

dev->d_iob = iob;
dev->d_len = iob->io_pktlen;
#ifdef CONFIG_NET_PKT
pkt_input(dev);
#endif
dev->d_iob = NULL;
dev->d_len = 0;
iob->io_conn = NULL;
iob_free_chain(iob);
}
}
#endif

/****************************************************************************
* Function: stm32_receive
*
Expand All @@ -1721,6 +1802,12 @@ static void stm32_receive(struct stm32_ethmac_s *priv)
{
struct net_driver_s *dev = &priv->dev;

#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX
/* Return any TX hardware timestamp loopback packets first */

stm32_tx_tstamp_flush(priv);
#endif

/* Loop while while stm32_recvframe() successfully retrieves valid
* Ethernet frames.
*/
Expand Down Expand Up @@ -1833,7 +1920,18 @@ static void stm32_receive(struct stm32_ethmac_s *priv)
else
#endif
{
nerr("ERROR: Dropped, Unknown type: %04x\n", BUF->type);
#ifdef CONFIG_NET_PKT
/* Frames that packet sockets consume directly (PTP, Ethertype
* 0x88f7, and IPv6) were already delivered via pkt_input()
* above, so they are not "unknown" and must not be logged as
* dropped.
*/

if (BUF->type != HTONS(0x88f7) && BUF->type != HTONS(ETHTYPE_IP6))
#endif
{
nerr("ERROR: Dropped, Unknown type: %04x\n", BUF->type);
}
}

/* We are finished with the RX buffer. NOTE: If the buffer is
Expand Down Expand Up @@ -1912,6 +2010,29 @@ static void stm32_freeframe(struct stm32_ethmac_s *priv)

if ((txdesc->tdes0 & ETH_TDES0_LS) != 0)
{
#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX
uint32_t tail = txdesc - g_txtable;
struct iob_s *clone = priv->tx_meta[tail];

priv->tx_meta[tail] = NULL;
if (clone != NULL)
{
if ((txdesc->tdes0 & ETH_TDES0_TTSS) != 0)
{
uint64_t hw_time = ((uint64_t)txdesc->tdes7 << 32)
| ((txdesc->tdes6 & ETH_PTPTSLR_MASK)
<< 1);

ptp_to_timespec(hw_time, &clone->io_time);
iob_add_queue(clone, &priv->tx_tstampq);
}
else
{
iob_free_chain(clone);
}
}
#endif

/* Yes.. Decrement the number of frames "in-flight". */

priv->inflight--;
Expand Down Expand Up @@ -1975,6 +2096,12 @@ static void stm32_txdone(struct stm32_ethmac_s *priv)

stm32_freeframe(priv);

#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX
/* Deliver any pending TX hardware timestamp loopback packets */

stm32_tx_tstamp_flush(priv);
#endif

/* If no further xmits are pending, then cancel the TX timeout */

if (priv->inflight <= 0)
Expand Down Expand Up @@ -2341,6 +2468,9 @@ static int stm32_ifdown(struct net_driver_s *dev)
(struct stm32_ethmac_s *)dev->d_private;
irqstate_t flags;
int ret = OK;
#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX
int i;
#endif

ninfo("Taking the network down\n");

Expand Down Expand Up @@ -2378,6 +2508,20 @@ static int stm32_ifdown(struct net_driver_s *dev)
"still assuming it's going down.\n");
}

#ifdef CONFIG_STM32_ETH_TIMESTAMP_TX
/* Drain any pending TX timestamp loopback packets and clones */

iob_free_queue(&priv->tx_tstampq);
for (i = 0; i < CONFIG_STM32_ETH_NTXDESC; i++)
{
if (priv->tx_meta[i] != NULL)
{
iob_free_chain(priv->tx_meta[i]);
priv->tx_meta[i] = NULL;
}
}
#endif

/* Mark the device "down" */

priv->ifup = false;
Expand Down Expand Up @@ -3981,12 +4125,6 @@ static uint64_t stm32_eth_ptp_gettime(void)
}
#endif

static inline void ptp_to_timespec(uint64_t timestamp, struct timespec *ts)
{
ts->tv_sec = (timestamp >> 32);
ts->tv_nsec = ((uint32_t)timestamp * (uint64_t)NSEC_PER_SEC) >> 32;
}

/* Convert RX timestamp to CLOCK_REALTIME */
#ifdef CONFIG_STM32_ETH_TIMESTAMP_RX
static void stm32_eth_ptp_convert_rxtime(struct stm32_ethmac_s *priv)
Expand Down
Loading