* [PATCH net v5] net: stmmac: propagate PTP init failures in __stmmac_open() and stmmac_resume()
@ 2026-09-14 15:26 Lorenzo Bianconi
2026-09-16 15:28 ` netdev-bot+sashiko
2026-09-18 1:20 ` patchwork-bot+netdevbpf
0 siblings, 2 replies; 4+ messages in thread
From: Lorenzo Bianconi @ 2026-09-14 15:26 UTC (permalink / raw)
To: Maxime Chevallier, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
Richard Cochran, Rayagond Kokatanur, Thierry Reding, Russell King,
Joakim Zhang
Cc: netdev, linux-stm32, linux-arm-kernel, Lorenzo Bianconi
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(), stopping the DMA engines when PTP setup
fails. In stmmac_resume(), re-initialise timestamping only when the
PTP clock was registered before suspend, re-apply the platform clock
rate configuration, and stop the DMA engines if re-initialisation
fails.
While at it, factor the hardware timestamping capability check into a
stmmac_check_timestamp_cap() helper and apply it to the hwtstamp get
path and the ethtool ts_info path. stmmac_setup_ptp() now enables the
PTP reference clock and runs the platform ptp_clk_freq_config()
callback before validating the resulting clock rate; if no valid rate
is available, it disables the clock and returns success without
registering the PTP clock. This keeps the interface operational on
platforms with PTP-capable silicon but an unconfigured PTP clock,
where timestamping cannot be enabled: instead of failing, the driver
proceeds without setting up timestamping. Extend the timestamping gate
on the set path (which keeps testing the core's basic/advanced
timestamping capability) and the devlink registration with the PTP
reference clock rate requirement.
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 <maxime.chevallier@bootlin.com>
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
---
Changes in v5:
- Introduce stmmac_init_ptp_clk_freq() routine to configure ptp clock
frequency.
- Update commit message.
- Update stmmac_init_timestamping kernel-doc.
- Link to v4: https://lore.kernel.org/r/20260913-stmmac-ptp-error-propagate-v4-1-a947aceac928@oss.qualcomm.com
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 | 7 ++
.../net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 3 +-
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 114 +++++++++++++++------
.../net/ethernet/stmicro/stmmac/stmmac_platform.c | 6 +-
4 files changed, 95 insertions(+), 35 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 7582fca63741..4fc96b317d79 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,11 @@ 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)
+{
+ return priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp;
+}
+
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..0cc6eafa19a3 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;
@@ -866,11 +867,6 @@ static int stmmac_init_tstamp_counter(struct stmmac_priv *priv,
{
struct timespec64 now;
- if (!priv->plat->clk_ptp_rate) {
- netdev_err(priv->dev, "Invalid PTP clock rate");
- return -EINVAL;
- }
-
stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags);
priv->systime_flags = systime_flags;
@@ -885,26 +881,37 @@ static int stmmac_init_tstamp_counter(struct stmmac_priv *priv,
return 0;
}
+static int stmmac_init_ptp_clk_freq(struct stmmac_priv *priv)
+{
+ if (priv->plat->ptp_clk_freq_config)
+ priv->plat->ptp_clk_freq_config(priv);
+
+ if (!priv->plat->clk_ptp_rate) {
+ netdev_info(priv->dev, "PTP clock rate not configured\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/**
* 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: initialise the hardware timestamping counter, reset the
+ * timestamping configuration and derive the advanced timestamping flags from
+ * the HW capabilities. The caller must have ensured a valid PTP reference
+ * clock rate (see stmmac_init_ptp_clk_freq()); the configured state is valid
+ * as long as the interface is open and not suspended, and this function is
+ * re-run on resume.
+ *
+ * Return: 0 on success, a negative errno otherwise.
*/
static int stmmac_init_timestamping(struct stmmac_priv *priv)
{
bool xmac = dwmac_is_xmac(priv->plat->core_type);
int ret;
- 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 +944,48 @@ 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);
+ if (stmmac_init_ptp_clk_freq(priv)) {
+ clk_disable_unprepare(priv->plat->clk_ptp_ref);
+ return 0;
+ }
+
+ 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 +4192,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 +4222,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 +7776,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) || !priv->plat->clk_ptp_rate)
return 0;
priv->devlink = devlink_alloc(&stmmac_devlink_ops, sizeof(*dl_priv),
@@ -8346,14 +8381,19 @@ 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) {
+ if (stmmac_init_ptp_clk_freq(priv))
+ goto init_coalesce;
+ ret = stmmac_init_timestamping(priv);
+ if (ret)
+ goto error_stop_dma;
+ }
+
+init_coalesce:
stmmac_init_coalesce(priv);
phylink_rx_clk_stop_block(priv->phylink);
stmmac_set_rx_mode(ndev);
@@ -8376,6 +8416,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: e6b6078ea1731b05b3b552497b3bce4bf8b014ae
change-id: 20260904-stmmac-ptp-error-propagate-6c0147b4ce29
Best regards,
--
Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH net v5] net: stmmac: propagate PTP init failures in __stmmac_open() and stmmac_resume() 2026-09-14 15:26 [PATCH net v5] net: stmmac: propagate PTP init failures in __stmmac_open() and stmmac_resume() Lorenzo Bianconi @ 2026-09-16 15:28 ` netdev-bot+sashiko 2026-09-16 17:59 ` Lorenzo Bianconi 2026-09-18 1:20 ` patchwork-bot+netdevbpf 1 sibling, 1 reply; 4+ messages in thread From: netdev-bot+sashiko @ 2026-09-16 15:28 UTC (permalink / raw) To: lorenzo.bianconi Cc: maxime.chevallier, andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32, alexandre.torgue, richardcochran, rayagond, treding, linux, qiangqing.zhang, netdev, linux-stm32, linux-arm-kernel Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: Critical: 0 · High: 1 (1) · Medium: 3 (3) · Low: 0 - [High] The claimed propagation of stmmac_init_timestamping() failures is vacuous: after this patch stmmac_init_tstamp_counter()… - [Medium] The patch splits the hardware-timestamping gate across peer sites of the same uAPI. - [Medium] In stmmac_resume(), when priv->ptp_enabled is true but stmmac_init_ptp_clk_freq() fails (clk_ptp_rate came back as 0), the… - [Medium] The patch makes 'interface operational with an unconfigured PTP reference clock' an explicitly supported state (stmmac_setup_ptp()… Pre-existing issues: - [High] The new ptp_error unwind adds another way for __stmmac_open() to fail *after* `memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf))`… - [Medium] stmmac_init_timestamping() hardcodes `STMMAC_HWTS_ACTIVE | PTP_TCR_TSCFUPDT` (fine update), overwrites priv->systime_flags and… - [Medium] stmmac_get_ts_info() reports `info->phc_index = 0` when priv->ptp_clock is NULL, whereas the ethtool core uses -1 to express… - [Medium] If clk_prepare_enable(priv->plat->clk_ptp_ref) fails in stmmac_pltfr_noirq_resume() (stmmac_platform.c:990), the function… -- net: stmmac: propagate PTP init failures in __stmmac_open() and stmmac_resume() This makes stmmac_setup_ptp() return an error code so that PTP setup failures are propagated by __stmmac_open() and stmmac_resume(), factors the timestamping capability test into stmmac_check_timestamp_cap(), moves the platform PTP clock rate configuration into stmmac_init_ptp_clk_freq(), and gates the noirq suspend/resume PTP reference clock handling on a new priv->ptp_enabled flag. A few questions below. > diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h > index 7582fca637419..4fc96b317d79c 100644 > --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h > +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h [ ... ] > @@ -419,6 +421,11 @@ 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) > +{ > + return priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp; > +} > + > 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 154cc0c7623da..7758b854700a3 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 | [Severity: Medium] This is a pre-existing issue and not something this patch introduces, but this branch of stmmac_get_ts_info() reports phc_index = 0 when priv->ptp_clock is NULL: if (priv->ptp_clock) info->phc_index = ptp_clock_index(priv->ptp_clock); else info->phc_index = 0; The ethtool core expresses "no PHC" with -1 (ethtool_init_tsinfo() and ethtool_op_get_ts_info() both set info->phc_index = -1), so index 0 is reported to userspace as a real PHC that usually belongs to another device. The patch makes the unregistered-PHC case a normal outcome, since stmmac_setup_ptp() now returns success without registering the clock when no rate is available. Should the else branch leave the core's -1 in place? > diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c > index 62c3441911e71..0cc6eafa19a3d 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; [Severity: Medium] Should the clk_ptp_rate requirement be applied to the peer sites of the same uAPI too? The set path now returns -EOPNOTSUPP when clk_ptp_rate is zero, while stmmac_hwtstamp_get() and stmmac_get_ts_info() were converted to stmmac_check_timestamp_cap(), which only tests dma_cap.time_stamp / dma_cap.atime_stamp. On the configuration this patch newly declares supported (PTP-capable silicon, no PTP clock rate, so stmmac_setup_ptp() returns 0 without registering the PHC) ethtool -T still advertises SOF_TIMESTAMPING_TX_HARDWARE / SOF_TIMESTAMPING_RX_HARDWARE plus the full rx_filters list and SIOCGHWTSTAMP still succeeds, but every SIOCSHWTSTAMP is rejected. That state looks reachable at probe time, since stmmac_probe_config_dt() does: plat->clk_ptp_rate = clk_get_rate(plat->stmmac_clk); which yields 0 when the CSR clock lookup only warned and stmmac_clk is NULL and no ptp_ref clock is described. > @@ -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; > @@ -866,11 +867,6 @@ static int stmmac_init_tstamp_counter(struct stmmac_priv *priv, > { > struct timespec64 now; > > - if (!priv->plat->clk_ptp_rate) { > - netdev_err(priv->dev, "Invalid PTP clock rate"); > - return -EINVAL; > - } > - > stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags); > priv->systime_flags = systime_flags; [Severity: High] With this check moved out, does stmmac_init_tstamp_counter() still have any failure path left? The remainder is: stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags); priv->systime_flags = systime_flags; stmmac_update_subsecond_increment(priv); ktime_get_real_ts64(&now); stmmac_init_systime(priv, priv->ptpaddr, (u32)now.tv_sec, now.tv_nsec); return 0; stmmac_update_subsecond_increment() is void and discards the status of config_addend(), and the int returned by the stmmac_init_systime() callback is dropped, so the function looks like it can only return 0. If that is the case, stmmac_init_timestamping() can only return 0 as well, and then the netdev_warn("PTP init failed") branch, the ret = stmmac_init_timestamping(priv); if (ret) { clk_disable_unprepare(priv->plat->clk_ptp_ref); return ret; } block in stmmac_setup_ptp(), the goto error_stop_dma in stmmac_resume() and the whole new error_stop_dma label are unreachable. The failure named in the commit message ("the MAC system time counter is left in its post-reset, non-running state") is exactly what init_systime() reports: drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c:init_systime() { ... return readl_poll_timeout_atomic(ioaddr + PTP_TCR, value, !(value & PTP_TCR_TSINIT), 10, 100000); } When that times out with -ETIMEDOUT, execution still reaches stmmac_ptp_register() and priv->ptp_enabled = true, so the interface comes up with a stopped time base that TAPRIO/EST offload will arm against. Should the hwif return values be checked so the only genuinely propagated error is not just the clk_prepare_enable() one? > @@ -885,26 +881,37 @@ static int stmmac_init_tstamp_counter(struct stmmac_priv *priv, > return 0; > } > > +static int stmmac_init_ptp_clk_freq(struct stmmac_priv *priv) > +{ > + if (priv->plat->ptp_clk_freq_config) > + priv->plat->ptp_clk_freq_config(priv); > + > + if (!priv->plat->clk_ptp_rate) { > + netdev_info(priv->dev, "PTP clock rate not configured\n"); > + return -EINVAL; > + } > + > + return 0; > +} > + > /** > * 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: initialise the hardware timestamping counter, reset the > + * timestamping configuration and derive the advanced timestamping flags from > + * the HW capabilities. The caller must have ensured a valid PTP reference > + * clock rate (see stmmac_init_ptp_clk_freq()); the configured state is valid > + * as long as the interface is open and not suspended, and this function is > + * re-run on resume. > + * > + * Return: 0 on success, a negative errno otherwise. > */ > static int stmmac_init_timestamping(struct stmmac_priv *priv) > { > bool xmac = dwmac_is_xmac(priv->plat->core_type); > int ret; > > - 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) { [Severity: Medium] This isn't a bug introduced by this patch, but since the resume path is being reworked to re-run stmmac_init_timestamping() under if (priv->ptp_enabled), the hardcoded PTP_TCR_TSCFUPDT here reverts a devlink-configured coarse mode after every suspend/resume, while stmmac_dl_ts_coarse_get() keeps reporting the old value: priv->tsfupdt_coarse = ctx->val.vbool; ... ctx->val.vbool = priv->tsfupdt_coarse; The same function also does: memset(&priv->tstamp_config, 0, sizeof(priv->tstamp_config)); priv->hwts_tx_en = 0; priv->hwts_rx_en = 0; so an active SIOCSHWTSTAMP configuration is dropped across suspend as well. I see from the v4/v5 notes that the subsecond increment rework was deliberately deferred, so this is noted only for completeness. > @@ -937,24 +944,48 @@ 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); > + if (stmmac_init_ptp_clk_freq(priv)) { > + clk_disable_unprepare(priv->plat->clk_ptp_ref); > + return 0; > + } [Severity: Medium] This path makes "interface up with clk_ptp_rate == 0 and clk_ptp_ref disabled" a supported state. Is the devlink runtime setter still safe in that state? stmmac_dl_ts_coarse_set() ("phc_coarse_adj", CAP_NET_ADMIN) has no clk_ptp_rate or ptp_enabled check: /* In Coarse mode, we can use a smaller subsecond increment, let's * reconfigure the systime, subsecond increment and addend. */ stmmac_update_subsecond_increment(priv); and that helper divides by the rate: priv->default_addend = div_u64(temp, priv->plat->clk_ptp_rate); as does the hwif callback it invokes: drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c:config_sub_second_increment() { if (value & PTP_TCR_TSCFUPDT) data = (2000000000ULL / ptp_clock); else data = (1000000000ULL / ptp_clock); ... } Devlink is only registered when clk_ptp_rate was nonzero at probe, but a platform ptp_clk_freq_config() callback can turn it into zero later, e.g. drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c:ethqos_ptp_clk_freq_config() { ... plat_dat->clk_ptp_rate = clk_get_rate(plat_dat->clk_ptp_ref); } after which this return 0 (and the goto init_coalesce in stmmac_resume()) keeps the interface and its registered devlink params alive. A subsequent devlink dev param set then divides by zero and programs PTP_SSIR/addend with clk_ptp_ref disabled. Should the rate requirement be extended to the devlink setter as well? > + > + 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; > } [ ... ] > @@ -4161,10 +4192,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 +4222,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; > } [Severity: High] This is a pre-existing issue that the patch does not introduce, but the new ptp_error return adds one more way to leave __stmmac_open() after memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf)); has aliased priv->dma_conf to the caller's descriptor and page pool pointers. On the reopen path the caller then frees them: stmmac_change_mtu() { ... free_dma_desc_resources(priv, dma_conf); kfree(dma_conf); ... return ret; } __free_dma_rx_desc_resources() only clears buf_alloc_num and xsk_pool, so dma_rx/dma_erx, buf_pool and page_pool stay set in priv->dma_conf. Since the netdev remains netif_running() after a failed ndo_change_mtu, a later ip link set down runs: __stmmac_release() { ... for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); ... free_dma_desc_resources(priv, &priv->dma_conf); } Can this free the same dma_free_coherent()/buf_pool/page_pool objects a second time? The ptp_error path also skips stmmac_init_coalesce(), so the txtimers in the freshly zeroed dma_conf were never passed to hrtimer_setup() before hrtimer_cancel() runs on them. [ ... ] > @@ -8346,14 +8381,19 @@ 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) { > + if (stmmac_init_ptp_clk_freq(priv)) > + goto init_coalesce; > > + ret = stmmac_init_timestamping(priv); > + if (ret) > + goto error_stop_dma; > + } > + > +init_coalesce: > stmmac_init_coalesce(priv); [Severity: Medium] When priv->ptp_enabled is true and stmmac_init_ptp_clk_freq() fails here, the interface is resumed via init_coalesce with an info-level log only. Does that leave software and hardware state inconsistent? stmmac_hw_setup() has just done the DMA/MAC software reset, so PTP_TCR (TSENA), PTP_SSIR, the addend and the system time registers are back at reset values and the counter is stopped, yet: - priv->ptp_clock stays registered and priv->ptp_enabled stays true, so a PHC whose time never advances remains exposed to userspace - stmmac_pltfr_noirq_resume() re-enables clk_ptp_ref because ptp_enabled is true - priv->hwts_tx_en/hwts_rx_en/systime_flags/sub_second_inc/default_addend still describe the pre-suspend hardware configuration - priv->plat->clk_ptp_rate stays 0 for the code that divides by it, e.g. stmmac_update_subsecond_increment() The open path handles the same failure differently: it also disables clk_ptp_ref and leaves the PHC unregistered, so software and hardware agree. Should resume unregister the PHC and clear ptp_enabled instead of continuing? > diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c > index 6128ed1bd521a..19d4815544990 100644 > --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c > +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c [ ... ] > @@ -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, [Severity: Medium] This is a pre-existing issue rather than something the patch adds, but with the new gating the two flags can now disagree. If clk_prepare_enable() here fails, the function returns the error with priv->ptp_enabled still true, so no PTP clock reference is held while the flag says one is. A later close or driver removal then passes the new gate in stmmac_release_ptp(): if (!priv->ptp_enabled) return; stmmac_ptp_unregister(priv); clk_disable_unprepare(priv->plat->clk_ptp_ref); and calls clk_disable_unprepare() without a matching enable. Should the failure path clear priv->ptp_enabled? -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260914-stmmac-ptp-error-propagate-v5-1-81149897e65d%40oss.qualcomm.com ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net v5] net: stmmac: propagate PTP init failures in __stmmac_open() and stmmac_resume() 2026-09-16 15:28 ` netdev-bot+sashiko @ 2026-09-16 17:59 ` Lorenzo Bianconi 0 siblings, 0 replies; 4+ messages in thread From: Lorenzo Bianconi @ 2026-09-16 17:59 UTC (permalink / raw) To: netdev-bot+sashiko Cc: maxime.chevallier, andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32, alexandre.torgue, richardcochran, rayagond, treding, linux, qiangqing.zhang, netdev, linux-stm32, linux-arm-kernel [-- Attachment #1: Type: text/plain, Size: 18289 bytes --] [...] > > A few questions below. > > > diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h > > index 7582fca637419..4fc96b317d79c 100644 > > --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h > > +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h > > [ ... ] > > > @@ -419,6 +421,11 @@ 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) > > +{ > > + return priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp; > > +} > > + > > 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 154cc0c7623da..7758b854700a3 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 | > > [Severity: Medium] > This is a pre-existing issue and not something this patch introduces, but > this branch of stmmac_get_ts_info() reports phc_index = 0 when > priv->ptp_clock is NULL: > > if (priv->ptp_clock) > info->phc_index = ptp_clock_index(priv->ptp_clock); > else > info->phc_index = 0; > > The ethtool core expresses "no PHC" with -1 (ethtool_init_tsinfo() and > ethtool_op_get_ts_info() both set info->phc_index = -1), so index 0 is > reported to userspace as a real PHC that usually belongs to another > device. This issue is already fixed in the following commit: https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/commit/?id=f0ef4b1eaed000a304726a43091588e8426ba08a > > The patch makes the unregistered-PHC case a normal outcome, since > stmmac_setup_ptp() now returns success without registering the clock when > no rate is available. Should the else branch leave the core's -1 in place? > > > diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c > > index 62c3441911e71..0cc6eafa19a3d 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; > > [Severity: Medium] > Should the clk_ptp_rate requirement be applied to the peer sites of the > same uAPI too? > > The set path now returns -EOPNOTSUPP when clk_ptp_rate is zero, while > stmmac_hwtstamp_get() and stmmac_get_ts_info() were converted to > stmmac_check_timestamp_cap(), which only tests dma_cap.time_stamp / > dma_cap.atime_stamp. I have not changed the logic in stmmac_get_ts_info() since I do not think it is required (priv->tstamp_config is set just in stmmac_set_ts_info()). > > On the configuration this patch newly declares supported (PTP-capable > silicon, no PTP clock rate, so stmmac_setup_ptp() returns 0 without > registering the PHC) ethtool -T still advertises > SOF_TIMESTAMPING_TX_HARDWARE / SOF_TIMESTAMPING_RX_HARDWARE plus the full > rx_filters list and SIOCGHWTSTAMP still succeeds, but every SIOCSHWTSTAMP > is rejected. > > That state looks reachable at probe time, since stmmac_probe_config_dt() > does: > > plat->clk_ptp_rate = clk_get_rate(plat->stmmac_clk); > > which yields 0 when the CSR clock lookup only warned and stmmac_clk is > NULL and no ptp_ref clock is described. > > > @@ -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; > > @@ -866,11 +867,6 @@ static int stmmac_init_tstamp_counter(struct stmmac_priv *priv, > > { > > struct timespec64 now; > > > > - if (!priv->plat->clk_ptp_rate) { > > - netdev_err(priv->dev, "Invalid PTP clock rate"); > > - return -EINVAL; > > - } > > - > > stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags); > > priv->systime_flags = systime_flags; > > [Severity: High] > With this check moved out, does stmmac_init_tstamp_counter() still have any > failure path left? The remainder is: I will improve stmmac_init_tstamp_counter() error path in a subsequent patch > > stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags); > priv->systime_flags = systime_flags; > > stmmac_update_subsecond_increment(priv); > > ktime_get_real_ts64(&now); > > stmmac_init_systime(priv, priv->ptpaddr, (u32)now.tv_sec, now.tv_nsec); > > return 0; > > stmmac_update_subsecond_increment() is void and discards the status of > config_addend(), and the int returned by the stmmac_init_systime() callback > is dropped, so the function looks like it can only return 0. > > If that is the case, stmmac_init_timestamping() can only return 0 as well, > and then the netdev_warn("PTP init failed") branch, the > > ret = stmmac_init_timestamping(priv); > if (ret) { > clk_disable_unprepare(priv->plat->clk_ptp_ref); > return ret; > } > > block in stmmac_setup_ptp(), the goto error_stop_dma in stmmac_resume() and > the whole new error_stop_dma label are unreachable. > > The failure named in the commit message ("the MAC system time counter is > left in its post-reset, non-running state") is exactly what init_systime() > reports: > > drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c:init_systime() { > ... > return readl_poll_timeout_atomic(ioaddr + PTP_TCR, value, > !(value & PTP_TCR_TSINIT), > 10, 100000); > } > > When that times out with -ETIMEDOUT, execution still reaches > stmmac_ptp_register() and priv->ptp_enabled = true, so the interface comes > up with a stopped time base that TAPRIO/EST offload will arm against. > Should the hwif return values be checked so the only genuinely propagated > error is not just the clk_prepare_enable() one? This change is part of a dedicated patch (the one containing stmmac_init_tstamp_counter() error path changes) that I will post as follow-up > > > @@ -885,26 +881,37 @@ static int stmmac_init_tstamp_counter(struct stmmac_priv *priv, > > return 0; > > } > > > > +static int stmmac_init_ptp_clk_freq(struct stmmac_priv *priv) > > +{ > > + if (priv->plat->ptp_clk_freq_config) > > + priv->plat->ptp_clk_freq_config(priv); > > + > > + if (!priv->plat->clk_ptp_rate) { > > + netdev_info(priv->dev, "PTP clock rate not configured\n"); > > + return -EINVAL; > > + } > > + > > + return 0; > > +} > > + > > /** > > * 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: initialise the hardware timestamping counter, reset the > > + * timestamping configuration and derive the advanced timestamping flags from > > + * the HW capabilities. The caller must have ensured a valid PTP reference > > + * clock rate (see stmmac_init_ptp_clk_freq()); the configured state is valid > > + * as long as the interface is open and not suspended, and this function is > > + * re-run on resume. > > + * > > + * Return: 0 on success, a negative errno otherwise. > > */ > > static int stmmac_init_timestamping(struct stmmac_priv *priv) > > { > > bool xmac = dwmac_is_xmac(priv->plat->core_type); > > int ret; > > > > - 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) { > > [Severity: Medium] > This isn't a bug introduced by this patch, but since the resume path is > being reworked to re-run stmmac_init_timestamping() under > if (priv->ptp_enabled), the hardcoded PTP_TCR_TSCFUPDT here reverts a > devlink-configured coarse mode after every suspend/resume, while > stmmac_dl_ts_coarse_get() keeps reporting the old value: As pointed out by sashiko, this issue is not introduced by this patch and it will be fixed with a dedicated patch. Regards, Lorenzo > > priv->tsfupdt_coarse = ctx->val.vbool; > ... > ctx->val.vbool = priv->tsfupdt_coarse; > > The same function also does: > > memset(&priv->tstamp_config, 0, sizeof(priv->tstamp_config)); > priv->hwts_tx_en = 0; > priv->hwts_rx_en = 0; > > so an active SIOCSHWTSTAMP configuration is dropped across suspend as > well. I see from the v4/v5 notes that the subsecond increment rework was > deliberately deferred, so this is noted only for completeness. > > > @@ -937,24 +944,48 @@ 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); > > + if (stmmac_init_ptp_clk_freq(priv)) { > > + clk_disable_unprepare(priv->plat->clk_ptp_ref); > > + return 0; > > + } > > [Severity: Medium] > This path makes "interface up with clk_ptp_rate == 0 and clk_ptp_ref > disabled" a supported state. Is the devlink runtime setter still safe in > that state? > > stmmac_dl_ts_coarse_set() ("phc_coarse_adj", CAP_NET_ADMIN) has no > clk_ptp_rate or ptp_enabled check: > > /* In Coarse mode, we can use a smaller subsecond increment, let's > * reconfigure the systime, subsecond increment and addend. > */ > stmmac_update_subsecond_increment(priv); > > and that helper divides by the rate: > > priv->default_addend = div_u64(temp, priv->plat->clk_ptp_rate); > > as does the hwif callback it invokes: > > drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c:config_sub_second_increment() { > if (value & PTP_TCR_TSCFUPDT) > data = (2000000000ULL / ptp_clock); > else > data = (1000000000ULL / ptp_clock); > ... > } > > Devlink is only registered when clk_ptp_rate was nonzero at probe, but a > platform ptp_clk_freq_config() callback can turn it into zero later, e.g. > > drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c:ethqos_ptp_clk_freq_config() { > ... > plat_dat->clk_ptp_rate = clk_get_rate(plat_dat->clk_ptp_ref); > } > > after which this return 0 (and the goto init_coalesce in stmmac_resume()) > keeps the interface and its registered devlink params alive. A subsequent > devlink dev param set then divides by zero and programs PTP_SSIR/addend > with clk_ptp_ref disabled. Should the rate requirement be extended to the > devlink setter as well? > > > + > > + 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; > > } > > [ ... ] > > > @@ -4161,10 +4192,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 +4222,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; > > } > > [Severity: High] > This is a pre-existing issue that the patch does not introduce, but the new > ptp_error return adds one more way to leave __stmmac_open() after > > memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf)); > > has aliased priv->dma_conf to the caller's descriptor and page pool > pointers. On the reopen path the caller then frees them: > > stmmac_change_mtu() { > ... > free_dma_desc_resources(priv, dma_conf); > kfree(dma_conf); > ... > return ret; > } > > __free_dma_rx_desc_resources() only clears buf_alloc_num and xsk_pool, so > dma_rx/dma_erx, buf_pool and page_pool stay set in priv->dma_conf. Since > the netdev remains netif_running() after a failed ndo_change_mtu, a later > ip link set down runs: > > __stmmac_release() { > ... > for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) > hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); > ... > free_dma_desc_resources(priv, &priv->dma_conf); > } > > Can this free the same dma_free_coherent()/buf_pool/page_pool objects a > second time? The ptp_error path also skips stmmac_init_coalesce(), so the > txtimers in the freshly zeroed dma_conf were never passed to > hrtimer_setup() before hrtimer_cancel() runs on them. > > [ ... ] > > > @@ -8346,14 +8381,19 @@ 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) { > > + if (stmmac_init_ptp_clk_freq(priv)) > > + goto init_coalesce; > > > > + ret = stmmac_init_timestamping(priv); > > + if (ret) > > + goto error_stop_dma; > > + } > > + > > +init_coalesce: > > stmmac_init_coalesce(priv); > > [Severity: Medium] > When priv->ptp_enabled is true and stmmac_init_ptp_clk_freq() fails here, > the interface is resumed via init_coalesce with an info-level log only. > Does that leave software and hardware state inconsistent? > > stmmac_hw_setup() has just done the DMA/MAC software reset, so PTP_TCR > (TSENA), PTP_SSIR, the addend and the system time registers are back at > reset values and the counter is stopped, yet: > > - priv->ptp_clock stays registered and priv->ptp_enabled stays true, so a > PHC whose time never advances remains exposed to userspace > - stmmac_pltfr_noirq_resume() re-enables clk_ptp_ref because > ptp_enabled is true > - priv->hwts_tx_en/hwts_rx_en/systime_flags/sub_second_inc/default_addend > still describe the pre-suspend hardware configuration > - priv->plat->clk_ptp_rate stays 0 for the code that divides by it, e.g. > stmmac_update_subsecond_increment() > > The open path handles the same failure differently: it also disables > clk_ptp_ref and leaves the PHC unregistered, so software and hardware > agree. Should resume unregister the PHC and clear ptp_enabled instead of > continuing? > > > diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c > > index 6128ed1bd521a..19d4815544990 100644 > > --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c > > +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c > > [ ... ] > > > @@ -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, > > [Severity: Medium] > This is a pre-existing issue rather than something the patch adds, but with > the new gating the two flags can now disagree. If clk_prepare_enable() here > fails, the function returns the error with priv->ptp_enabled still true, so > no PTP clock reference is held while the flag says one is. > > A later close or driver removal then passes the new gate in > stmmac_release_ptp(): > > if (!priv->ptp_enabled) > return; > > stmmac_ptp_unregister(priv); > clk_disable_unprepare(priv->plat->clk_ptp_ref); > > and calls clk_disable_unprepare() without a matching enable. Should the > failure path clear priv->ptp_enabled? > > -- > Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260914-stmmac-ptp-error-propagate-v5-1-81149897e65d%40oss.qualcomm.com [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net v5] net: stmmac: propagate PTP init failures in __stmmac_open() and stmmac_resume() 2026-09-14 15:26 [PATCH net v5] net: stmmac: propagate PTP init failures in __stmmac_open() and stmmac_resume() Lorenzo Bianconi 2026-09-16 15:28 ` netdev-bot+sashiko @ 2026-09-18 1:20 ` patchwork-bot+netdevbpf 1 sibling, 0 replies; 4+ messages in thread From: patchwork-bot+netdevbpf @ 2026-09-18 1:20 UTC (permalink / raw) To: Lorenzo Bianconi Cc: maxime.chevallier, andrew+netdev, davem, edumazet, kuba, pabeni, mcoquelin.stm32, alexandre.torgue, richardcochran, rayagond, treding, linux, qiangqing.zhang, netdev, linux-stm32, linux-arm-kernel Hello: This patch was applied to netdev/net-next.git (main) by Jakub Kicinski <kuba@kernel.org>: On Mon, 14 Sep 2026 17:26:44 +0200 you wrote: > 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. > > [...] Here is the summary with links: - [net,v5] net: stmmac: propagate PTP init failures in __stmmac_open() and stmmac_resume() https://git.kernel.org/netdev/net-next/c/8181678a92f0 You are awesome, thank you! -- Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-18 1:21 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-09-14 15:26 [PATCH net v5] net: stmmac: propagate PTP init failures in __stmmac_open() and stmmac_resume() Lorenzo Bianconi 2026-09-16 15:28 ` netdev-bot+sashiko 2026-09-16 17:59 ` Lorenzo Bianconi 2026-09-18 1:20 ` patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox