Skip to content

Commit a6da2bb

Browse files
h-assmanndavem330
authored andcommitted
net: stmmac: retain PTP clock time during SIOCSHWTSTAMP ioctls
Currently, when user space emits SIOCSHWTSTAMP ioctl calls such as enabling/disabling timestamping or changing filter settings, the driver reads the current CLOCK_REALTIME value and programming this into the NIC's hardware clock. This might be necessary during system initialization, but at runtime, when the PTP clock has already been synchronized to a grandmaster, a reset of the timestamp settings might result in a clock jump. Furthermore, if the clock is also controlled by phc2sys in automatic mode (where the UTC offset is queried from ptp4l), that UTC-to-TAI offset (currently 37 seconds in 2021) would be temporarily reset to 0, and it would take a long time for phc2sys to readjust so that CLOCK_REALTIME and the PHC are apart by 37 seconds again. To address the issue, we introduce a new function called stmmac_init_tstamp_counter(), which gets called during ndo_open(). It contains the code snippet moved from stmmac_hwtstamp_set() that manages the time synchronization. Besides, the sub second increment configuration is also moved here since the related values are hardware dependent and runtime invariant. Furthermore, the hardware clock must be kept running even when no time stamping mode is selected in order to retain the synchronized time base. That way, timestamping can be enabled again at any time only with the need to compensate the clock's natural drifting. As a side effect, this patch fixes the issue that ptp_clock_info::enable can be called before SIOCSHWTSTAMP and the driver (which looks at priv->systime_flags) was not prepared to handle that ordering. Fixes: 92ba688 ("stmmac: add the support for PTP hw clock driver") Reported-by: Michael Olbrich <[email protected]> Signed-off-by: Ahmad Fatoum <[email protected]> Signed-off-by: Holger Assmann <[email protected]> Signed-off-by: Vladimir Oltean <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 3bd6b2a commit a6da2bb

File tree

3 files changed

+81
-47
lines changed

3 files changed

+81
-47
lines changed

drivers/net/ethernet/stmicro/stmmac/stmmac.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ int stmmac_mdio_reset(struct mii_bus *mii);
314314
int stmmac_xpcs_setup(struct mii_bus *mii);
315315
void stmmac_set_ethtool_ops(struct net_device *netdev);
316316

317+
int stmmac_init_tstamp_counter(struct stmmac_priv *priv, u32 systime_flags);
317318
void stmmac_ptp_register(struct stmmac_priv *priv);
318319
void stmmac_ptp_unregister(struct stmmac_priv *priv);
319320
int stmmac_open(struct net_device *dev);

drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

Lines changed: 79 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@
5050
#include "dwxgmac2.h"
5151
#include "hwif.h"
5252

53+
/* As long as the interface is active, we keep the timestamping counter enabled
54+
* with fine resolution and binary rollover. This avoid non-monotonic behavior
55+
* (clock jumps) when changing timestamping settings at runtime.
56+
*/
57+
#define STMMAC_HWTS_ACTIVE (PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | \
58+
PTP_TCR_TSCTRLSSR)
59+
5360
#define STMMAC_ALIGN(x) ALIGN(ALIGN(x, SMP_CACHE_BYTES), 16)
5461
#define TSO_MAX_BUFF_SIZE (SZ_16K - 1)
5562

