Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: stmmac: Add a devlink attribute to control timestamping mode

The DWMAC1000 supports 2 timestamping configurations to configure how
frequency adjustments are made to the ptp_clock, as well as the reported
timestamp values.

There was a previous attempt at upstreaming support for configuring this
mode by Olivier Dautricourt and Julien Beraud a few years back [1]

In a nutshell, the timestamping can be either set in fine mode or in
coarse mode.

In fine mode, which is the default, we use the overflow of an accumulator to
trigger frequency adjustments, but by doing so we lose precision on the
timetamps that are produced by the timestamping unit. The main drawback
is that the sub-second increment value, used to generate timestamps, can't be
set to lower than (2 / ptp_clock_freq).

The "fine" qualification comes from the frequent frequency adjustments we are
able to do, which is perfect for a PTP follower usecase.

In Coarse mode, we don't do frequency adjustments based on an
accumulator overflow. We can therefore have very fine subsecond
increment values, allowing for better timestamping precision. However
this mode works best when the ptp clock frequency is adjusted based on
an external signal, such as a PPS input produced by a GPS clock. This
mode is therefore perfect for a Grand-master usecase.

Introduce a driver-specific devlink parameter "ts_coarse" to enable or
disable coarse mode, keeping the "fine" mode as a default.

This can then be changed with:

devlink dev param set <dev> name ts_coarse value true cmode runtime

The associated documentation is also added.

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

Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Link: https://patch.msgid.link/20251024070720.71174-3-maxime.chevallier@bootlin.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Maxime Chevallier and committed by
Paolo Abeni
6920fa0c 792000fb

