Merge branch 'net-stmmac-add-support-for-coarse-timestamping'

Maxime Chevallier says:

====================
net: stmmac: Add support for coarse timestamping

This is V2 for coarse timetamping support in stmmac. This version uses a
dedicated devlink param "ts_coarse" to control this mode.

This doesn't conflict with Russell's cleanup of hwif.

Maxime

[1] : https://lore.kernel.org/netdev/20200514102808.31163-1-olivier.dautricourt@orolia.com/

V1: https://lore.kernel.org/netdev/20251015102725.1297985-1-maxime.chevallier@bootlin.com/
====================

Link: https://patch.msgid.link/20251024070720.71174-1-maxime.chevallier@bootlin.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2025-10-28 15:34:36 +01:00
commit cebba694d2
5 changed files with 176 additions and 23 deletions

View File

@ -99,5 +99,6 @@ parameters, info versions, and other features it supports.
prestera
qed
sfc
stmmac
ti-cpsw-switch
zl3073x

View File

@ -0,0 +1,31 @@
.. SPDX-License-Identifier: GPL-2.0
=======================================
stmmac (synopsys dwmac) devlink support
=======================================
This document describes the devlink features implemented by the ``stmmac``
device driver.
Parameters
==========
The ``stmmac`` driver implements the following driver-specific parameters.
.. list-table:: Driver-specific parameters implemented
:widths: 5 5 5 85
* - Name
- Type
- Mode
- Description
* - ``ts_coarse``
- Boolean
- runtime
- Enable the Coarse timestamping mode. In Coarse mode, the ptp clock is
expected to be updated through an external PPS input, but the subsecond
increment used for timestamping is set to 1/ptp_clock_rate. In Fine mode
(i.e. Coarse mode == false), the ptp clock frequency is adjusted more
frequently, but the subsecond increment is set to 2/ptp_clock_rate.
Coarse mode is suitable for PTP Grand Master operation. If unsure, leave
the parameter to False.

View File

@ -10,6 +10,7 @@ config STMMAC_ETH
select PHYLINK
select CRC32
select RESET_CONTROLLER
select NET_DEVLINK
help
This is the driver for the Ethernet IPs built around a
Synopsys IP Core.

View File

