net: stmmac: propagate PTP init failures in __stmmac_open() and stmmac_resume()

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().

Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
Link: https://patch.msgid.link/20260914-stmmac-ptp-error-propagate-v5-1-81149897e65d@oss.qualcomm.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Lorenzo Bianconi 2026-09-14 17:26:44 +02:00 committed by Jakub Kicinski
parent a0afc297a0
commit 8181678a92
4 changed files with 95 additions and 35 deletions

View File

@ -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;

View File

@ -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 |

View File

@ -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);
@ -4191,7 +4224,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;
}
@ -7764,8 +7800,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),
@ -8370,14 +8405,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);
@ -8400,6 +8440,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);

View File

@ -970,7 +970,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)
@ -995,6 +996,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,