+148 -3
+1
Documentation/networking/devlink/index.rst
··· 99 99 prestera 100 100 qed 101 101 sfc 102 + stmmac 102 103 ti-cpsw-switch 103 104 zl3073x
+31
Documentation/networking/devlink/stmmac.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + ======================================= 4 + stmmac (synopsys dwmac) devlink support 5 + ======================================= 6 + 7 + This document describes the devlink features implemented by the ``stmmac`` 8 + device driver. 9 + 10 + Parameters 11 + ========== 12 + 13 + The ``stmmac`` driver implements the following driver-specific parameters. 14 + 15 + .. list-table:: Driver-specific parameters implemented 16 + :widths: 5 5 5 85 17 + 18 + * - Name 19 + - Type 20 + - Mode 21 + - Description 22 + * - ``ts_coarse`` 23 + - Boolean 24 + - runtime 25 + - Enable the Coarse timestamping mode. In Coarse mode, the ptp clock is 26 + expected to be updated through an external PPS input, but the subsecond 27 + increment used for timestamping is set to 1/ptp_clock_rate. In Fine mode 28 + (i.e. Coarse mode == false), the ptp clock frequency is adjusted more 29 + frequently, but the subsecond increment is set to 2/ptp_clock_rate. 30 + Coarse mode is suitable for PTP Grand Master operation. If unsure, leave 31 + the parameter to False.
+1
drivers/net/ethernet/stmicro/stmmac/Kconfig
··· 10 10 select PHYLINK 11 11 select CRC32 12 12 select RESET_CONTROLLER 13 + select NET_DEVLINK 13 14 help 14 15 This is the driver for the Ethernet IPs built around a 15 16 Synopsys IP Core.
+3
drivers/net/ethernet/stmicro/stmmac/stmmac.h
··· 259 259 u32 sarc_type; 260 260 u32 rx_riwt[MTL_MAX_RX_QUEUES]; 261 261 int hwts_rx_en; 262 + bool tsfupdt_coarse; 262 263 263 264 void __iomem *ioaddr; 264 265 struct net_device *dev; ··· 370 369 /* XDP BPF Program */ 371 370 unsigned long *af_xdp_zc_qps; 372 371 struct bpf_prog *xdp_prog; 372 + 373 + struct devlink *devlink; 373 374 }; 374 375 375 376 enum stmmac_state {
+112 -3
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
··· 40 40 #include <linux/phylink.h> 41 41 #include <linux/udp.h> 42 42 #include <linux/bpf_trace.h> 43 + #include <net/devlink.h> 43 44 #include <net/page_pool/helpers.h> 44 45 #include <net/pkt_cls.h> 45 46 #include <net/xdp_sock_drv.h> ··· 59 58 * with fine resolution and binary rollover. This avoid non-monotonic behavior 60 59 * (clock jumps) when changing timestamping settings at runtime. 61 60 */ 62 - #define STMMAC_HWTS_ACTIVE (PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | \ 63 - PTP_TCR_TSCTRLSSR) 61 + #define STMMAC_HWTS_ACTIVE (PTP_TCR_TSENA | PTP_TCR_TSCTRLSSR) 64 62 65 63 #define STMMAC_ALIGN(x) ALIGN(ALIGN(x, SMP_CACHE_BYTES), 16) 66 64 #define TSO_MAX_BUFF_SIZE (SZ_16K - 1) ··· 147 147 #endif 148 148 149 149 #define STMMAC_COAL_TIMER(x) (ns_to_ktime((x) * NSEC_PER_USEC)) 150 + 151 + struct stmmac_devlink_priv { 152 + struct stmmac_priv *stmmac_priv; 153 + }; 154 + 155 + enum stmmac_dl_param_id { 156 + STMMAC_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, 157 + STMMAC_DEVLINK_PARAM_ID_TS_COARSE, 158 + }; 150 159 151 160 /** 152 161 * stmmac_set_clk_tx_rate() - set the clock rate for the MAC transmit clock ··· 684 675 priv->hwts_tx_en = config->tx_type == HWTSTAMP_TX_ON; 685 676 686 677 priv->systime_flags = STMMAC_HWTS_ACTIVE; 678 + if (!priv->tsfupdt_coarse) 679 + priv->systime_flags |= PTP_TCR_TSCFUPDT; 687 680 688 681 if (priv->hwts_tx_en || priv->hwts_rx_en) { 689 682 priv->systime_flags |= tstamp_all | ptp_v2 | ··· 776 765 return -EOPNOTSUPP; 777 766 } 778 767 779 - ret = stmmac_init_tstamp_counter(priv, STMMAC_HWTS_ACTIVE); 768 + ret = stmmac_init_tstamp_counter(priv, STMMAC_HWTS_ACTIVE | 769 + PTP_TCR_TSCFUPDT); 780 770 if (ret) { 781 771 netdev_warn(priv->dev, "PTP init failed\n"); 782 772 return ret; ··· 7412 7400 .xmo_rx_timestamp = stmmac_xdp_rx_timestamp, 7413 7401 }; 7414 7402 7403 + static int stmmac_dl_ts_coarse_set(struct devlink *dl, u32 id, 7404 + struct devlink_param_gset_ctx *ctx, 7405 + struct netlink_ext_ack *extack) 7406 + { 7407 + struct stmmac_devlink_priv *dl_priv = devlink_priv(dl); 7408 + struct stmmac_priv *priv = dl_priv->stmmac_priv; 7409 + 7410 + priv->tsfupdt_coarse = ctx->val.vbool; 7411 + 7412 + if (priv->tsfupdt_coarse) 7413 + priv->systime_flags &= ~PTP_TCR_TSCFUPDT; 7414 + else 7415 + priv->systime_flags |= PTP_TCR_TSCFUPDT; 7416 + 7417 + /* In Coarse mode, we can use a smaller subsecond increment, let's 7418 + * reconfigure the systime, subsecond increment and addend. 7419 + */ 7420 + stmmac_update_subsecond_increment(priv); 7421 + 7422 + return 0; 7423 + } 7424 + 7425 + static int stmmac_dl_ts_coarse_get(struct devlink *dl, u32 id, 7426 + struct devlink_param_gset_ctx *ctx) 7427 + { 7428 + struct stmmac_devlink_priv *dl_priv = devlink_priv(dl); 7429 + struct stmmac_priv *priv = dl_priv->stmmac_priv; 7430 + 7431 + ctx->val.vbool = priv->tsfupdt_coarse; 7432 + 7433 + return 0; 7434 + } 7435 + 7436 + static const struct devlink_param stmmac_devlink_params[] = { 7437 + DEVLINK_PARAM_DRIVER(STMMAC_DEVLINK_PARAM_ID_TS_COARSE, "ts_coarse", 7438 + DEVLINK_PARAM_TYPE_BOOL, 7439 + BIT(DEVLINK_PARAM_CMODE_RUNTIME), 7440 + stmmac_dl_ts_coarse_get, 7441 + stmmac_dl_ts_coarse_set, NULL), 7442 + }; 7443 + 7444 + /* None of the generic devlink parameters are implemented */ 7445 + static const struct devlink_ops stmmac_devlink_ops = {}; 7446 + 7447 + static int stmmac_register_devlink(struct stmmac_priv *priv) 7448 + { 7449 + struct stmmac_devlink_priv *dl_priv; 7450 + int ret; 7451 + 7452 + /* For now, what is exposed over devlink is only relevant when 7453 + * timestamping is available and we have a valid ptp clock rate 7454 + */ 7455 + if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp) || 7456 + !priv->plat->clk_ptp_rate) 7457 + return 0; 7458 + 7459 + priv->devlink = devlink_alloc(&stmmac_devlink_ops, sizeof(*dl_priv), 7460 + priv->device); 7461 + if (!priv->devlink) 7462 + return -ENOMEM; 7463 + 7464 + dl_priv = devlink_priv(priv->devlink); 7465 + dl_priv->stmmac_priv = priv; 7466 + 7467 + ret = devlink_params_register(priv->devlink, stmmac_devlink_params, 7468 + ARRAY_SIZE(stmmac_devlink_params)); 7469 + if (ret) 7470 + goto dl_free; 7471 + 7472 + devlink_register(priv->devlink); 7473 + return 0; 7474 + 7475 + dl_free: 7476 + devlink_free(priv->devlink); 7477 + 7478 + return ret; 7479 + } 7480 + 7481 + static void stmmac_unregister_devlink(struct stmmac_priv *priv) 7482 + { 7483 + if (!priv->devlink) 7484 + return; 7485 + 7486 + devlink_unregister(priv->devlink); 7487 + devlink_params_unregister(priv->devlink, stmmac_devlink_params, 7488 + ARRAY_SIZE(stmmac_devlink_params)); 7489 + devlink_free(priv->devlink); 7490 + } 7491 + 7415 7492 /** 7416 7493 * stmmac_dvr_probe 7417 7494 * @device: device pointer ··· 7774 7673 goto error_phy_setup; 7775 7674 } 7776 7675 7676 + ret = stmmac_register_devlink(priv); 7677 + if (ret) 7678 + goto error_devlink_setup; 7679 + 7777 7680 ret = register_netdev(ndev); 7778 7681 if (ret) { 7779 7682 dev_err(priv->device, "%s: ERROR %i registering the device\n", ··· 7800 7695 return ret; 7801 7696 7802 7697 error_netdev_register: 7698 + stmmac_unregister_devlink(priv); 7699 + error_devlink_setup: 7803 7700 phylink_destroy(priv->phylink); 7804 7701 error_phy_setup: 7805 7702 stmmac_pcs_clean(ndev); ··· 7838 7731 #ifdef CONFIG_DEBUG_FS 7839 7732 stmmac_exit_fs(ndev); 7840 7733 #endif 7734 + stmmac_unregister_devlink(priv); 7735 + 7841 7736 phylink_destroy(priv->phylink); 7842 7737 if (priv->plat->stmmac_rst) 7843 7738 reset_control_assert(priv->plat->stmmac_rst);