@ -259,6 +259,7 @@ struct stmmac_priv {
u32 sarc_type;
u32 rx_riwt[MTL_MAX_RX_QUEUES];
int hwts_rx_en;
bool tsfupdt_coarse;
void __iomem *ioaddr;
struct net_device *dev;
@ -369,6 +370,8 @@ struct stmmac_priv {
/* XDP BPF Program */
unsigned long *af_xdp_zc_qps;
struct bpf_prog *xdp_prog;
struct devlink *devlink;
};
enum stmmac_state {

View File

@ -40,6 +40,7 @@
#include <linux/phylink.h>
#include <linux/udp.h>
#include <linux/bpf_trace.h>
#include <net/devlink.h>
#include <net/page_pool/helpers.h>
#include <net/pkt_cls.h>
#include <net/xdp_sock_drv.h>
@ -58,8 +59,7 @@
* with fine resolution and binary rollover. This avoid non-monotonic behavior
* (clock jumps) when changing timestamping settings at runtime.
*/
#define STMMAC_HWTS_ACTIVE (PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | \
PTP_TCR_TSCTRLSSR)
#define STMMAC_HWTS_ACTIVE (PTP_TCR_TSENA | PTP_TCR_TSCTRLSSR)
#define STMMAC_ALIGN(x) ALIGN(ALIGN(x, SMP_CACHE_BYTES), 16)
#define TSO_MAX_BUFF_SIZE (SZ_16K - 1)
@ -148,6 +148,15 @@ static void stmmac_exit_fs(struct net_device *dev);
#define STMMAC_COAL_TIMER(x) (ns_to_ktime((x) * NSEC_PER_USEC))
struct stmmac_devlink_priv {
struct stmmac_priv *stmmac_priv;
};
enum stmmac_dl_param_id {
STMMAC_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
STMMAC_DEVLINK_PARAM_ID_TS_COARSE,
};
/**
* stmmac_set_clk_tx_rate() - set the clock rate for the MAC transmit clock
* @bsp_priv: BSP private data structure (unused)
@ -464,6 +473,33 @@ static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p,
}
}
static void stmmac_update_subsecond_increment(struct stmmac_priv *priv)
{
bool xmac = dwmac_is_xmac(priv->plat->core_type);
u32 sec_inc = 0;
u64 temp = 0;
stmmac_config_hw_tstamping(priv, priv->ptpaddr, priv->systime_flags);
/* program Sub Second Increment reg */
stmmac_config_sub_second_increment(priv, priv->ptpaddr,
priv->plat->clk_ptp_rate,
xmac, &sec_inc);
temp = div_u64(1000000000ULL, sec_inc);
/* Store sub second increment for later use */
priv->sub_second_inc = sec_inc;
/* calculate default added value:
* formula is :
* addend = (2^32)/freq_div_ratio;
* where, freq_div_ratio = 1e9ns/sec_inc
*/
temp = (u64)(temp << 32);
priv->default_addend = div_u64(temp, priv->plat->clk_ptp_rate);
stmmac_config_addend(priv, priv->ptpaddr, priv->default_addend);
}
/**
* stmmac_hwtstamp_set - control hardware timestamping.
* @dev: device pointer.
@ -648,6 +684,8 @@ static int stmmac_hwtstamp_set(struct net_device *dev,
priv->hwts_tx_en = config->tx_type == HWTSTAMP_TX_ON;
priv->systime_flags = STMMAC_HWTS_ACTIVE;
if (!priv->tsfupdt_coarse)
priv->systime_flags |= PTP_TCR_TSCFUPDT;
if (priv->hwts_tx_en || priv->hwts_rx_en) {
priv->systime_flags |= tstamp_all | ptp_v2 |
@ -697,10 +735,7 @@ static int stmmac_hwtstamp_get(struct net_device *dev,
static int stmmac_init_tstamp_counter(struct stmmac_priv *priv,
u32 systime_flags)
{
bool xmac = dwmac_is_xmac(priv->plat->core_type);
struct timespec64 now;
u32 sec_inc = 0;
u64 temp = 0;
if (!priv->plat->clk_ptp_rate) {
netdev_err(priv->dev, "Invalid PTP clock rate");
@ -710,23 +745,7 @@ static int stmmac_init_tstamp_counter(struct stmmac_priv *priv,
stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags);
priv->systime_flags = systime_flags;
/* program Sub Second Increment reg */
stmmac_config_sub_second_increment(priv, priv->ptpaddr,
priv->plat->clk_ptp_rate,
xmac, &sec_inc);
temp = div_u64(1000000000ULL, sec_inc);
/* Store sub second increment for later use */
priv->sub_second_inc = sec_inc;
/* calculate default added value:
* formula is :
* addend = (2^32)/freq_div_ratio;
* where, freq_div_ratio = 1e9ns/sec_inc
*/
temp = (u64)(temp << 32);
priv->default_addend = div_u64(temp, priv->plat->clk_ptp_rate);
stmmac_config_addend(priv, priv->ptpaddr, priv->default_addend);
stmmac_update_subsecond_increment(priv);
/* initialize system time */
ktime_get_real_ts64(&now);
@ -757,7 +776,8 @@ static int stmmac_init_timestamping(struct stmmac_priv *priv)
return -EOPNOTSUPP;
}
ret = stmmac_init_tstamp_counter(priv, STMMAC_HWTS_ACTIVE);
ret = stmmac_init_tstamp_counter(priv, STMMAC_HWTS_ACTIVE |
PTP_TCR_TSCFUPDT);
if (ret) {
netdev_warn(priv->dev, "PTP init failed\n");
return ret;
@ -7392,6 +7412,95 @@ static const struct xdp_metadata_ops stmmac_xdp_metadata_ops = {
.xmo_rx_timestamp = stmmac_xdp_rx_timestamp,
};
static int stmmac_dl_ts_coarse_set(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct stmmac_devlink_priv *dl_priv = devlink_priv(dl);
struct stmmac_priv *priv = dl_priv->stmmac_priv;
priv->tsfupdt_coarse = ctx->val.vbool;
if (priv->tsfupdt_coarse)
priv->systime_flags &= ~PTP_TCR_TSCFUPDT;
else
priv->systime_flags |= PTP_TCR_TSCFUPDT;
/* In Coarse mode, we can use a smaller subsecond increment, let's
* reconfigure the systime, subsecond increment and addend.
*/
stmmac_update_subsecond_increment(priv);
return 0;
}
static int stmmac_dl_ts_coarse_get(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx)
{
struct stmmac_devlink_priv *dl_priv = devlink_priv(dl);
struct stmmac_priv *priv = dl_priv->stmmac_priv;
ctx->val.vbool = priv->tsfupdt_coarse;
return 0;
}
static const struct devlink_param stmmac_devlink_params[] = {
DEVLINK_PARAM_DRIVER(STMMAC_DEVLINK_PARAM_ID_TS_COARSE, "ts_coarse",
DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
stmmac_dl_ts_coarse_get,
stmmac_dl_ts_coarse_set, NULL),
};
/* None of the generic devlink parameters are implemented */
static const struct devlink_ops stmmac_devlink_ops = {};
static int stmmac_register_devlink(struct stmmac_priv *priv)
{
struct stmmac_devlink_priv *dl_priv;
int ret;
/* 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)
return 0;
priv->devlink = devlink_alloc(&stmmac_devlink_ops, sizeof(*dl_priv),
priv->device);
if (!priv->devlink)
return -ENOMEM;
dl_priv = devlink_priv(priv->devlink);
dl_priv->stmmac_priv = priv;
ret = devlink_params_register(priv->devlink, stmmac_devlink_params,
ARRAY_SIZE(stmmac_devlink_params));
if (ret)
goto dl_free;
devlink_register(priv->devlink);
return 0;
dl_free:
devlink_free(priv->devlink);
return ret;
}
static void stmmac_unregister_devlink(struct stmmac_priv *priv)
{
if (!priv->devlink)
return;
devlink_unregister(priv->devlink);
devlink_params_unregister(priv->devlink, stmmac_devlink_params,
ARRAY_SIZE(stmmac_devlink_params));
devlink_free(priv->devlink);
}
/**
* stmmac_dvr_probe
* @device: device pointer
@ -7665,6 +7774,10 @@ int stmmac_dvr_probe(struct device *device,
goto error_phy_setup;
}
ret = stmmac_register_devlink(priv);
if (ret)
goto error_devlink_setup;
ret = register_netdev(ndev);
if (ret) {
dev_err(priv->device, "%s: ERROR %i registering the device\n",
@ -7687,6 +7800,8 @@ int stmmac_dvr_probe(struct device *device,
return ret;
error_netdev_register:
stmmac_unregister_devlink(priv);
error_devlink_setup:
phylink_destroy(priv->phylink);
error_phy_setup:
stmmac_pcs_clean(ndev);
@ -7723,6 +7838,8 @@ void stmmac_dvr_remove(struct device *dev)
#ifdef CONFIG_DEBUG_FS
stmmac_exit_fs(ndev);
#endif
stmmac_unregister_devlink(priv);
phylink_destroy(priv->phylink);
if (priv->plat->stmmac_rst)
reset_control_assert(priv->plat->stmmac_rst);