stmmac_setup_ptp() returns void and swallows both PTP setup errors: the PTP reference clock enable and stmmac_init_timestamping() failures are logged but never propagated. When they fail, the MAC system time counter is left in its post-reset, non-running state, while the driver keeps operating as if timestamping were up. This matters for TAPRIO/EST qdisc offloading, which derives the EST base time from the hardware timestamp counter: arming the gate list against a non-advancing time base would leave the schedule permanently stuck. Make stmmac_setup_ptp() return an error code and propagate the failure in __stmmac_open() and stmmac_resume(), stopping the DMA engines when PTP setup fails. Extend the same error propagation to the timestamping counter initialisation: stmmac_update_subsecond_increment() and stmmac_init_tstamp_counter() now return the addend and system time programming errors instead of discarding them, so a counter that cannot be configured is reported as a failure rather than silently left non-running. While at it, factor the timestamping availability check into a stmmac_check_timestamp_cap() helper that requires both the hardware timestamping capability and a valid PTP reference clock rate. This keeps the interface operational on platforms with PTP-capable silicon but an unconfigured PTP clock, where timestamping cannot be enabled: those are treated as PTP-less rather than failing to open or resume. Apply the same helper to the hwtstamp get/set paths so they consistently report -EOPNOTSUPP when timestamping is not usable. Gate the PTP reference clock enable/disable in the platform noirq suspend/resume callbacks with the same helper, keeping the clock reference balanced against the new early return in stmmac_release_ptp(). Fixes: 92ba6888510c ("stmmac: add the support for PTP hw clock driver") Fixes: 0ad2be79f254 ("net: stmmac: Balance PTP reference clock enable/disable") Reviewed-by: Maxime Chevallier Signed-off-by: Lorenzo Bianconi --- Changes in v3: - Rebase on top of net main branch. - Link to v2: https://lore.kernel.org/r/20260907-stmmac-ptp-error-propagate-v2-1-4a2e8e41e860@oss.qualcomm.com Changes in v2: - Check clk_ptp_rate value in stmmac_check_timestamp_cap(). - Return error code in stmmac_update_subsecond_increment() and stmmac_init_tstamp_counter(). - Rely on stmmac_check_timestamp_cap() in stmmac_hwtstamp_set() and stmmac_hwtstamp_get(). - Link to v1: https://lore.kernel.org/r/20260904-stmmac-ptp-error-propagate-v1-1-80f01b03dafa@oss.qualcomm.com --- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 11 +++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 98 ++++++++++++++-------- .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 6 +- 3 files changed, 78 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 7582fca63741..0a00490832f3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -419,6 +419,17 @@ int stmmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i, struct plat_stmmacenet_data *stmmac_plat_dat_alloc(struct device *dev); +static inline bool stmmac_check_timestamp_cap(struct stmmac_priv *priv) +{ + if (!priv->dma_cap.time_stamp && !priv->dma_cap.atime_stamp) + return false; + + if (!priv->plat->clk_ptp_rate) + return false; + + return true; +} + static inline bool stmmac_xdp_is_enabled(struct stmmac_priv *priv) { return !!priv->xdp_prog; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 5fe7e95fdd34..7f5ada1132c4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -601,7 +601,7 @@ static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p, } } -static void stmmac_update_subsecond_increment(struct stmmac_priv *priv) +static int stmmac_update_subsecond_increment(struct stmmac_priv *priv) { bool xmac = dwmac_is_xmac(priv->plat->core_type); u32 sec_inc = 0; @@ -625,7 +625,7 @@ static void stmmac_update_subsecond_increment(struct stmmac_priv *priv) */ temp = (u64)(temp << 32); priv->default_addend = div_u64(temp, priv->plat->clk_ptp_rate); - stmmac_config_addend(priv, priv->ptpaddr, priv->default_addend); + return stmmac_config_addend(priv, priv->ptpaddr, priv->default_addend); } /** @@ -653,7 +653,7 @@ static int stmmac_hwtstamp_set(struct net_device *dev, u32 ts_master_en = 0; u32 ts_event_en = 0; - if (!(priv->dma_cap.time_stamp || priv->adv_ts)) { + if (!stmmac_check_timestamp_cap(priv)) { NL_SET_ERR_MSG_MOD(extack, "No support for HW time stamping"); priv->hwts_tx_en = 0; priv->hwts_rx_en = 0; @@ -843,7 +843,7 @@ static int stmmac_hwtstamp_get(struct net_device *dev, { struct stmmac_priv *priv = netdev_priv(dev); - if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp)) + if (!stmmac_check_timestamp_cap(priv)) return -EOPNOTSUPP; *config = priv->tstamp_config; @@ -865,32 +865,32 @@ static int stmmac_init_tstamp_counter(struct stmmac_priv *priv, u32 systime_flags) { struct timespec64 now; - - if (!priv->plat->clk_ptp_rate) { - netdev_err(priv->dev, "Invalid PTP clock rate"); - return -EINVAL; - } + int ret; stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags); priv->systime_flags = systime_flags; - stmmac_update_subsecond_increment(priv); + ret = stmmac_update_subsecond_increment(priv); + if (ret) + return ret; /* initialize system time */ ktime_get_real_ts64(&now); /* lower 32 bits of tv_sec are safe until y2106 */ - stmmac_init_systime(priv, priv->ptpaddr, (u32)now.tv_sec, now.tv_nsec); - - return 0; + return stmmac_init_systime(priv, priv->ptpaddr, (u32)now.tv_sec, + now.tv_nsec); } /** * stmmac_init_timestamping - initialise timestamping * @priv: driver private structure - * Description: this is to verify if the HW supports the PTPv1 or PTPv2. - * This is done by looking at the HW cap. register. - * This function also registers the ptp driver. + * Description: configure the PTP reference clock and initialise the hardware + * timestamp counter, then detect which timestamping modes the HW supports. + * The caller is responsible for enabling the PTP reference clock and for + * registering the PTP clock driver. + * Return: 0 on success, or a negative error code if the timestamp counter + * cannot be initialised. */ static int stmmac_init_timestamping(struct stmmac_priv *priv) { @@ -900,11 +900,6 @@ static int stmmac_init_timestamping(struct stmmac_priv *priv) if (priv->plat->ptp_clk_freq_config) priv->plat->ptp_clk_freq_config(priv); - if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp)) { - netdev_info(priv->dev, "PTP not supported by HW\n"); - return -EOPNOTSUPP; - } - ret = stmmac_init_tstamp_counter(priv, STMMAC_HWTS_ACTIVE | PTP_TCR_TSCFUPDT); if (ret) { @@ -937,22 +932,39 @@ static int stmmac_init_timestamping(struct stmmac_priv *priv) return 0; } -static void stmmac_setup_ptp(struct stmmac_priv *priv) +static int stmmac_setup_ptp(struct stmmac_priv *priv) { int ret; + if (!stmmac_check_timestamp_cap(priv)) { + netdev_info(priv->dev, "PTP not supported\n"); + return 0; + } + ret = clk_prepare_enable(priv->plat->clk_ptp_ref); - if (ret < 0) + if (ret < 0) { netdev_warn(priv->dev, "failed to enable PTP reference clock: %pe\n", ERR_PTR(ret)); + return ret; + } - if (stmmac_init_timestamping(priv) == 0) - stmmac_ptp_register(priv); + ret = stmmac_init_timestamping(priv); + if (ret) { + clk_disable_unprepare(priv->plat->clk_ptp_ref); + return ret; + } + + stmmac_ptp_register(priv); + + return 0; } static void stmmac_release_ptp(struct stmmac_priv *priv) { + if (!stmmac_check_timestamp_cap(priv)) + return; + stmmac_ptp_unregister(priv); clk_disable_unprepare(priv->plat->clk_ptp_ref); } @@ -4161,10 +4173,12 @@ static int __stmmac_open(struct net_device *dev, ret = stmmac_hw_setup(dev); if (ret < 0) { netdev_err(priv->dev, "%s: Hw setup failed\n", __func__); - goto init_error; + return ret; } - stmmac_setup_ptp(priv); + ret = stmmac_setup_ptp(priv); + if (ret) + goto ptp_error; stmmac_init_coalesce(priv); @@ -4189,7 +4203,10 @@ static int __stmmac_open(struct net_device *dev, hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); stmmac_release_ptp(priv); -init_error: +ptp_error: + stmmac_stop_all_dma(priv); + stmmac_mac_set(priv, priv->ioaddr, false); + return ret; } @@ -7685,9 +7702,7 @@ static int stmmac_dl_ts_coarse_set(struct devlink *dl, u32 id, /* In Coarse mode, we can use a smaller subsecond increment, let's * reconfigure the systime, subsecond increment and addend. */ - stmmac_update_subsecond_increment(priv); - - return 0; + return stmmac_update_subsecond_increment(priv); } static int stmmac_dl_ts_coarse_get(struct devlink *dl, u32 id, @@ -8327,13 +8342,14 @@ int stmmac_resume(struct device *dev) ret = stmmac_hw_setup(ndev); if (ret < 0) { netdev_err(priv->dev, "%s: Hw setup failed\n", __func__); - stmmac_legacy_serdes_power_down(priv); - mutex_unlock(&priv->lock); - rtnl_unlock(); - return ret; + goto error_unlock; } - stmmac_init_timestamping(priv); + if (stmmac_check_timestamp_cap(priv)) { + ret = stmmac_init_timestamping(priv); + if (ret) + goto error_stop_dma; + } stmmac_init_coalesce(priv); phylink_rx_clk_stop_block(priv->phylink); @@ -8357,6 +8373,16 @@ int stmmac_resume(struct device *dev) netif_device_attach(ndev); return 0; + +error_stop_dma: + stmmac_stop_all_dma(priv); + stmmac_mac_set(priv, priv->ioaddr, false); +error_unlock: + stmmac_legacy_serdes_power_down(priv); + mutex_unlock(&priv->lock); + rtnl_unlock(); + + return ret; } EXPORT_SYMBOL_GPL(stmmac_resume); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 6128ed1bd521..dcfce51b2886 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -959,7 +959,8 @@ static int __maybe_unused stmmac_pltfr_noirq_suspend(struct device *dev) if (!priv->wolopts) { /* Disable clock in case of PWM is off */ - clk_disable_unprepare(priv->plat->clk_ptp_ref); + if (stmmac_check_timestamp_cap(priv)) + clk_disable_unprepare(priv->plat->clk_ptp_ref); ret = pm_runtime_force_suspend(dev); if (ret) @@ -984,6 +985,9 @@ static int __maybe_unused stmmac_pltfr_noirq_resume(struct device *dev) if (ret) return ret; + if (!stmmac_check_timestamp_cap(priv)) + return 0; + ret = clk_prepare_enable(priv->plat->clk_ptp_ref); if (ret < 0) { netdev_warn(priv->dev, --- base-commit: 2ac09b5353fe6858411fdc8c6efa60d832e20f13 change-id: 20260904-stmmac-ptp-error-propagate-6c0147b4ce29 Best regards, -- Lorenzo Bianconi