Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: stmmac: correct MAC propagation delay

The IEEE1588 Standard specifies that the timestamps of Packets must be
captured when the PTP message timestamp point (leading edge of first
octet after the start of frame delimiter) crosses the boundary between
the node and the network. As the MAC latches the timestamp at an
internal point, the captured timestamp must be corrected for the
additional path latency, as described in the publicly available
datasheet [1].

This patch only corrects for the MAC-Internal delay, which can be read
out from the MAC_Ingress_Timestamp_Latency register, since the Phy
framework currently does not support querying the Phy ingress and egress
latency. The Closs Domain Crossing Circuits errors as indicated in [1]
are already being accounted in the stmmac_get_tx_hwtstamp() function and
are not corrected here.

As the Latency varies for different link speeds and MII
modes of operation, the correction value needs to be updated on each
link state change.

As the delay also causes a phase shift in the timestamp counter compared
to the rest of the network, this correction will also reduce phase error
when generating PPS outputs from the timestamp counter.

[1] i.MX8MP Reference Manual, rev.1 Section 11.7.2.5.3 "Timestamp
correction"

Signed-off-by: Johannes Zink <j.zink@pengutronix.de>
Link: https://lore.kernel.org/r/20230719-stmmac_correct_mac_delay-v2-1-3366f38ee9a6@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Johannes Zink and committed by
Jakub Kicinski
20bf98c9 09bd2d7d

+56
+3
drivers/net/ethernet/stmicro/stmmac/hwif.h
··· 532 532 void (*get_systime) (void __iomem *ioaddr, u64 *systime); 533 533 void (*get_ptptime)(void __iomem *ioaddr, u64 *ptp_time); 534 534 void (*timestamp_interrupt)(struct stmmac_priv *priv); 535 + void (*correct_latency)(struct stmmac_priv *priv); 535 536 }; 536 537 537 538 #define stmmac_config_hw_tstamping(__priv, __args...) \ ··· 551 550 stmmac_do_void_callback(__priv, ptp, get_ptptime, __args) 552 551 #define stmmac_timestamp_interrupt(__priv, __args...) \ 553 552 stmmac_do_void_callback(__priv, ptp, timestamp_interrupt, __args) 553 + #define stmmac_correct_latency(__priv, __args...) \ 554 + stmmac_do_void_callback(__priv, ptp, correct_latency, __args) 554 555 555 556 struct stmmac_tx_queue; 556 557 struct stmmac_rx_queue;
+43
drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
··· 60 60 *ssinc = data; 61 61 } 62 62 63 + static void correct_latency(struct stmmac_priv *priv) 64 + { 65 + void __iomem *ioaddr = priv->ptpaddr; 66 + u32 reg_tsic, reg_tsicsns; 67 + u32 reg_tsec, reg_tsecsns; 68 + u64 scaled_ns; 69 + u32 val; 70 + 71 + /* MAC-internal ingress latency */ 72 + scaled_ns = readl(ioaddr + PTP_TS_INGR_LAT); 73 + 74 + /* See section 11.7.2.5.3.1 "Ingress Correction" on page 4001 of 75 + * i.MX8MP Applications Processor Reference Manual Rev. 1, 06/2021 76 + */ 77 + val = readl(ioaddr + PTP_TCR); 78 + if (val & PTP_TCR_TSCTRLSSR) 79 + /* nanoseconds field is in decimal format with granularity of 1ns/bit */ 80 + scaled_ns = ((u64)NSEC_PER_SEC << 16) - scaled_ns; 81 + else 82 + /* nanoseconds field is in binary format with granularity of ~0.466ns/bit */ 83 + scaled_ns = ((1ULL << 31) << 16) - 84 + DIV_U64_ROUND_CLOSEST(scaled_ns * PSEC_PER_NSEC, 466U); 85 + 86 + reg_tsic = scaled_ns >> 16; 87 + reg_tsicsns = scaled_ns & 0xff00; 88 + 89 + /* set bit 31 for 2's compliment */ 90 + reg_tsic |= BIT(31); 91 + 92 + writel(reg_tsic, ioaddr + PTP_TS_INGR_CORR_NS); 93 + writel(reg_tsicsns, ioaddr + PTP_TS_INGR_CORR_SNS); 94 + 95 + /* MAC-internal egress latency */ 96 + scaled_ns = readl(ioaddr + PTP_TS_EGR_LAT); 97 + 98 + reg_tsec = scaled_ns >> 16; 99 + reg_tsecsns = scaled_ns & 0xff00; 100 + 101 + writel(reg_tsec, ioaddr + PTP_TS_EGR_CORR_NS); 102 + writel(reg_tsecsns, ioaddr + PTP_TS_EGR_CORR_SNS); 103 + } 104 + 63 105 static int init_systime(void __iomem *ioaddr, u32 sec, u32 nsec) 64 106 { 65 107 u32 value; ··· 263 221 .get_systime = get_systime, 264 222 .get_ptptime = get_ptptime, 265 223 .timestamp_interrupt = timestamp_interrupt, 224 + .correct_latency = correct_latency, 266 225 };
+4
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
··· 909 909 priv->hwts_tx_en = 0; 910 910 priv->hwts_rx_en = 0; 911 911 912 + stmmac_correct_latency(priv, priv); 913 + 912 914 return 0; 913 915 } 914 916 ··· 1096 1094 1097 1095 if (priv->dma_cap.fpesel) 1098 1096 stmmac_fpe_link_state_handle(priv, true); 1097 + 1098 + stmmac_correct_latency(priv, priv); 1099 1099 } 1100 1100 1101 1101 static const struct phylink_mac_ops stmmac_phylink_mac_ops = {
+6
drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.h
··· 26 26 #define PTP_ACR 0x40 /* Auxiliary Control Reg */ 27 27 #define PTP_ATNR 0x48 /* Auxiliary Timestamp - Nanoseconds Reg */ 28 28 #define PTP_ATSR 0x4c /* Auxiliary Timestamp - Seconds Reg */ 29 + #define PTP_TS_INGR_CORR_NS 0x58 /* Ingress timestamp correction nanoseconds */ 30 + #define PTP_TS_EGR_CORR_NS 0x5C /* Egress timestamp correction nanoseconds*/ 31 + #define PTP_TS_INGR_CORR_SNS 0x60 /* Ingress timestamp correction subnanoseconds */ 32 + #define PTP_TS_EGR_CORR_SNS 0x64 /* Egress timestamp correction subnanoseconds */ 33 + #define PTP_TS_INGR_LAT 0x68 /* MAC internal Ingress Latency */ 34 + #define PTP_TS_EGR_LAT 0x6c /* MAC internal Egress Latency */ 29 35 30 36 #define PTP_STNSUR_ADDSUB_SHIFT 31 31 37 #define PTP_DIGITAL_ROLLOVER_MODE 0x3B9ACA00 /* 10e9-1 ns */