* [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