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. 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 helper to the hwtstamp get path and the ethtool ts_info path, and keep the timestamping gate on the set path (now also requiring a valid PTP reference clock rate), so all of them consistently report -EOPNOTSUPP when timestamping is not usable. Gate the PTP reference clock enable/disable in the platform noirq suspend/resume callbacks on priv->ptp_enabled, 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: 276aae377206 ("net: stmmac: fix system hang caused by eee_ctrl_timer during suspend/resume") Reviewed-by: Maxime Chevallier Signed-off-by: Lorenzo Bianconi --- Changes in v4: - Drop stmmac_restore_subsecond_increment() and stmmac_update_subsecond_increment() changes for the moment. - Add ptp_enabled check in stmmac_pltfr_noirq_suspend() and stmmac_pltfr_noirq_resume(). - Link to v3: https://lore.kernel.org/r/20260910-stmmac-ptp-error-propagate-v3-1-4f386e8256b6@oss.qualcomm.com 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 | 13 ++++ .../net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 3 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 72 +++++++++++++++------- .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 6 +- 4 files changed, 70 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 7582fca63741..c781cf3441fc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -329,6 +329,8 @@ struct stmmac_priv { struct kernel_hwtstamp_config tstamp_config; struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_clock_ops; + bool ptp_enabled; + unsigned int default_addend; u32 sub_second_inc; u32 systime_flags; @@ -419,6 +421,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_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 154cc0c7623d..7758b854700a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -1007,8 +1007,7 @@ static int stmmac_get_ts_info(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)) { info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE | diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 62c3441911e7..9d2c6bf68575 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -653,7 +653,8 @@ 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 (!priv->plat->clk_ptp_rate || + !(priv->dma_cap.time_stamp || priv->adv_ts)) { NL_SET_ERR_MSG_MOD(extack, "No support for HW time stamping"); priv->hwts_tx_en = 0; priv->hwts_rx_en = 0; @@ -843,7 +844,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; @@ -900,11 +901,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,24 +933,43 @@ 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); + priv->ptp_enabled = true; + + return 0; } static void stmmac_release_ptp(struct stmmac_priv *priv) { + if (!priv->ptp_enabled) + return; + stmmac_ptp_unregister(priv); clk_disable_unprepare(priv->plat->clk_ptp_ref); + priv->ptp_enabled = false; } static void stmmac_legacy_serdes_power_down(struct stmmac_priv *priv) @@ -4161,10 +4176,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 +4206,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; } @@ -7740,8 +7760,7 @@ static int stmmac_register_devlink(struct stmmac_priv *priv) /* For now, what is exposed over devlink is only relevant when * timestamping is available and we have a valid ptp clock rate */ - if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp) || - !priv->plat->clk_ptp_rate) + if (!stmmac_check_timestamp_cap(priv)) return 0; priv->devlink = devlink_alloc(&stmmac_devlink_ops, sizeof(*dl_priv), @@ -8346,13 +8365,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 (priv->ptp_enabled) { + ret = stmmac_init_timestamping(priv); + if (ret) + goto error_stop_dma; + } stmmac_init_coalesce(priv); phylink_rx_clk_stop_block(priv->phylink); @@ -8376,6 +8396,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..19d481554499 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 (priv->ptp_enabled) + 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 (!priv->ptp_enabled) + return 0; + ret = clk_prepare_enable(priv->plat->clk_ptp_ref); if (ret < 0) { netdev_warn(priv->dev, --- base-commit: 6500e89132d5d510c75e1be443455af87267b40e change-id: 20260904-stmmac-ptp-error-propagate-6c0147b4ce29 Best regards, -- Lorenzo Bianconi