@@ -613,8 +620,6 @@ static int stmmac_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
613620
{
614621
struct stmmac_priv *priv = netdev_priv(dev);
615622
struct hwtstamp_config config;
616-
struct timespec64 now;
617-
u64 temp = 0;
618623
u32 ptp_v2 = 0;
619624
u32 tstamp_all = 0;
620625
u32 ptp_over_ipv4_udp = 0;
@@ -623,11 +628,6 @@ static int stmmac_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
623628
u32 snap_type_sel = 0;
624629
u32 ts_master_en = 0;
625630
u32 ts_event_en = 0;
626-
u32 sec_inc = 0;
627-
u32 value = 0;
628-
bool xmac;
629-
630-
xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;
631631

632632
if (!(priv->dma_cap.time_stamp || priv->adv_ts)) {
633633
netdev_alert(priv->dev, "No support for HW time stamping\n");
@@ -789,42 +789,17 @@ static int stmmac_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
789789
priv->hwts_rx_en = ((config.rx_filter == HWTSTAMP_FILTER_NONE) ? 0 : 1);
790790
priv->hwts_tx_en = config.tx_type == HWTSTAMP_TX_ON;
791791

792-
if (!priv->hwts_tx_en && !priv->hwts_rx_en)
793-
stmmac_config_hw_tstamping(priv, priv->ptpaddr, 0);
794-
else {
795-
value = (PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | PTP_TCR_TSCTRLSSR |
796-
tstamp_all | ptp_v2 | ptp_over_ethernet |
797-
ptp_over_ipv6_udp | ptp_over_ipv4_udp | ts_event_en |
798-
ts_master_en | snap_type_sel);
799-
stmmac_config_hw_tstamping(priv, priv->ptpaddr, value);
800-
801-
/* program Sub Second Increment reg */
802-
stmmac_config_sub_second_increment(priv,
803-
priv->ptpaddr, priv->plat->clk_ptp_rate,
804-
xmac, &sec_inc);
805-
temp = div_u64(1000000000ULL, sec_inc);
806-
807-
/* Store sub second increment and flags for later use */
808-
priv->sub_second_inc = sec_inc;
809-
priv->systime_flags = value;
810-
811-
/* calculate default added value:
812-
* formula is :
813-
* addend = (2^32)/freq_div_ratio;
814-
* where, freq_div_ratio = 1e9ns/sec_inc
815-
*/
816-
temp = (u64)(temp << 32);
817-
priv->default_addend = div_u64(temp, priv->plat->clk_ptp_rate);
818-
stmmac_config_addend(priv, priv->ptpaddr, priv->default_addend);
819-
820-
/* initialize system time */
821-
ktime_get_real_ts64(&now);
792+
priv->systime_flags = STMMAC_HWTS_ACTIVE;
822793

823-
/* lower 32 bits of tv_sec are safe until y2106 */
824-
stmmac_init_systime(priv, priv->ptpaddr,
825-
(u32)now.tv_sec, now.tv_nsec);
794+
if (priv->hwts_tx_en || priv->hwts_rx_en) {
795+
priv->systime_flags |= tstamp_all | ptp_v2 |
796+
ptp_over_ethernet | ptp_over_ipv6_udp |
797+
ptp_over_ipv4_udp | ts_event_en |
798+
ts_master_en | snap_type_sel;
826799
}
827800

801+
stmmac_config_hw_tstamping(priv, priv->ptpaddr, priv->systime_flags);
802+
828803
memcpy(&priv->tstamp_config, &config, sizeof(config));
829804

830805
return copy_to_user(ifr->ifr_data, &config,
@@ -852,6 +827,66 @@ static int stmmac_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
852827
sizeof(*config)) ? -EFAULT : 0;
853828
}
854829

830+
/**
831+
* stmmac_init_tstamp_counter - init hardware timestamping counter
832+
* @priv: driver private structure
833+
* @systime_flags: timestamping flags
834+
* Description:
835+
* Initialize hardware counter for packet timestamping.
836+
* This is valid as long as the interface is open and not suspended.
837+
* Will be rerun after resuming from suspend, case in which the timestamping
838+
* flags updated by stmmac_hwtstamp_set() also need to be restored.
839+
*/
840+
int stmmac_init_tstamp_counter(struct stmmac_priv *priv, u32 systime_flags)
841+
{
842+
bool xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;
843+
struct timespec64 now;
844+
u32 sec_inc = 0;
845+
u64 temp = 0;
846+
int ret;
847+
848+
if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp))
849+
return -EOPNOTSUPP;
850+
851+
ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
852+
if (ret < 0) {
853+
netdev_warn(priv->dev,
854+
"failed to enable PTP reference clock: %pe\n",
855+
ERR_PTR(ret));
856+
return ret;
857+
}
858+
859+
stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags);
860+
priv->systime_flags = systime_flags;
861+
862+
/* program Sub Second Increment reg */
863+
stmmac_config_sub_second_increment(priv, priv->ptpaddr,
864+
priv->plat->clk_ptp_rate,
865+
xmac, &sec_inc);
866+
temp = div_u64(1000000000ULL, sec_inc);
867+
868+
/* Store sub second increment for later use */
869+
priv->sub_second_inc = sec_inc;
870+
871+
/* calculate default added value:
872+
* formula is :
873+
* addend = (2^32)/freq_div_ratio;
874+
* where, freq_div_ratio = 1e9ns/sec_inc
875+
*/
876+
temp = (u64)(temp << 32);
877+
priv->default_addend = div_u64(temp, priv->plat->clk_ptp_rate);
878+
stmmac_config_addend(priv, priv->ptpaddr, priv->default_addend);
879+
880+
/* initialize system time */
881+
ktime_get_real_ts64(&now);
882+
883+
/* lower 32 bits of tv_sec are safe until y2106 */
884+
stmmac_init_systime(priv, priv->ptpaddr, (u32)now.tv_sec, now.tv_nsec);
885+
886+
return 0;
887+
}
888+
EXPORT_SYMBOL_GPL(stmmac_init_tstamp_counter);
889+
855890
/**
856891
* stmmac_init_ptp - init PTP
857892
* @priv: driver private structure
@@ -862,9 +897,11 @@ static int stmmac_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
862897
static int stmmac_init_ptp(struct stmmac_priv *priv)
863898
{
864899
bool xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;
900+
int ret;
865901

866-
if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp))
867-
return -EOPNOTSUPP;
902+
ret = stmmac_init_tstamp_counter(priv, STMMAC_HWTS_ACTIVE);
903+
if (ret)
904+
return ret;
868905

869906
priv->adv_ts = 0;
870907
/* Check if adv_ts can be enabled for dwmac 4.x / xgmac core */
@@ -3272,10 +3309,6 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
32723309
stmmac_mmc_setup(priv);
32733310

32743311
if (init_ptp) {
3275-
ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
3276-
if (ret < 0)
3277-
netdev_warn(priv->dev, "failed to enable PTP reference clock: %d\n", ret);
3278-
32793312
ret = stmmac_init_ptp(priv);
32803313
if (ret == -EOPNOTSUPP)
32813314
netdev_warn(priv->dev, "PTP not supported by HW\n");

drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ static int __maybe_unused stmmac_pltfr_noirq_resume(struct device *dev)
816816
if (ret)
817817
return ret;
818818

819-
clk_prepare_enable(priv->plat->clk_ptp_ref);
819+
stmmac_init_tstamp_counter(priv, priv->systime_flags);
820820
}
821821

822822
return 0;

0 commit comments

Comments
 (0)