diff --git a/arch/arm/src/stm32h7/hardware/stm32_ethernet.h b/arch/arm/src/stm32h7/hardware/stm32_ethernet.h index 5765047abe4..c8b04b4c475 100644 --- a/arch/arm/src/stm32h7/hardware/stm32_ethernet.h +++ b/arch/arm/src/stm32h7/hardware/stm32_ethernet.h @@ -66,6 +66,22 @@ #define STM32_ETH_MACA0HR_OFFSET 0x0300 /* Address 0 high register */ #define STM32_ETH_MACA0LR_OFFSET 0x0304 /* Address 0 low register */ +/* MAC Timestamp Registers */ + +#define STM32_ETH_MACTSCR_OFFSET 0x0B00 /* Ethernet MAC timestamp control register */ +#define STM32_ETH_MACSSIR_OFFSET 0x0B04 /* Ethernet MAC sub-second increment register */ +#define STM32_ETH_MACSTSR_OFFSET 0x0B08 /* Ethernet MAC system time seconds register */ +#define STM32_ETH_MACSTNR_OFFSET 0x0B0C /* Ethernet MAC system time nanoseconds register */ +#define STM32_ETH_MACSTSUR_OFFSET 0x0B10 /* Ethernet MAC system time seconds update register */ +#define STM32_ETH_MACSTNUR_OFFSET 0x0B14 /* Ethernet MAC system time nanoseconds update register */ +#define STM32_ETH_MACTSAR_OFFSET 0x0B18 /* Ethernet MAC timestamp addend register */ +#define STM32_ETH_MACTSSR_OFFSET 0x0B20 /* Ethernet MAC timestamp status register */ +#define STM32_ETH_MACPPSCR_OFFSET 0x0B70 /* Ethernet MAC PPS control register */ +#define STM32_ETH_MACPPSTTSR_OFFSET 0x0B80 /* Ethernet MAC PPS target time seconds register */ +#define STM32_ETH_MACPPSTTNR_OFFSET 0x0B84 /* Ethernet MAC PPS target time nanoseconds register */ +#define STM32_ETH_MACPPSIR_OFFSET 0x0B88 /* Ethernet MAC PPS interval register */ +#define STM32_ETH_MACPPSWR_OFFSET 0x0B8C /* Ethernet MAC PPS width register */ + /* MTL Registers */ #define STM32_ETH_MTLOMR_OFFSET 0x0C00 /* Operating mode Register */ @@ -121,6 +137,22 @@ #define STM32_ETH_MACA0LR (STM32_EMAC_BASE+STM32_ETH_MACA0LR_OFFSET) #define STM32_ETH_MACA0HR (STM32_EMAC_BASE+STM32_ETH_MACA0HR_OFFSET) +/* MAC Timestamp Registers */ + +#define STM32_ETH_MACTSCR (STM32_EMAC_BASE+STM32_ETH_MACTSCR_OFFSET) +#define STM32_ETH_MACSSIR (STM32_EMAC_BASE+STM32_ETH_MACSSIR_OFFSET) +#define STM32_ETH_MACSTSR (STM32_EMAC_BASE+STM32_ETH_MACSTSR_OFFSET) +#define STM32_ETH_MACSTNR (STM32_EMAC_BASE+STM32_ETH_MACSTNR_OFFSET) +#define STM32_ETH_MACSTSUR (STM32_EMAC_BASE+STM32_ETH_MACSTSUR_OFFSET) +#define STM32_ETH_MACSTNUR (STM32_EMAC_BASE+STM32_ETH_MACSTNUR_OFFSET) +#define STM32_ETH_MACTSAR (STM32_EMAC_BASE+STM32_ETH_MACTSAR_OFFSET) +#define STM32_ETH_MACTSSR (STM32_EMAC_BASE+STM32_ETH_MACTSSR_OFFSET) +#define STM32_ETH_MACPPSCR (STM32_EMAC_BASE+STM32_ETH_MACPPSCR_OFFSET) +#define STM32_ETH_MACPPSTTSR (STM32_EMAC_BASE+STM32_ETH_MACPPSTTSR_OFFSET) +#define STM32_ETH_MACPPSTTNR (STM32_EMAC_BASE+STM32_ETH_MACPPSTTNR_OFFSET) +#define STM32_ETH_MACPPSIR (STM32_EMAC_BASE+STM32_ETH_MACPPSIR_OFFSET) +#define STM32_ETH_MACPPSWR (STM32_EMAC_BASE+STM32_ETH_MACPPSWR_OFFSET) + /* MTL Registers */ #define STM32_ETH_MTLOMR (STM32_EMAC_BASE+STM32_ETH_MTLOMR_OFFSET) @@ -314,6 +346,72 @@ #define STM32_ETH_MACAxHR_SA (1 << 30) /* Bit 30: Source Address */ #define STM32_ETH_MACAxHR_AE (1 << 31) /* Bit 31: Address Enable */ +/* Ethernet MAC timestamp control register */ + +#define ETH_MACTSCR_TSENA (1 << 0) /* Bit 0: Enable timestamp */ +#define ETH_MACTSCR_TSCFUPDT (1 << 1) /* Bit 1: Fine or coarse timestamp update */ +#define ETH_MACTSCR_TSINIT (1 << 2) /* Bit 2: Initialize timestamp */ +#define ETH_MACTSCR_TSUPDT (1 << 3) /* Bit 3: Update timestamp */ +#define ETH_MACTSCR_TSADDREG (1 << 5) /* Bit 5: Update addend register */ +#define ETH_MACTSCR_TSENALL (1 << 8) /* Bit 8: Enable timestamp for all packets */ +#define ETH_MACTSCR_TSCTRLSSR (1 << 9) /* Bit 9: Timestamp digital or binary rollover control */ +#define ETH_MACTSCR_TSVER2ENA (1 << 10) /* Bit 10: Enable PTP packet processing for version 2 format */ +#define ETH_MACTSCR_TSIPENA (1 << 11) /* Bit 11: Enable processing of PTP over Ethernet packets */ +#define ETH_MACTSCR_TSIPV6ENA (1 << 12) /* Bit 12: Enable processing of PTP packets sent over IPv6-UDP */ +#define ETH_MACTSCR_TSIPV4ENA (1 << 13) /* Bit 13: Enable processing of PTP packets sent over IPv4-UDP */ +#define ETH_MACTSCR_TSEVNTENA (1 << 14) /* Bit 14: Enable timestamp snapshot for event messages */ +#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_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 */ + +/* Ethernet MAC sub-second increment register */ + +#define ETH_MACSSIR_SNSINC_SHIFT (8) /* Bits 8-15: Sub-nanosecond increment value */ +#define ETH_MACSSIR_SNSINC_MASK (0xFF << ETH_MACSSIR_SNSINC_SHIFT) +#define ETH_MACSSIR_SSINC_SHIFT (16) /* Bits 16-23: Sub-second increment value */ +#define ETH_MACSSIR_SSINC_MASK (0xFF << ETH_MACSSIR_SSINC_SHIFT) + +/* Ethernet MAC system time nanoseconds register */ + +#define ETH_MACSTNR_TSSS_SHIFT (0) /* Bits 0-30: Timestamp sub-seconds */ +#define ETH_MACSTNR_TSSS_MASK (0x7FFFFFFF << ETH_MACSTNR_TSSS_SHIFT) + +/* Ethernet MAC system time nanoseconds update register */ + +#define ETH_MACSTNUR_TSSS_SHIFT (0) /* Bits 0-30: Timestamp sub-seconds */ +#define ETH_MACSTNUR_TSSS_MASK (0x7FFFFFFF << ETH_MACSTNUR_TSSS_SHIFT) +#define ETH_MACSTNUR_ADDSUB (1 << 31) /* Bit 31: Add or subtract time */ + +/* Ethernet MAC timestamp status register */ + +#define ETH_MACTSSR_TSSOVF (1 << 0) /* Bit 0: Timestamp seconds overflow */ +#define ETH_MACTSSR_TSTARGT0 (1 << 1) /* Bit 1: Timestamp target time reached */ +#define ETH_MACTSSR_AUXTSTRIG (1 << 2) /* Bit 2: Auxiliary timestamp trigger snapshot */ +#define ETH_MACTSSR_TSTRGTERR0 (1 << 3) /* Bit 3: Timestamp target time error */ +#define ETH_MACTSSR_TXTSSIS (1 << 15) /* Bit 15: Tx timestamp status interrupt status */ +#define ETH_MACTSSR_ATSSTN_SHIFT (16) /* Bits 16-19: Auxiliary timestamp snapshot trigger identifier */ +#define ETH_MACTSSR_ATSSTN_MASK (0xF << ETH_MACTSSR_ATSSTN_SHIFT) +#define ETH_MACTSSR_ATSSTM (1 << 24) /* Bit 24: Auxiliary timestamp snapshot trigger missed */ +#define ETH_MACTSSR_ATSNS_SHIFT (25) /* Bits 25-29: Number of auxiliary timestamp snapshots */ +#define ETH_MACTSSR_ATSNS_MASK (0x1F << ETH_MACTSSR_ATSNS_SHIFT) + +/* Ethernet MAC PPS control register */ + +#define ETH_MACPPSCR_PPSCTRL_SHIFT (0) /* Bits 0-3: PPS output frequency control or command */ +#define ETH_MACPPSCR_PPSCTRL_MASK (0xF << ETH_MACPPSCR_PPSCTRL_SHIFT) +# define ETH_MACPPSCR_PPSCMD_START_TRAIN (2 << ETH_MACPPSCR_PPSCTRL_SHIFT) +# define ETH_MACPPSCR_PPSCMD_STOP_NOW (5 << ETH_MACPPSCR_PPSCTRL_SHIFT) +#define ETH_MACPPSCR_PPSEN0 (1 << 4) /* Bit 4: Flexible PPS output mode enable */ + +/* Ethernet MAC PPS target time nanoseconds register */ + +#define ETH_MACPPSTTNR_TTSL0_SHIFT (0) /* Bits 0-30: Target time low */ +#define ETH_MACPPSTTNR_TTSL0_MASK (0x7FFFFFFF << ETH_MACPPSTTNR_TTSL0_SHIFT) +#define ETH_MACPPSTTNR_TRGTBUSY0 (1 << 31) /* Bit 31: PPS target time register busy */ + /* Ethernet MTL registers */ #define ETH_MTLOMR_DTXSTS (1 << 1) /* Bit 1: Drop Transmit Status */ diff --git a/arch/arm/src/stm32h7/stm32_ethernet.c b/arch/arm/src/stm32h7/stm32_ethernet.c index d6e5e36a776..388ca220666 100644 --- a/arch/arm/src/stm32h7/stm32_ethernet.c +++ b/arch/arm/src/stm32h7/stm32_ethernet.c @@ -51,6 +51,7 @@ #include #include #include +#include #if defined(CONFIG_NET_PKT) # include @@ -253,7 +254,47 @@ #endif #ifdef CONFIG_STM32_ETH_PTP -# warning "CONFIG_STM32_ETH_PTP is not yet supported" +/* The timestamp unit is clocked from HCLK. The system time advances by + * STM32_PTP_SSINC nanoseconds each time the 32-bit accumulator of the + * addend register overflows, so its rate is HCLK * addend / 2^32. Aim at + * an update rate of half of HCLK: the nominal addend is then close to 2^31 + * and the frequency can be trimmed by +/- 50 %. + */ + +# define STM32_PTP_CLOCK STM32_HCLK_FREQUENCY +# define STM32_PTP_SSINC ((2 * NSEC_PER_SEC + STM32_PTP_CLOCK / 2) / \ + STM32_PTP_CLOCK) +# define STM32_PTP_ADDEND ((uint32_t)((((uint64_t)1 << 32) * \ + NSEC_PER_SEC) / \ + ((uint64_t)STM32_PTP_SSINC * \ + STM32_PTP_CLOCK))) + +/* Time to wait for the unit to take an update of the addend or of the + * system time, in microseconds. + */ + +# define STM32_PTP_UPDATE_USTIMEOUT (1000) + +/* The addend can be trimmed by up to 50% each way from its nominal value, + * in parts per billion. + */ + +# define STM32_PTP_MAX_ADJ (500000000) + +# ifdef CONFIG_STM32_ETH_PTP_GPIO + +/* The pulse-per-second output is a pulse train with a period of one + * second and a width of half of it, that starts at a whole second of the + * system time. The first pulse is at least STM32_PTP_PPS_MARGIN_NS ahead, + * so the target time is loaded before it. The interval and the width are + * in increments of the system time, minus one. + */ + +# define STM32_PTP_PPS_MARGIN_NS (100000000) +# define STM32_PTP_PPS_INTERVAL (NSEC_PER_SEC / STM32_PTP_SSINC - 1) +# define STM32_PTP_PPS_WIDTH ((NSEC_PER_SEC / 2) / \ + STM32_PTP_SSINC - 1) +# endif #endif #undef CONFIG_STM32_ETH_HWCHECKSUM @@ -722,6 +763,10 @@ struct stm32_ethmac_s sq_queue_t freeb; /* The free buffer list */ struct mdio_bus_s *mdio; + +#if defined(CONFIG_STM32_ETH_PTP) && defined(CONFIG_PTP_CLOCK) + struct ptp_lowerhalf_s ptp_lower; /* PTP hardware clock lower half */ +#endif }; /**************************************************************************** @@ -860,6 +905,9 @@ static inline void stm32_selectrmii(void); #endif static inline void stm32_ethgpioconfig(struct stm32_ethmac_s *priv); static void stm32_ethreset(struct stm32_ethmac_s *priv); +#ifdef CONFIG_STM32_ETH_PTP +static int stm32_eth_ptp_init(void); +#endif static int stm32_macconfig(struct stm32_ethmac_s *priv); static void stm32_macaddress(struct stm32_ethmac_s *priv); static int stm32_macenable(struct stm32_ethmac_s *priv); @@ -3802,6 +3850,460 @@ static inline void stm32_ethgpioconfig(struct stm32_ethmac_s *priv) #endif } +#ifdef CONFIG_STM32_ETH_PTP +/**************************************************************************** + * Name: stm32_eth_ptp_wait + * + * Description: + * Wait for the timestamp unit to clear a bit that is set while it takes + * an update of the addend, of the system time or of the PPS target time. + * + * Input Parameters: + * reg - The register that holds the bit + * bit - The bit to wait for + * + * Returned Value: + * OK on success, -ETIMEDOUT if the bit was not cleared in time. + * + ****************************************************************************/ + +static int stm32_eth_ptp_wait(uint32_t reg, uint32_t bit) +{ + int timeout; + + for (timeout = 0; timeout < STM32_PTP_UPDATE_USTIMEOUT; timeout++) + { + if ((stm32_getreg(reg) & bit) == 0) + { + return OK; + } + + up_udelay(1); + } + + return -ETIMEDOUT; +} + +#if defined(CONFIG_STM32_ETH_PTP_GPIO) || defined(CONFIG_PTP_CLOCK) +/**************************************************************************** + * Name: stm32_eth_ptp_read + * + * Description: + * Read the system time. The seconds and the nanoseconds are in two + * registers, so the seconds are read again to detect a rollover between + * the reads. + * + * Input Parameters: + * sec - The location to store the seconds + * nsec - The location to store the nanoseconds + * + ****************************************************************************/ + +static void stm32_eth_ptp_read(uint32_t *sec, uint32_t *nsec) +{ + uint32_t sec1; + uint32_t sec2; + uint32_t ns; + + sec1 = stm32_getreg(STM32_ETH_MACSTSR); + ns = stm32_getreg(STM32_ETH_MACSTNR); + sec2 = stm32_getreg(STM32_ETH_MACSTSR); + + if (sec1 != sec2) + { + ns = stm32_getreg(STM32_ETH_MACSTNR); + } + + *sec = sec2; + *nsec = ns & ETH_MACSTNR_TSSS_MASK; +} +#endif + +/**************************************************************************** + * Name: stm32_eth_ptp_setaddend + * + * Description: + * Load the addend, that sets the rate of the system time. + * + * Input Parameters: + * addend - The value to load + * + * Returned Value: + * OK on success, -ETIMEDOUT if the unit did not take it in time. + * + ****************************************************************************/ + +static int stm32_eth_ptp_setaddend(uint32_t addend) +{ + stm32_putreg(addend, STM32_ETH_MACTSAR); + stm32_putreg(stm32_getreg(STM32_ETH_MACTSCR) | ETH_MACTSCR_TSADDREG, + STM32_ETH_MACTSCR); + return stm32_eth_ptp_wait(STM32_ETH_MACTSCR, ETH_MACTSCR_TSADDREG); +} + +#ifdef CONFIG_STM32_ETH_PTP_GPIO +/**************************************************************************** + * Name: stm32_eth_ptp_pps_start + * + * Description: + * Start the pulse-per-second output, a pulse train with a period of one + * second and a duty cycle of 50%, aligned to the system time. The fixed + * frequency mode of the MAC gives a pulse too short to be seen, so the + * flexible mode is used. + * + * Returned Value: + * OK on success, a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32_eth_ptp_pps_start(void) +{ + uint32_t sec; + uint32_t nsec; + int ret; + + /* The first pulse is at the next whole second, or at the one after it + * if that is too close to be loaded in time. + */ + + stm32_eth_ptp_read(&sec, &nsec); + sec += (nsec < NSEC_PER_SEC - STM32_PTP_PPS_MARGIN_NS) ? 1 : 2; + + stm32_putreg(sec, STM32_ETH_MACPPSTTSR); + stm32_putreg(0, STM32_ETH_MACPPSTTNR); + + ret = stm32_eth_ptp_wait(STM32_ETH_MACPPSTTNR, ETH_MACPPSTTNR_TRGTBUSY0); + if (ret < 0) + { + nerr("ERROR: Timed out loading the PPS target time\n"); + return ret; + } + + stm32_putreg(STM32_PTP_PPS_INTERVAL, STM32_ETH_MACPPSIR); + stm32_putreg(STM32_PTP_PPS_WIDTH, STM32_ETH_MACPPSWR); + stm32_putreg(ETH_MACPPSCR_PPSEN0 | ETH_MACPPSCR_PPSCMD_START_TRAIN, + STM32_ETH_MACPPSCR); + return OK; +} + +#ifdef CONFIG_PTP_CLOCK +/**************************************************************************** + * Name: stm32_eth_ptp_pps_restart + * + * Description: + * Align the pulse-per-second output to the whole seconds of the system + * time again. The pulse train counts by itself, and does not follow a + * step of the system time, so after a step the pulses would be displaced + * by the same amount. + * + * Returned Value: + * OK on success, a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32_eth_ptp_pps_restart(void) +{ + int ret; + + stm32_putreg(ETH_MACPPSCR_PPSEN0 | ETH_MACPPSCR_PPSCMD_STOP_NOW, + STM32_ETH_MACPPSCR); + ret = stm32_eth_ptp_wait(STM32_ETH_MACPPSCR, ETH_MACPPSCR_PPSCTRL_MASK); + if (ret < 0) + { + nerr("ERROR: Timed out stopping the PPS output\n"); + return ret; + } + + return stm32_eth_ptp_pps_start(); +} +#endif /* CONFIG_PTP_CLOCK */ +#endif /* CONFIG_STM32_ETH_PTP_GPIO */ + +/**************************************************************************** + * Name: stm32_eth_ptp_init + * + * Description: + * Start the system time of the timestamp unit of the MAC, at zero and at + * its nominal rate. The unit needs to be enabled before the addend and + * the system time can be loaded. It does not depend on the link, but it + * needs the MAC to be out of reset, and a reset of the MAC clears it. + * + * Returned Value: + * OK on success, a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32_eth_ptp_init(void) +{ + uint32_t tscr; + int ret; + + /* Enable the unit, with the fine update method (the addend sets the + * rate) and a digital rollover (the nanoseconds count from 0 to + * 999999999). + */ + + tscr = ETH_MACTSCR_TSENA | ETH_MACTSCR_TSCFUPDT | ETH_MACTSCR_TSCTRLSSR; + stm32_putreg(tscr, STM32_ETH_MACTSCR); + + /* Set the increment of the system time */ + + stm32_putreg(STM32_PTP_SSINC << ETH_MACSSIR_SSINC_SHIFT, + STM32_ETH_MACSSIR); + + /* Load the addend for the nominal rate */ + + ret = stm32_eth_ptp_setaddend(STM32_PTP_ADDEND); + if (ret < 0) + { + nerr("ERROR: Timed out loading the PTP addend\n"); + return ret; + } + + /* Initialize the system time to zero */ + + stm32_putreg(0, STM32_ETH_MACSTSUR); + stm32_putreg(0, STM32_ETH_MACSTNUR); + stm32_putreg(tscr | ETH_MACTSCR_TSINIT, STM32_ETH_MACTSCR); + ret = stm32_eth_ptp_wait(STM32_ETH_MACTSCR, ETH_MACTSCR_TSINIT); + if (ret < 0) + { + nerr("ERROR: Timed out initializing the PTP system time\n"); + return ret; + } + +#ifdef CONFIG_STM32_ETH_PTP_GPIO + ret = stm32_eth_ptp_pps_start(); + if (ret < 0) + { + return ret; + } +#endif + + ninfo("PTP: system time started, SSINC=%u addend=%" PRIu32 "\n", + (unsigned int)STM32_PTP_SSINC, (uint32_t)STM32_PTP_ADDEND); + return OK; +} + +#ifdef CONFIG_PTP_CLOCK +/**************************************************************************** + * Name: stm32_ptp_adjfine + * + * Description: + * Adjust the frequency of the system time. + * + * Input Parameters: + * lower - The PTP clock lower half (unused) + * ppb - The offset from the nominal frequency, in parts per billion. + * Positive makes the system time run faster. + * + * Returned Value: + * OK on success, a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32_ptp_adjfine(struct ptp_lowerhalf_s *lower, long ppb) +{ + irqstate_t flags; + int64_t addend; + int ret; + + addend = STM32_PTP_ADDEND; + addend += addend * ppb / NSEC_PER_SEC; + + if (addend <= 0 || addend > UINT32_MAX) + { + nerr("ERROR: PTP adjustment out of range: %ld ppb\n", ppb); + return -EINVAL; + } + + flags = enter_critical_section(); + ret = stm32_eth_ptp_setaddend((uint32_t)addend); + leave_critical_section(flags); + + return ret; +} + +/**************************************************************************** + * Name: stm32_ptp_adjtime + * + * Description: + * Step the system time by a signed amount, without changing its rate. + * + * Input Parameters: + * lower - The PTP clock lower half (unused) + * delta - The amount to add, in nanoseconds + * + * Returned Value: + * OK on success, a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32_ptp_adjtime(struct ptp_lowerhalf_s *lower, int64_t delta) +{ + irqstate_t flags; + uint64_t abs_ns; + uint64_t sec; + uint32_t nsec; + uint32_t nsreg; + int ret; + + abs_ns = delta < 0 ? (uint64_t)0 - (uint64_t)delta : (uint64_t)delta; + sec = abs_ns / NSEC_PER_SEC; + nsec = abs_ns % NSEC_PER_SEC; + + if (sec > UINT32_MAX) + { + return -EINVAL; + } + + if (delta < 0) + { + /* To subtract, the seconds register holds the negated seconds and + * the nanoseconds register 10^9 minus the nanoseconds. + */ + + sec = (uint32_t)(0 - (uint32_t)sec); + nsreg = ETH_MACSTNUR_ADDSUB | (NSEC_PER_SEC - nsec); + } + else + { + nsreg = nsec; + } + + flags = enter_critical_section(); + stm32_putreg((uint32_t)sec, STM32_ETH_MACSTSUR); + stm32_putreg(nsreg, STM32_ETH_MACSTNUR); + stm32_putreg(stm32_getreg(STM32_ETH_MACTSCR) | ETH_MACTSCR_TSUPDT, + STM32_ETH_MACTSCR); + ret = stm32_eth_ptp_wait(STM32_ETH_MACTSCR, ETH_MACTSCR_TSUPDT); + if (ret < 0) + { + nerr("ERROR: Timed out stepping the PTP system time\n"); + } +#ifdef CONFIG_STM32_ETH_PTP_GPIO + else + { + ret = stm32_eth_ptp_pps_restart(); + } +#endif + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: stm32_ptp_adjphase + * + * Description: + * Step the system time by a signed amount, in nanoseconds. + * + ****************************************************************************/ + +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 system time. The system clock timestamps are not supported. + * + ****************************************************************************/ + +static int stm32_ptp_gettime(struct ptp_lowerhalf_s *lower, + struct timespec *ts, + struct ptp_system_timestamp *sts) +{ + uint32_t sec; + uint32_t nsec; + + stm32_eth_ptp_read(&sec, &nsec); + + ts->tv_sec = sec; + ts->tv_nsec = nsec; + return OK; +} + +/**************************************************************************** + * Name: stm32_ptp_settime + * + * Description: + * Set the system time. + * + * Input Parameters: + * lower - The PTP clock lower half (unused) + * ts - The time to set. The seconds must fit in 32 bits. + * + * Returned Value: + * OK on success, a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32_ptp_settime(struct ptp_lowerhalf_s *lower, + const struct timespec *ts) +{ + irqstate_t flags; + int ret; + + if (ts->tv_sec < 0 || ts->tv_sec > UINT32_MAX || + ts->tv_nsec < 0 || ts->tv_nsec >= NSEC_PER_SEC) + { + return -EINVAL; + } + + flags = enter_critical_section(); + stm32_putreg((uint32_t)ts->tv_sec, STM32_ETH_MACSTSUR); + stm32_putreg((uint32_t)ts->tv_nsec, STM32_ETH_MACSTNUR); + stm32_putreg(stm32_getreg(STM32_ETH_MACTSCR) | ETH_MACTSCR_TSINIT, + STM32_ETH_MACTSCR); + ret = stm32_eth_ptp_wait(STM32_ETH_MACTSCR, ETH_MACTSCR_TSINIT); + if (ret < 0) + { + nerr("ERROR: Timed out setting the PTP system time\n"); + } +#ifdef CONFIG_STM32_ETH_PTP_GPIO + else + { + ret = stm32_eth_ptp_pps_restart(); + } +#endif + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: stm32_ptp_getres + * + * Description: + * Get the resolution of the system time, that is its increment. + * + ****************************************************************************/ + +static int stm32_ptp_getres(struct ptp_lowerhalf_s *lower, + struct timespec *res) +{ + res->tv_sec = 0; + res->tv_nsec = STM32_PTP_SSINC; + 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 /* CONFIG_PTP_CLOCK */ +#endif /* CONFIG_STM32_ETH_PTP */ + /**************************************************************************** * Function: stm32_ethreset * @@ -4087,9 +4589,13 @@ static int stm32_macenable(struct stm32_ethmac_s *priv) regval |= ETH_DMACRXCR_SR; stm32_putreg(regval, STM32_ETH_DMACRXCR); - /* Enable Ethernet DMA interrupts */ + /* Enable Ethernet MAC interrupts, except the one of the timestamp unit. + * It is set each time the target time of the PPS output is reached and + * is cleared by reading MACTSSR, which the interrupt handler does not, + * so it would stay pending and keep the handler running. + */ - stm32_putreg(ETH_MACIER_ALLINTS, STM32_ETH_MACIER); + stm32_putreg(ETH_MACIER_ALLINTS & ~ETH_MACIER_TSIE, STM32_ETH_MACIER); /* Ethernet DMA supports two classes of interrupts: Normal interrupt * summary (NIS) and Abnormal interrupt summary (AIS) with a variety @@ -4154,6 +4660,19 @@ static int stm32_ethconfig(struct stm32_ethmac_s *priv) ninfo("Reset the Ethernet block\n"); stm32_ethreset(priv); +#ifdef CONFIG_STM32_ETH_PTP + /* Start the system time of the timestamp unit. It does not depend on the + * link, so it is started before the PHY, which fails to initialize when + * there is no link. + */ + + ret = stm32_eth_ptp_init(); + if (ret < 0) + { + return ret; + } +#endif + /* Initialize TX Descriptors list */ stm32_txdescinit(priv, @@ -4315,6 +4834,17 @@ static inline 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 hardware clock, /dev/ptp0 */ + + priv->ptp_lower.ops = &g_stm32_ptp_ops; + if (ptp_clock_register(&priv->ptp_lower, STM32_PTP_MAX_ADJ, intf) < 0) + { + nerr("ERROR: Failed to register the PTP clock\n"); + } +#endif + return ret; }