* [PATCH v10 iwl-next 00/11] idpf: add initial PTP support
@ 2025-04-08 10:30 Milena Olech
2025-04-08 10:30 ` [PATCH v10 iwl-next 01/11] idpf: change the method for mailbox workqueue allocation Milena Olech
` (10 more replies)
0 siblings, 11 replies; 35+ messages in thread
From: Milena Olech @ 2025-04-08 10:30 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Milena Olech
This patch series introduces support for Precision Time Protocol (PTP) to
Intel(R) Infrastructure Data Path Function (IDPF) driver. PTP feature is
supported when the PTP capability is negotiated with the Control
Plane (CP). IDPF creates a PTP clock and sets a set of supported
functions.
During the PTP initialization, IDPF requests a set of PTP capabilities
and receives a writeback from the CP with the set of supported options.
These options are:
- get time of the PTP clock
- get cross timestamp
- set the time of the PTP clock
- adjust the PTP clock
- Tx timestamping
Each feature is considered to have direct access, where the operations
on PCIe BAR registers are allowed, or the mailbox access, where the
virtchnl messages are used to perform any PTP action. Mailbox access
means that PTP requests are sent to the CP through dedicated secondary
mailbox and the CP reads/writes/modifies desired resource - PTP Clock
or Tx timestamp registers.
Tx timestamp capabilities are negotiated only for vports that have
UPLINK_VPORT flag set by the CP. Capabilities provide information about
the number of available Tx timestamp latches, their indexes and size of
the Tx timestamp value. IDPF requests Tx timestamp by setting the
TSYN bit and the requested timestamp index in the context descriptor for
the PTP packets. When the completion tag for that packet is received,
IDPF schedules a worker to read the Tx timestamp value.
v9 -> v10: create a separate patch for cross timestamping, change the
order, improve get device clock time latch mechanism
v8 -> v9: fix Rx filters upscaling, check if the link is up in
idpf_hwtstamp_get/set, fix typo
v7 -> v8: split Tx and Rx timestamping enablement, refactor
idpf_for_each_vport
v6 -> v7: remove section about Tx timestamp limitation from cover letter
since it has been fixed, change preparing flow descriptor method
v5 -> v6: change locking mechanism in get_ts_info, clean timestamp
fields when preparing flow descriptor, add Rx filter
v4 -> v5: fix spin unlock when Tx timestamp index is requested
v3 -> v4: change timestamp filters dependent on Tx timestamp cap,
rewrite function that extends Tx timestamp value, minor fixes
v2 -> v3: fix minor issues, revert idpf_for_each_vport changes,
extend idpf_ptp_set_rx_tstamp, split tstamp statistics
v1 -> v2: add stats for timestamping, use ndo_hwtamp_get/set,
fix minor spelling issues
Milena Olech (11):
idpf: change the method for mailbox workqueue allocation
idpf: add initial PTP support
virtchnl: add PTP virtchnl definitions
idpf: move virtchnl structures to the header file
idpf: negotiate PTP capabilities and get PTP clock
idpf: add mailbox access to read PTP clock time
idpf: add cross timestamping
idpf: add PTP clock configuration
idpf: add Tx timestamp capabilities negotiation
idpf: add Tx timestamp flows
idpf: add support for Rx timestamping
drivers/net/ethernet/intel/idpf/Kconfig | 1 +
drivers/net/ethernet/intel/idpf/Makefile | 3 +
drivers/net/ethernet/intel/idpf/idpf.h | 35 +
.../ethernet/intel/idpf/idpf_controlq_api.h | 3 +
drivers/net/ethernet/intel/idpf/idpf_dev.c | 14 +
.../net/ethernet/intel/idpf/idpf_ethtool.c | 75 +-
.../ethernet/intel/idpf/idpf_lan_pf_regs.h | 4 +
.../net/ethernet/intel/idpf/idpf_lan_txrx.h | 13 +-
drivers/net/ethernet/intel/idpf/idpf_lib.c | 47 +
drivers/net/ethernet/intel/idpf/idpf_main.c | 9 +-
drivers/net/ethernet/intel/idpf/idpf_ptp.c | 1005 +++++++++++++++++
drivers/net/ethernet/intel/idpf/idpf_ptp.h | 381 +++++++
drivers/net/ethernet/intel/idpf/idpf_txrx.c | 171 ++-
drivers/net/ethernet/intel/idpf/idpf_txrx.h | 18 +-
.../net/ethernet/intel/idpf/idpf_virtchnl.c | 160 ++-
.../net/ethernet/intel/idpf/idpf_virtchnl.h | 84 ++
.../ethernet/intel/idpf/idpf_virtchnl_ptp.c | 678 +++++++++++
drivers/net/ethernet/intel/idpf/virtchnl2.h | 314 ++++-
18 files changed, 2912 insertions(+), 103 deletions(-)
create mode 100644 drivers/net/ethernet/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/ethernet/intel/idpf/idpf_ptp.h
create mode 100644 drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
base-commit: edf956e8bd7d4c7ac8a7643ed74a36227db1fa27
--
2.43.5
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH v10 iwl-next 01/11] idpf: change the method for mailbox workqueue allocation
2025-04-08 10:30 [PATCH v10 iwl-next 00/11] idpf: add initial PTP support Milena Olech
@ 2025-04-08 10:30 ` Milena Olech
2025-04-08 21:01 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:30 ` [PATCH v10 iwl-next 02/11] idpf: add initial PTP support Milena Olech
` (9 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Milena Olech @ 2025-04-08 10:30 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Milena Olech,
Alexander Lobakin, Samuel Salin
Since workqueues are created per CPU, the works scheduled to this
workqueues are run on the CPU they were assigned. It may result in
overloaded CPU that is not able to handle virtchnl messages in
relatively short time. Allocating workqueue with WQ_UNBOUND and
WQ_HIGHPRI flags allows scheduler to queue virtchl messages on less loaded
CPUs, what eliminates delays.
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
---
drivers/net/ethernet/intel/idpf/idpf_main.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c
index bec4a02c5373..1284ab2adaf1 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_main.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_main.c
@@ -198,9 +198,8 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_serv_wq_alloc;
}
- adapter->mbx_wq = alloc_workqueue("%s-%s-mbx",
- WQ_UNBOUND | WQ_MEM_RECLAIM, 0,
- dev_driver_string(dev),
+ adapter->mbx_wq = alloc_workqueue("%s-%s-mbx", WQ_UNBOUND | WQ_HIGHPRI,
+ 0, dev_driver_string(dev),
dev_name(dev));
if (!adapter->mbx_wq) {
dev_err(dev, "Failed to allocate mailbox workqueue\n");
--
2.43.5
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v10 iwl-next 02/11] idpf: add initial PTP support
2025-04-08 10:30 [PATCH v10 iwl-next 00/11] idpf: add initial PTP support Milena Olech
2025-04-08 10:30 ` [PATCH v10 iwl-next 01/11] idpf: change the method for mailbox workqueue allocation Milena Olech
@ 2025-04-08 10:30 ` Milena Olech
2025-04-08 21:02 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:30 ` [PATCH v10 iwl-next 03/11] virtchnl: add PTP virtchnl definitions Milena Olech
` (8 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Milena Olech @ 2025-04-08 10:30 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Milena Olech,
Alexander Lobakin, Vadim Fedorenko, Willem de Bruijn,
Mina Almasry, Samuel Salin
PTP feature is supported if the VIRTCHNL2_CAP_PTP is negotiated during the
capabilities recognition. Initial PTP support includes PTP initialization
and registration of the clock.
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Tested-by: Mina Almasry <almasrymina@google.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
---
drivers/net/ethernet/intel/idpf/Kconfig | 1 +
drivers/net/ethernet/intel/idpf/Makefile | 1 +
drivers/net/ethernet/intel/idpf/idpf.h | 3 +
drivers/net/ethernet/intel/idpf/idpf_main.c | 4 +
drivers/net/ethernet/intel/idpf/idpf_ptp.c | 89 +++++++++++++++++++
drivers/net/ethernet/intel/idpf/idpf_ptp.h | 32 +++++++
.../net/ethernet/intel/idpf/idpf_virtchnl.c | 9 +-
7 files changed, 138 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/ethernet/intel/idpf/idpf_ptp.h
diff --git a/drivers/net/ethernet/intel/idpf/Kconfig b/drivers/net/ethernet/intel/idpf/Kconfig
index 1addd663acad..2c359a8551c7 100644
--- a/drivers/net/ethernet/intel/idpf/Kconfig
+++ b/drivers/net/ethernet/intel/idpf/Kconfig
@@ -4,6 +4,7 @@
config IDPF
tristate "Intel(R) Infrastructure Data Path Function Support"
depends on PCI_MSI
+ depends on PTP_1588_CLOCK_OPTIONAL
select DIMLIB
select LIBETH
help
diff --git a/drivers/net/ethernet/intel/idpf/Makefile b/drivers/net/ethernet/intel/idpf/Makefile
index 2ce01a0b5898..1f38a9d7125c 100644
--- a/drivers/net/ethernet/intel/idpf/Makefile
+++ b/drivers/net/ethernet/intel/idpf/Makefile
@@ -17,3 +17,4 @@ idpf-y := \
idpf_vf_dev.o
idpf-$(CONFIG_IDPF_SINGLEQ) += idpf_singleq_txrx.o
+idpf-$(CONFIG_PTP_1588_CLOCK) += idpf_ptp.o
diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h
index 5f73a4cf5161..5ad18bc3f386 100644
--- a/drivers/net/ethernet/intel/idpf/idpf.h
+++ b/drivers/net/ethernet/intel/idpf/idpf.h
@@ -530,6 +530,7 @@ struct idpf_vc_xn_manager;
* @vector_lock: Lock to protect vector distribution
* @queue_lock: Lock to protect queue distribution
* @vc_buf_lock: Lock to protect virtchnl buffer
+ * @ptp: Storage for PTP-related data
*/
struct idpf_adapter {
struct pci_dev *pdev;
@@ -587,6 +588,8 @@ struct idpf_adapter {
struct mutex vector_lock;
struct mutex queue_lock;
struct mutex vc_buf_lock;
+
+ struct idpf_ptp *ptp;
};
/**
diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c
index 1284ab2adaf1..b9874c59db0a 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_main.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_main.c
@@ -167,6 +167,10 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_free;
}
+ err = pci_enable_ptm(pdev, NULL);
+ if (err)
+ pci_dbg(pdev, "PCIe PTM is not supported by PCIe bus/controller\n");
+
/* set up for high or low dma */
err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
if (err) {
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
new file mode 100644
index 000000000000..1ac6367f5989
--- /dev/null
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2024 Intel Corporation */
+
+#include "idpf.h"
+#include "idpf_ptp.h"
+
+/**
+ * idpf_ptp_create_clock - Create PTP clock device for userspace
+ * @adapter: Driver specific private structure
+ *
+ * This function creates a new PTP clock device.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int idpf_ptp_create_clock(const struct idpf_adapter *adapter)
+{
+ struct ptp_clock *clock;
+
+ /* Attempt to register the clock before enabling the hardware. */
+ clock = ptp_clock_register(&adapter->ptp->info,
+ &adapter->pdev->dev);
+ if (IS_ERR(clock)) {
+ pci_err(adapter->pdev, "PTP clock creation failed: %pe\n", clock);
+ return PTR_ERR(clock);
+ }
+
+ adapter->ptp->clock = clock;
+
+ return 0;
+}
+
+/**
+ * idpf_ptp_init - Initialize PTP hardware clock support
+ * @adapter: Driver specific private structure
+ *
+ * Set up the device for interacting with the PTP hardware clock for all
+ * functions. Function will allocate and register a ptp_clock with the
+ * PTP_1588_CLOCK infrastructure.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int idpf_ptp_init(struct idpf_adapter *adapter)
+{
+ int err;
+
+ if (!idpf_is_cap_ena(adapter, IDPF_OTHER_CAPS, VIRTCHNL2_CAP_PTP)) {
+ pci_dbg(adapter->pdev, "PTP capability is not detected\n");
+ return -EOPNOTSUPP;
+ }
+
+ adapter->ptp = kzalloc(sizeof(*adapter->ptp), GFP_KERNEL);
+ if (!adapter->ptp)
+ return -ENOMEM;
+
+ /* add a back pointer to adapter */
+ adapter->ptp->adapter = adapter;
+
+ err = idpf_ptp_create_clock(adapter);
+ if (err)
+ goto free_ptp;
+
+ pci_dbg(adapter->pdev, "PTP init successful\n");
+
+ return 0;
+
+free_ptp:
+ kfree(adapter->ptp);
+ adapter->ptp = NULL;
+
+ return err;
+}
+
+/**
+ * idpf_ptp_release - Clear PTP hardware clock support
+ * @adapter: Driver specific private structure
+ */
+void idpf_ptp_release(struct idpf_adapter *adapter)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+
+ if (!ptp)
+ return;
+
+ if (ptp->clock)
+ ptp_clock_unregister(ptp->clock);
+
+ kfree(ptp);
+ adapter->ptp = NULL;
+}
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.h b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
new file mode 100644
index 000000000000..d009417bf947
--- /dev/null
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2024 Intel Corporation */
+
+#ifndef _IDPF_PTP_H
+#define _IDPF_PTP_H
+
+#include <linux/ptp_clock_kernel.h>
+
+/**
+ * struct idpf_ptp - PTP parameters
+ * @info: structure defining PTP hardware capabilities
+ * @clock: pointer to registered PTP clock device
+ * @adapter: back pointer to the adapter
+ */
+struct idpf_ptp {
+ struct ptp_clock_info info;
+ struct ptp_clock *clock;
+ struct idpf_adapter *adapter;
+};
+
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+int idpf_ptp_init(struct idpf_adapter *adapter);
+void idpf_ptp_release(struct idpf_adapter *adapter);
+#else /* CONFIG_PTP_1588_CLOCK */
+static inline int idpf_ptp_init(struct idpf_adapter *adapter)
+{
+ return 0;
+}
+
+static inline void idpf_ptp_release(struct idpf_adapter *adapter) { }
+#endif /* CONFIG_PTP_1588_CLOCK */
+#endif /* _IDPF_PTP_H */
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index 3d2413b8684f..7004289b974c 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -5,6 +5,7 @@
#include "idpf.h"
#include "idpf_virtchnl.h"
+#include "idpf_ptp.h"
#define IDPF_VC_XN_MIN_TIMEOUT_MSEC 2000
#define IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC (60 * 1000)
@@ -900,7 +901,8 @@ static int idpf_send_get_caps_msg(struct idpf_adapter *adapter)
VIRTCHNL2_CAP_MACFILTER |
VIRTCHNL2_CAP_SPLITQ_QSCHED |
VIRTCHNL2_CAP_PROMISC |
- VIRTCHNL2_CAP_LOOPBACK);
+ VIRTCHNL2_CAP_LOOPBACK |
+ VIRTCHNL2_CAP_PTP);
xn_params.vc_op = VIRTCHNL2_OP_GET_CAPS;
xn_params.send_buf.iov_base = ∩︀
@@ -3029,6 +3031,10 @@ int idpf_vc_core_init(struct idpf_adapter *adapter)
goto err_intr_req;
}
+ err = idpf_ptp_init(adapter);
+ if (err)
+ pci_err(adapter->pdev, "PTP init failed, err=%pe\n", ERR_PTR(err));
+
idpf_init_avail_queues(adapter);
/* Skew the delay for init tasks for each function based on fn number
@@ -3091,6 +3097,7 @@ void idpf_vc_core_deinit(struct idpf_adapter *adapter)
if (!remove_in_prog)
idpf_vc_xn_shutdown(adapter->vcxn_mngr);
+ idpf_ptp_release(adapter);
idpf_deinit_task(adapter);
idpf_intr_rel(adapter);
--
2.43.5
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v10 iwl-next 03/11] virtchnl: add PTP virtchnl definitions
2025-04-08 10:30 [PATCH v10 iwl-next 00/11] idpf: add initial PTP support Milena Olech
2025-04-08 10:30 ` [PATCH v10 iwl-next 01/11] idpf: change the method for mailbox workqueue allocation Milena Olech
2025-04-08 10:30 ` [PATCH v10 iwl-next 02/11] idpf: add initial PTP support Milena Olech
@ 2025-04-08 10:30 ` Milena Olech
2025-04-08 21:12 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:30 ` [PATCH v10 iwl-next 04/11] idpf: move virtchnl structures to the header file Milena Olech
` (7 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Milena Olech @ 2025-04-08 10:30 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Milena Olech,
Alexander Lobakin, Willem de Bruijn, Mina Almasry, Samuel Salin
PTP capabilities are negotiated using virtchnl commands. There are two
available modes of the PTP support: direct and mailbox. When the direct
access to PTP resources is negotiated, virtchnl messages returns a set
of registers that allow read/write directly. When the mailbox access to
PTP resources is negotiated, virtchnl messages are used to access
PTP clock and to read the timestamp values.
Virtchnl API covers both modes and exposes a set of PTP capabilities.
Using virtchnl API, the driver recognizes also HW abilities - maximum
adjustment of the clock and the basic increment value.
Additionally, API allows to configure the secondary mailbox, dedicated
exclusively for PTP purposes.
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Tested-by: Mina Almasry <almasrymina@google.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
---
v1 -> v2: fix struct description
drivers/net/ethernet/intel/idpf/virtchnl2.h | 302 ++++++++++++++++++++
1 file changed, 302 insertions(+)
diff --git a/drivers/net/ethernet/intel/idpf/virtchnl2.h b/drivers/net/ethernet/intel/idpf/virtchnl2.h
index 63deb120359c..44a5ee84ed60 100644
--- a/drivers/net/ethernet/intel/idpf/virtchnl2.h
+++ b/drivers/net/ethernet/intel/idpf/virtchnl2.h
@@ -68,6 +68,16 @@ enum virtchnl2_op {
VIRTCHNL2_OP_ADD_MAC_ADDR = 535,
VIRTCHNL2_OP_DEL_MAC_ADDR = 536,
VIRTCHNL2_OP_CONFIG_PROMISCUOUS_MODE = 537,
+
+ /* TimeSync opcodes */
+ VIRTCHNL2_OP_PTP_GET_CAPS = 541,
+ VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP = 542,
+ VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME = 543,
+ VIRTCHNL2_OP_PTP_GET_CROSS_TIME = 544,
+ VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME = 545,
+ VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE = 546,
+ VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME = 547,
+ VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS = 548,
};
/**
@@ -1270,4 +1280,296 @@ struct virtchnl2_promisc_info {
};
VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_promisc_info);
+/**
+ * enum virtchnl2_ptp_caps - PTP capabilities
+ * @VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME: direct access to get the time of
+ * device clock
+ * @VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME_MB: mailbox access to get the time of
+ * device clock
+ * @VIRTCHNL2_CAP_PTP_GET_CROSS_TIME: direct access to cross timestamp
+ * @VIRTCHNL2_CAP_PTP_GET_CROSS_TIME_MB: mailbox access to cross timestamp
+ * @VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME: direct access to set the time of
+ * device clock
+ * @VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB: mailbox access to set the time of
+ * device clock
+ * @VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK: direct access to adjust the time of device
+ * clock
+ * @VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB: mailbox access to adjust the time of
+ * device clock
+ * @VIRTCHNL2_CAP_PTP_TX_TSTAMPS: direct access to the Tx timestamping
+ * @VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB: mailbox access to the Tx timestamping
+ *
+ * PF/VF negotiates a set of supported PTP capabilities with the Control Plane.
+ * There are two access methods - mailbox (_MB) and direct.
+ * PTP capabilities enables Main Timer operations: get/set/adjust Main Timer,
+ * cross timestamping and the Tx timestamping.
+ */
+enum virtchnl2_ptp_caps {
+ VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME = BIT(0),
+ VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME_MB = BIT(1),
+ VIRTCHNL2_CAP_PTP_GET_CROSS_TIME = BIT(2),
+ VIRTCHNL2_CAP_PTP_GET_CROSS_TIME_MB = BIT(3),
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME = BIT(4),
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB = BIT(5),
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK = BIT(6),
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB = BIT(7),
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS = BIT(8),
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB = BIT(9),
+};
+
+/**
+ * struct virtchnl2_ptp_clk_reg_offsets - Offsets of device and PHY clocks
+ * registers.
+ * @dev_clk_ns_l: Device clock low register offset
+ * @dev_clk_ns_h: Device clock high register offset
+ * @phy_clk_ns_l: PHY clock low register offset
+ * @phy_clk_ns_h: PHY clock high register offset
+ * @cmd_sync_trigger: The command sync trigger register offset
+ * @pad: Padding for future extensions
+ */
+struct virtchnl2_ptp_clk_reg_offsets {
+ __le32 dev_clk_ns_l;
+ __le32 dev_clk_ns_h;
+ __le32 phy_clk_ns_l;
+ __le32 phy_clk_ns_h;
+ __le32 cmd_sync_trigger;
+ u8 pad[4];
+};
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_clk_reg_offsets);
+
+/**
+ * struct virtchnl2_ptp_cross_time_reg_offsets - Offsets of the device cross
+ * time registers.
+ * @sys_time_ns_l: System time low register offset
+ * @sys_time_ns_h: System time high register offset
+ * @cmd_sync_trigger: The command sync trigger register offset
+ * @pad: Padding for future extensions
+ */
+struct virtchnl2_ptp_cross_time_reg_offsets {
+ __le32 sys_time_ns_l;
+ __le32 sys_time_ns_h;
+ __le32 cmd_sync_trigger;
+ u8 pad[4];
+};
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_cross_time_reg_offsets);
+
+/**
+ * struct virtchnl2_ptp_clk_adj_reg_offsets - Offsets of device and PHY clocks
+ * adjustments registers.
+ * @dev_clk_cmd_type: Device clock command type register offset
+ * @dev_clk_incval_l: Device clock increment value low register offset
+ * @dev_clk_incval_h: Device clock increment value high registers offset
+ * @dev_clk_shadj_l: Device clock shadow adjust low register offset
+ * @dev_clk_shadj_h: Device clock shadow adjust high register offset
+ * @phy_clk_cmd_type: PHY timer command type register offset
+ * @phy_clk_incval_l: PHY timer increment value low register offset
+ * @phy_clk_incval_h: PHY timer increment value high register offset
+ * @phy_clk_shadj_l: PHY timer shadow adjust low register offset
+ * @phy_clk_shadj_h: PHY timer shadow adjust high register offset
+ */
+struct virtchnl2_ptp_clk_adj_reg_offsets {
+ __le32 dev_clk_cmd_type;
+ __le32 dev_clk_incval_l;
+ __le32 dev_clk_incval_h;
+ __le32 dev_clk_shadj_l;
+ __le32 dev_clk_shadj_h;
+ __le32 phy_clk_cmd_type;
+ __le32 phy_clk_incval_l;
+ __le32 phy_clk_incval_h;
+ __le32 phy_clk_shadj_l;
+ __le32 phy_clk_shadj_h;
+};
+VIRTCHNL2_CHECK_STRUCT_LEN(40, virtchnl2_ptp_clk_adj_reg_offsets);
+
+/**
+ * struct virtchnl2_ptp_tx_tstamp_latch_caps - PTP Tx timestamp latch
+ * capabilities.
+ * @tx_latch_reg_offset_l: Tx timestamp latch low register offset
+ * @tx_latch_reg_offset_h: Tx timestamp latch high register offset
+ * @index: Latch index provided to the Tx descriptor
+ * @pad: Padding for future extensions
+ */
+struct virtchnl2_ptp_tx_tstamp_latch_caps {
+ __le32 tx_latch_reg_offset_l;
+ __le32 tx_latch_reg_offset_h;
+ u8 index;
+ u8 pad[7];
+};
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch_caps);
+
+/**
+ * struct virtchnl2_ptp_get_vport_tx_tstamp_caps - Structure that defines Tx
+ * tstamp entries.
+ * @vport_id: Vport number
+ * @num_latches: Total number of latches
+ * @tstamp_ns_lo_bit: First bit for nanosecond part of the timestamp
+ * @tstamp_ns_hi_bit: Last bit for nanosecond part of the timestamp
+ * @pad: Padding for future tstamp granularity extensions
+ * @tstamp_latches: Capabilities of Tx timestamp entries
+ *
+ * PF/VF sends this message to negotiate the Tx timestamp latches for each
+ * Vport.
+ *
+ * Associated with VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS.
+ */
+struct virtchnl2_ptp_get_vport_tx_tstamp_caps {
+ __le32 vport_id;
+ __le16 num_latches;
+ u8 tstamp_ns_lo_bit;
+ u8 tstamp_ns_hi_bit;
+ u8 pad[8];
+
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tstamp_latches[]
+ __counted_by_le(num_latches);
+};
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_vport_tx_tstamp_caps);
+
+/**
+ * struct virtchnl2_ptp_get_caps - Get PTP capabilities
+ * @caps: PTP capability bitmap. See enum virtchnl2_ptp_caps
+ * @max_adj: The maximum possible frequency adjustment
+ * @base_incval: The default timer increment value
+ * @peer_mbx_q_id: ID of the PTP Device Control daemon queue
+ * @peer_id: Peer ID for PTP Device Control daemon
+ * @secondary_mbx: Indicates to the driver that it should create a secondary
+ * mailbox to inetract with control plane for PTP
+ * @pad: Padding for future extensions
+ * @clk_offsets: Main timer and PHY registers offsets
+ * @cross_time_offsets: Cross time registers offsets
+ * @clk_adj_offsets: Offsets needed to adjust the PHY and the main timer
+ *
+ * PF/VF sends this message to negotiate PTP capabilities. CP updates bitmap
+ * with supported features and fulfills appropriate structures.
+ * If HW uses primary MBX for PTP: secondary_mbx is set to false.
+ * If HW uses secondary MBX for PTP: secondary_mbx is set to true.
+ * Control plane has 2 MBX and the driver has 1 MBX, send to peer
+ * driver may be used to send a message using valid ptp_peer_mb_q_id and
+ * ptp_peer_id.
+ * If HW does not use send to peer driver: secondary_mbx is no care field and
+ * peer_mbx_q_id holds invalid value (0xFFFF).
+ *
+ * Associated with VIRTCHNL2_OP_PTP_GET_CAPS.
+ */
+struct virtchnl2_ptp_get_caps {
+ __le32 caps;
+ __le32 max_adj;
+ __le64 base_incval;
+ __le16 peer_mbx_q_id;
+ u8 peer_id;
+ u8 secondary_mbx;
+ u8 pad[4];
+
+ struct virtchnl2_ptp_clk_reg_offsets clk_offsets;
+ struct virtchnl2_ptp_cross_time_reg_offsets cross_time_offsets;
+ struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
+};
+VIRTCHNL2_CHECK_STRUCT_LEN(104, virtchnl2_ptp_get_caps);
+
+/**
+ * struct virtchnl2_ptp_tx_tstamp_latch - Structure that describes tx tstamp
+ * values, index and validity.
+ * @tstamp: Timestamp value
+ * @index: Timestamp index from which the value is read
+ * @valid: Timestamp validity
+ * @pad: Padding for future extensions
+ */
+struct virtchnl2_ptp_tx_tstamp_latch {
+ __le64 tstamp;
+ u8 index;
+ u8 valid;
+ u8 pad[6];
+};
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch);
+
+/**
+ * struct virtchnl2_ptp_get_vport_tx_tstamp_latches - Tx timestamp latches
+ * associated with the vport.
+ * @vport_id: Number of vport that requests the timestamp
+ * @num_latches: Number of latches
+ * @get_devtime_with_txtstmp: Flag to request device time along with Tx timestamp
+ * @pad: Padding for future extensions
+ * @device_time: device time if get_devtime_with_txtstmp was set in request
+ * @tstamp_latches: PTP TX timestamp latch
+ *
+ * PF/VF sends this message to receive a specified number of timestamps
+ * entries.
+ *
+ * Associated with VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP.
+ */
+struct virtchnl2_ptp_get_vport_tx_tstamp_latches {
+ __le32 vport_id;
+ __le16 num_latches;
+ u8 get_devtime_with_txtstmp;
+ u8 pad[1];
+ __le64 device_time;
+
+ struct virtchnl2_ptp_tx_tstamp_latch tstamp_latches[]
+ __counted_by_le(num_latches);
+};
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_vport_tx_tstamp_latches);
+
+/**
+ * struct virtchnl2_ptp_get_dev_clk_time - Associated with message
+ * VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME.
+ * @dev_time_ns: Device clock time value in nanoseconds
+ *
+ * PF/VF sends this message to receive the time from the main timer.
+ */
+struct virtchnl2_ptp_get_dev_clk_time {
+ __le64 dev_time_ns;
+};
+VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_get_dev_clk_time);
+
+/**
+ * struct virtchnl2_ptp_get_cross_time: Associated with message
+ * VIRTCHNL2_OP_PTP_GET_CROSS_TIME.
+ * @sys_time_ns: System counter value expressed in nanoseconds, read
+ * synchronously with device time
+ * @dev_time_ns: Device clock time value expressed in nanoseconds
+ *
+ * PF/VF sends this message to receive the cross time.
+ */
+struct virtchnl2_ptp_get_cross_time {
+ __le64 sys_time_ns;
+ __le64 dev_time_ns;
+};
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_cross_time);
+
+/**
+ * struct virtchnl2_ptp_set_dev_clk_time: Associated with message
+ * VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME.
+ * @dev_time_ns: Device time value expressed in nanoseconds to set
+ *
+ * PF/VF sends this message to set the time of the main timer.
+ */
+struct virtchnl2_ptp_set_dev_clk_time {
+ __le64 dev_time_ns;
+};
+VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_set_dev_clk_time);
+
+/**
+ * struct virtchnl2_ptp_adj_dev_clk_fine: Associated with message
+ * VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE.
+ * @incval: Source timer increment value per clock cycle
+ *
+ * PF/VF sends this message to adjust the frequency of the main timer by the
+ * indicated scaled ppm.
+ */
+struct virtchnl2_ptp_adj_dev_clk_fine {
+ __le64 incval;
+};
+VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_adj_dev_clk_fine);
+
+/**
+ * struct virtchnl2_ptp_adj_dev_clk_time: Associated with message
+ * VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME.
+ * @delta: Offset in nanoseconds to adjust the time by
+ *
+ * PF/VF sends this message to adjust the time of the main timer by the delta.
+ */
+struct virtchnl2_ptp_adj_dev_clk_time {
+ __le64 delta;
+};
+VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_adj_dev_clk_time);
+
#endif /* _VIRTCHNL_2_H_ */
--
2.43.5
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v10 iwl-next 04/11] idpf: move virtchnl structures to the header file
2025-04-08 10:30 [PATCH v10 iwl-next 00/11] idpf: add initial PTP support Milena Olech
` (2 preceding siblings ...)
2025-04-08 10:30 ` [PATCH v10 iwl-next 03/11] virtchnl: add PTP virtchnl definitions Milena Olech
@ 2025-04-08 10:30 ` Milena Olech
2025-04-08 21:12 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:30 ` [PATCH v10 iwl-next 05/11] idpf: negotiate PTP capabilities and get PTP clock Milena Olech
` (6 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Milena Olech @ 2025-04-08 10:30 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Milena Olech,
Alexander Lobakin, Willem de Bruijn, Mina Almasry, Samuel Salin
Move virtchnl structures to the header file to expose them for the PTP
virtchnl file.
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Tested-by: Mina Almasry <almasrymina@google.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
---
v1 -> v2: fix commit message title
.../net/ethernet/intel/idpf/idpf_virtchnl.c | 86 +------------------
.../net/ethernet/intel/idpf/idpf_virtchnl.h | 84 ++++++++++++++++++
2 files changed, 86 insertions(+), 84 deletions(-)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index 7004289b974c..a55ff20895ed 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -7,88 +7,6 @@
#include "idpf_virtchnl.h"
#include "idpf_ptp.h"
-#define IDPF_VC_XN_MIN_TIMEOUT_MSEC 2000
-#define IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC (60 * 1000)
-#define IDPF_VC_XN_IDX_M GENMASK(7, 0)
-#define IDPF_VC_XN_SALT_M GENMASK(15, 8)
-#define IDPF_VC_XN_RING_LEN U8_MAX
-
-/**
- * enum idpf_vc_xn_state - Virtchnl transaction status
- * @IDPF_VC_XN_IDLE: not expecting a reply, ready to be used
- * @IDPF_VC_XN_WAITING: expecting a reply, not yet received
- * @IDPF_VC_XN_COMPLETED_SUCCESS: a reply was expected and received,
- * buffer updated
- * @IDPF_VC_XN_COMPLETED_FAILED: a reply was expected and received, but there
- * was an error, buffer not updated
- * @IDPF_VC_XN_SHUTDOWN: transaction object cannot be used, VC torn down
- * @IDPF_VC_XN_ASYNC: transaction sent asynchronously and doesn't have the
- * return context; a callback may be provided to handle
- * return
- */
-enum idpf_vc_xn_state {
- IDPF_VC_XN_IDLE = 1,
- IDPF_VC_XN_WAITING,
- IDPF_VC_XN_COMPLETED_SUCCESS,
- IDPF_VC_XN_COMPLETED_FAILED,
- IDPF_VC_XN_SHUTDOWN,
- IDPF_VC_XN_ASYNC,
-};
-
-struct idpf_vc_xn;
-/* Callback for asynchronous messages */
-typedef int (*async_vc_cb) (struct idpf_adapter *, struct idpf_vc_xn *,
- const struct idpf_ctlq_msg *);
-
-/**
- * struct idpf_vc_xn - Data structure representing virtchnl transactions
- * @completed: virtchnl event loop uses that to signal when a reply is
- * available, uses kernel completion API
- * @state: virtchnl event loop stores the data below, protected by the
- * completion's lock.
- * @reply_sz: Original size of reply, may be > reply_buf.iov_len; it will be
- * truncated on its way to the receiver thread according to
- * reply_buf.iov_len.
- * @reply: Reference to the buffer(s) where the reply data should be written
- * to. May be 0-length (then NULL address permitted) if the reply data
- * should be ignored.
- * @async_handler: if sent asynchronously, a callback can be provided to handle
- * the reply when it's received
- * @vc_op: corresponding opcode sent with this transaction
- * @idx: index used as retrieval on reply receive, used for cookie
- * @salt: changed every message to make unique, used for cookie
- */
-struct idpf_vc_xn {
- struct completion completed;
- enum idpf_vc_xn_state state;
- size_t reply_sz;
- struct kvec reply;
- async_vc_cb async_handler;
- u32 vc_op;
- u8 idx;
- u8 salt;
-};
-
-/**
- * struct idpf_vc_xn_params - Parameters for executing transaction
- * @send_buf: kvec for send buffer
- * @recv_buf: kvec for recv buffer, may be NULL, must then have zero length
- * @timeout_ms: timeout to wait for reply
- * @async: send message asynchronously, will not wait on completion
- * @async_handler: If sent asynchronously, optional callback handler. The user
- * must be careful when using async handlers as the memory for
- * the recv_buf _cannot_ be on stack if this is async.
- * @vc_op: virtchnl op to send
- */
-struct idpf_vc_xn_params {
- struct kvec send_buf;
- struct kvec recv_buf;
- int timeout_ms;
- bool async;
- async_vc_cb async_handler;
- u32 vc_op;
-};
-
/**
* struct idpf_vc_xn_manager - Manager for tracking transactions
* @ring: backing and lookup for transactions
@@ -450,8 +368,8 @@ static void idpf_vc_xn_push_free(struct idpf_vc_xn_manager *vcxn_mngr,
* >= @recv_buf.iov_len, but we never overflow @@recv_buf_iov_base). < 0 for
* error.
*/
-static ssize_t idpf_vc_xn_exec(struct idpf_adapter *adapter,
- const struct idpf_vc_xn_params *params)
+ssize_t idpf_vc_xn_exec(struct idpf_adapter *adapter,
+ const struct idpf_vc_xn_params *params)
{
const struct kvec *send_buf = ¶ms->send_buf;
struct idpf_vc_xn *xn;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
index 83da5d8da56b..3522c1238ea2 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
@@ -4,6 +4,88 @@
#ifndef _IDPF_VIRTCHNL_H_
#define _IDPF_VIRTCHNL_H_
+#define IDPF_VC_XN_MIN_TIMEOUT_MSEC 2000
+#define IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC (60 * 1000)
+#define IDPF_VC_XN_IDX_M GENMASK(7, 0)
+#define IDPF_VC_XN_SALT_M GENMASK(15, 8)
+#define IDPF_VC_XN_RING_LEN U8_MAX
+
+/**
+ * enum idpf_vc_xn_state - Virtchnl transaction status
+ * @IDPF_VC_XN_IDLE: not expecting a reply, ready to be used
+ * @IDPF_VC_XN_WAITING: expecting a reply, not yet received
+ * @IDPF_VC_XN_COMPLETED_SUCCESS: a reply was expected and received, buffer
+ * updated
+ * @IDPF_VC_XN_COMPLETED_FAILED: a reply was expected and received, but there
+ * was an error, buffer not updated
+ * @IDPF_VC_XN_SHUTDOWN: transaction object cannot be used, VC torn down
+ * @IDPF_VC_XN_ASYNC: transaction sent asynchronously and doesn't have the
+ * return context; a callback may be provided to handle
+ * return
+ */
+enum idpf_vc_xn_state {
+ IDPF_VC_XN_IDLE = 1,
+ IDPF_VC_XN_WAITING,
+ IDPF_VC_XN_COMPLETED_SUCCESS,
+ IDPF_VC_XN_COMPLETED_FAILED,
+ IDPF_VC_XN_SHUTDOWN,
+ IDPF_VC_XN_ASYNC,
+};
+
+struct idpf_vc_xn;
+/* Callback for asynchronous messages */
+typedef int (*async_vc_cb) (struct idpf_adapter *, struct idpf_vc_xn *,
+ const struct idpf_ctlq_msg *);
+
+/**
+ * struct idpf_vc_xn - Data structure representing virtchnl transactions
+ * @completed: virtchnl event loop uses that to signal when a reply is
+ * available, uses kernel completion API
+ * @state: virtchnl event loop stores the data below, protected by the
+ * completion's lock.
+ * @reply_sz: Original size of reply, may be > reply_buf.iov_len; it will be
+ * truncated on its way to the receiver thread according to
+ * reply_buf.iov_len.
+ * @reply: Reference to the buffer(s) where the reply data should be written
+ * to. May be 0-length (then NULL address permitted) if the reply data
+ * should be ignored.
+ * @async_handler: if sent asynchronously, a callback can be provided to handle
+ * the reply when it's received
+ * @vc_op: corresponding opcode sent with this transaction
+ * @idx: index used as retrieval on reply receive, used for cookie
+ * @salt: changed every message to make unique, used for cookie
+ */
+struct idpf_vc_xn {
+ struct completion completed;
+ enum idpf_vc_xn_state state;
+ size_t reply_sz;
+ struct kvec reply;
+ async_vc_cb async_handler;
+ u32 vc_op;
+ u8 idx;
+ u8 salt;
+};
+
+/**
+ * struct idpf_vc_xn_params - Parameters for executing transaction
+ * @send_buf: kvec for send buffer
+ * @recv_buf: kvec for recv buffer, may be NULL, must then have zero length
+ * @timeout_ms: timeout to wait for reply
+ * @async: send message asynchronously, will not wait on completion
+ * @async_handler: If sent asynchronously, optional callback handler. The user
+ * must be careful when using async handlers as the memory for
+ * the recv_buf _cannot_ be on stack if this is async.
+ * @vc_op: virtchnl op to send
+ */
+struct idpf_vc_xn_params {
+ struct kvec send_buf;
+ struct kvec recv_buf;
+ int timeout_ms;
+ bool async;
+ async_vc_cb async_handler;
+ u32 vc_op;
+};
+
struct idpf_adapter;
struct idpf_netdev_priv;
struct idpf_vec_regs;
@@ -11,6 +93,8 @@ struct idpf_vport;
struct idpf_vport_max_q;
struct idpf_vport_user_config_data;
+ssize_t idpf_vc_xn_exec(struct idpf_adapter *adapter,
+ const struct idpf_vc_xn_params *params);
int idpf_init_dflt_mbx(struct idpf_adapter *adapter);
void idpf_deinit_dflt_mbx(struct idpf_adapter *adapter);
int idpf_vc_core_init(struct idpf_adapter *adapter);
--
2.43.5
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v10 iwl-next 05/11] idpf: negotiate PTP capabilities and get PTP clock
2025-04-08 10:30 [PATCH v10 iwl-next 00/11] idpf: add initial PTP support Milena Olech
` (3 preceding siblings ...)
2025-04-08 10:30 ` [PATCH v10 iwl-next 04/11] idpf: move virtchnl structures to the header file Milena Olech
@ 2025-04-08 10:30 ` Milena Olech
2025-04-08 21:15 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:30 ` [PATCH v10 iwl-next 06/11] idpf: add mailbox access to read PTP clock time Milena Olech
` (5 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Milena Olech @ 2025-04-08 10:30 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Milena Olech,
Willem de Bruijn, Mina Almasry, Samuel Salin
PTP capabilities are negotiated using virtchnl command. Add get
capabilities function, direct access to read the PTP clock.
Set initial PTP capabilities exposed to the stack.
Tested-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Tested-by: Mina Almasry <almasrymina@google.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
---
v9 -> v10: remove cross timestamping, add spinlock to protect device
clock read operation, change device clock read latch mechanism
v1 -> v2: change the size of access fields in ptp struct,
remove CONFIG_PCIE_PTM dependency
drivers/net/ethernet/intel/idpf/Makefile | 2 +
drivers/net/ethernet/intel/idpf/idpf.h | 2 +
drivers/net/ethernet/intel/idpf/idpf_dev.c | 14 ++
.../ethernet/intel/idpf/idpf_lan_pf_regs.h | 4 +
drivers/net/ethernet/intel/idpf/idpf_ptp.c | 170 ++++++++++++++++++
drivers/net/ethernet/intel/idpf/idpf_ptp.h | 84 +++++++++
.../ethernet/intel/idpf/idpf_virtchnl_ptp.c | 78 ++++++++
7 files changed, 354 insertions(+)
create mode 100644 drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
diff --git a/drivers/net/ethernet/intel/idpf/Makefile b/drivers/net/ethernet/intel/idpf/Makefile
index 1f38a9d7125c..83ac5e296382 100644
--- a/drivers/net/ethernet/intel/idpf/Makefile
+++ b/drivers/net/ethernet/intel/idpf/Makefile
@@ -17,4 +17,6 @@ idpf-y := \
idpf_vf_dev.o
idpf-$(CONFIG_IDPF_SINGLEQ) += idpf_singleq_txrx.o
+
idpf-$(CONFIG_PTP_1588_CLOCK) += idpf_ptp.o
+idpf-$(CONFIG_PTP_1588_CLOCK) += idpf_virtchnl_ptp.o
diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h
index 5ad18bc3f386..a5be660a8cf8 100644
--- a/drivers/net/ethernet/intel/idpf/idpf.h
+++ b/drivers/net/ethernet/intel/idpf/idpf.h
@@ -189,6 +189,7 @@ struct idpf_vport_max_q {
* @mb_intr_reg_init: Mailbox interrupt register initialization
* @reset_reg_init: Reset register initialization
* @trigger_reset: Trigger a reset to occur
+ * @ptp_reg_init: PTP register initialization
*/
struct idpf_reg_ops {
void (*ctlq_reg_init)(struct idpf_ctlq_create_info *cq);
@@ -197,6 +198,7 @@ struct idpf_reg_ops {
void (*reset_reg_init)(struct idpf_adapter *adapter);
void (*trigger_reset)(struct idpf_adapter *adapter,
enum idpf_flags trig_cause);
+ void (*ptp_reg_init)(const struct idpf_adapter *adapter);
};
/**
diff --git a/drivers/net/ethernet/intel/idpf/idpf_dev.c b/drivers/net/ethernet/intel/idpf/idpf_dev.c
index 41e4bd49402a..3fae81f1f988 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_dev.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_dev.c
@@ -4,6 +4,7 @@
#include "idpf.h"
#include "idpf_lan_pf_regs.h"
#include "idpf_virtchnl.h"
+#include "idpf_ptp.h"
#define IDPF_PF_ITR_IDX_SPACING 0x4
@@ -148,6 +149,18 @@ static void idpf_trigger_reset(struct idpf_adapter *adapter,
idpf_get_reg_addr(adapter, PFGEN_CTRL));
}
+/**
+ * idpf_ptp_reg_init - Initialize required registers
+ * @adapter: Driver specific private structure
+ *
+ * Set the bits required for enabling shtime and cmd execution
+ */
+static void idpf_ptp_reg_init(const struct idpf_adapter *adapter)
+{
+ adapter->ptp->cmd.shtime_enable_mask = PF_GLTSYN_CMD_SYNC_SHTIME_EN_M;
+ adapter->ptp->cmd.exec_cmd_mask = PF_GLTSYN_CMD_SYNC_EXEC_CMD_M;
+}
+
/**
* idpf_reg_ops_init - Initialize register API function pointers
* @adapter: Driver specific private structure
@@ -159,6 +172,7 @@ static void idpf_reg_ops_init(struct idpf_adapter *adapter)
adapter->dev_ops.reg_ops.mb_intr_reg_init = idpf_mb_intr_reg_init;
adapter->dev_ops.reg_ops.reset_reg_init = idpf_reset_reg_init;
adapter->dev_ops.reg_ops.trigger_reset = idpf_trigger_reset;
+ adapter->dev_ops.reg_ops.ptp_reg_init = idpf_ptp_reg_init;
}
/**
diff --git a/drivers/net/ethernet/intel/idpf/idpf_lan_pf_regs.h b/drivers/net/ethernet/intel/idpf/idpf_lan_pf_regs.h
index 24edb8a6ec2e..cc9aa2b6a14a 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_lan_pf_regs.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_lan_pf_regs.h
@@ -53,6 +53,10 @@
#define PF_FW_ATQH_ATQH_M GENMASK(9, 0)
#define PF_FW_ATQT (PF_FW_BASE + 0x24)
+/* Timesync registers */
+#define PF_GLTSYN_CMD_SYNC_EXEC_CMD_M GENMASK(1, 0)
+#define PF_GLTSYN_CMD_SYNC_SHTIME_EN_M BIT(2)
+
/* Interrupts */
#define PF_GLINT_BASE 0x08900000
#define PF_GLINT_DYN_CTL(_INT) (PF_GLINT_BASE + ((_INT) * 0x1000))
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
index 1ac6367f5989..cb27a8ee3acc 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
@@ -4,6 +4,163 @@
#include "idpf.h"
#include "idpf_ptp.h"
+/**
+ * idpf_ptp_get_access - Determine the access type of the PTP features
+ * @adapter: Driver specific private structure
+ * @direct: Capability that indicates the direct access
+ * @mailbox: Capability that indicates the mailbox access
+ *
+ * Return: the type of supported access for the PTP feature.
+ */
+static enum idpf_ptp_access
+idpf_ptp_get_access(const struct idpf_adapter *adapter, u32 direct, u32 mailbox)
+{
+ if (adapter->ptp->caps & direct)
+ return IDPF_PTP_DIRECT;
+ else if (adapter->ptp->caps & mailbox)
+ return IDPF_PTP_MAILBOX;
+ else
+ return IDPF_PTP_NONE;
+}
+
+/**
+ * idpf_ptp_get_features_access - Determine the access type of PTP features
+ * @adapter: Driver specific private structure
+ *
+ * Fulfill the adapter structure with type of the supported PTP features
+ * access.
+ */
+void idpf_ptp_get_features_access(const struct idpf_adapter *adapter)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+ u32 direct, mailbox;
+
+ /* Get the device clock time */
+ direct = VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME;
+ mailbox = VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME_MB;
+ ptp->get_dev_clk_time_access = idpf_ptp_get_access(adapter,
+ direct,
+ mailbox);
+}
+
+/**
+ * idpf_ptp_enable_shtime - Enable shadow time and execute a command
+ * @adapter: Driver specific private structure
+ */
+static void idpf_ptp_enable_shtime(struct idpf_adapter *adapter)
+{
+ u32 shtime_enable, exec_cmd;
+
+ /* Get offsets */
+ shtime_enable = adapter->ptp->cmd.shtime_enable_mask;
+ exec_cmd = adapter->ptp->cmd.exec_cmd_mask;
+
+ /* Set the shtime en and the sync field */
+ writel(shtime_enable, adapter->ptp->dev_clk_regs.cmd_sync);
+ writel(exec_cmd | shtime_enable, adapter->ptp->dev_clk_regs.cmd_sync);
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer value
+ * @adapter: Driver specific private structure
+ * @sts: Optional parameter for holding a pair of system timestamps from
+ * the system clock. Will be ignored when NULL is given.
+ *
+ * Return: the device clock time.
+ */
+static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter,
+ struct ptp_system_timestamp *sts)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+ u32 hi, lo;
+
+ spin_lock(&ptp->read_dev_clk_lock);
+
+ /* Read the system timestamp pre PHC read */
+ ptp_read_system_prets(sts);
+
+ idpf_ptp_enable_shtime(adapter);
+
+ /* Read the system timestamp post PHC read */
+ ptp_read_system_postts(sts);
+
+ lo = readl(ptp->dev_clk_regs.dev_clk_ns_l);
+ hi = readl(ptp->dev_clk_regs.dev_clk_ns_h);
+
+ spin_unlock(&ptp->read_dev_clk_lock);
+
+ return ((u64)hi << 32) | lo;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg - Read the main timer value
+ * @adapter: Driver specific private structure
+ * @src_clk: Returned main timer value in nanoseconds unit
+ * @sts: Optional parameter for holding a pair of system timestamps from
+ * the system clock. Will be ignored if NULL is given.
+ *
+ * Return: the device clock time on success, -errno otherwise.
+ */
+static int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk,
+ struct ptp_system_timestamp *sts)
+{
+ switch (adapter->ptp->get_dev_clk_time_access) {
+ case IDPF_PTP_NONE:
+ return -EOPNOTSUPP;
+ case IDPF_PTP_DIRECT:
+ *src_clk = idpf_ptp_read_src_clk_reg_direct(adapter, sts);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
+ * idpf_ptp_gettimex64 - Get the time of the clock
+ * @info: the driver's PTP info structure
+ * @ts: timespec64 structure to hold the current time value
+ * @sts: Optional parameter for holding a pair of system timestamps from
+ * the system clock. Will be ignored if NULL is given.
+ *
+ * Return: the device clock value in ns, after converting it into a timespec
+ * struct on success, -errno otherwise.
+ */
+static int idpf_ptp_gettimex64(struct ptp_clock_info *info,
+ struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ struct idpf_adapter *adapter = idpf_ptp_info_to_adapter(info);
+ u64 time_ns;
+ int err;
+
+ err = idpf_ptp_read_src_clk_reg(adapter, &time_ns, sts);
+ if (err)
+ return -EACCES;
+
+ *ts = ns_to_timespec64(time_ns);
+
+ return 0;
+}
+
+/**
+ * idpf_ptp_set_caps - Set PTP capabilities
+ * @adapter: Driver specific private structure
+ *
+ * This function sets the PTP functions.
+ */
+static void idpf_ptp_set_caps(const struct idpf_adapter *adapter)
+{
+ struct ptp_clock_info *info = &adapter->ptp->info;
+
+ snprintf(info->name, sizeof(info->name), "%s-%s-clk",
+ KBUILD_MODNAME, pci_name(adapter->pdev));
+
+ info->owner = THIS_MODULE;
+ info->gettimex64 = idpf_ptp_gettimex64;
+}
+
/**
* idpf_ptp_create_clock - Create PTP clock device for userspace
* @adapter: Driver specific private structure
@@ -16,6 +173,8 @@ static int idpf_ptp_create_clock(const struct idpf_adapter *adapter)
{
struct ptp_clock *clock;
+ idpf_ptp_set_caps(adapter);
+
/* Attempt to register the clock before enabling the hardware. */
clock = ptp_clock_register(&adapter->ptp->info,
&adapter->pdev->dev);
@@ -55,10 +214,21 @@ int idpf_ptp_init(struct idpf_adapter *adapter)
/* add a back pointer to adapter */
adapter->ptp->adapter = adapter;
+ if (adapter->dev_ops.reg_ops.ptp_reg_init)
+ adapter->dev_ops.reg_ops.ptp_reg_init(adapter);
+
+ err = idpf_ptp_get_caps(adapter);
+ if (err) {
+ pci_err(adapter->pdev, "Failed to get PTP caps err %d\n", err);
+ goto free_ptp;
+ }
+
err = idpf_ptp_create_clock(adapter);
if (err)
goto free_ptp;
+ spin_lock_init(&adapter->ptp->read_dev_clk_lock);
+
pci_dbg(adapter->pdev, "PTP init successful\n");
return 0;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.h b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
index d009417bf947..955d697e51ab 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
@@ -6,21 +6,96 @@
#include <linux/ptp_clock_kernel.h>
+/**
+ * struct idpf_ptp_cmd - PTP command masks
+ * @exec_cmd_mask: mask to trigger command execution
+ * @shtime_enable_mask: mask to enable shadow time
+ */
+struct idpf_ptp_cmd {
+ u32 exec_cmd_mask;
+ u32 shtime_enable_mask;
+};
+
+/* struct idpf_ptp_dev_clk_regs - PTP device registers
+ * @dev_clk_ns_l: low part of the device clock register
+ * @dev_clk_ns_h: high part of the device clock register
+ * @phy_clk_ns_l: low part of the PHY clock register
+ * @phy_clk_ns_h: high part of the PHY clock register
+ * @cmd: PTP command register
+ * @phy_cmd: PHY command register
+ * @cmd_sync: PTP command synchronization register
+ */
+struct idpf_ptp_dev_clk_regs {
+ /* Main clock */
+ void __iomem *dev_clk_ns_l;
+ void __iomem *dev_clk_ns_h;
+
+ /* PHY timer */
+ void __iomem *phy_clk_ns_l;
+ void __iomem *phy_clk_ns_h;
+
+ /* Command */
+ void __iomem *cmd;
+ void __iomem *phy_cmd;
+ void __iomem *cmd_sync;
+};
+
+/**
+ * enum idpf_ptp_access - the type of access to PTP operations
+ * @IDPF_PTP_NONE: no access
+ * @IDPF_PTP_DIRECT: direct access through BAR registers
+ * @IDPF_PTP_MAILBOX: access through mailbox messages
+ */
+enum idpf_ptp_access {
+ IDPF_PTP_NONE = 0,
+ IDPF_PTP_DIRECT,
+ IDPF_PTP_MAILBOX,
+};
+
/**
* struct idpf_ptp - PTP parameters
* @info: structure defining PTP hardware capabilities
* @clock: pointer to registered PTP clock device
* @adapter: back pointer to the adapter
+ * @cmd: HW specific command masks
+ * @dev_clk_regs: the set of registers to access the device clock
+ * @caps: PTP capabilities negotiated with the Control Plane
+ * @get_dev_clk_time_access: access type for getting the device clock time
+ * @rsv: reserved bits
+ * @read_dev_clk_lock: spinlock protecting access to the device clock read
+ * operation executed by the HW latch
*/
struct idpf_ptp {
struct ptp_clock_info info;
struct ptp_clock *clock;
struct idpf_adapter *adapter;
+ struct idpf_ptp_cmd cmd;
+ struct idpf_ptp_dev_clk_regs dev_clk_regs;
+ u32 caps;
+ enum idpf_ptp_access get_dev_clk_time_access:2;
+ u32 rsv:30;
+ spinlock_t read_dev_clk_lock;
};
+/**
+ * idpf_ptp_info_to_adapter - get driver adapter struct from ptp_clock_info
+ * @info: pointer to ptp_clock_info struct
+ *
+ * Return: pointer to the corresponding adapter struct
+ */
+static inline struct idpf_adapter *
+idpf_ptp_info_to_adapter(const struct ptp_clock_info *info)
+{
+ const struct idpf_ptp *ptp = container_of_const(info, struct idpf_ptp,
+ info);
+ return ptp->adapter;
+}
+
#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
int idpf_ptp_init(struct idpf_adapter *adapter);
void idpf_ptp_release(struct idpf_adapter *adapter);
+int idpf_ptp_get_caps(struct idpf_adapter *adapter);
+void idpf_ptp_get_features_access(const struct idpf_adapter *adapter);
#else /* CONFIG_PTP_1588_CLOCK */
static inline int idpf_ptp_init(struct idpf_adapter *adapter)
{
@@ -28,5 +103,14 @@ static inline int idpf_ptp_init(struct idpf_adapter *adapter)
}
static inline void idpf_ptp_release(struct idpf_adapter *adapter) { }
+
+static inline int idpf_ptp_get_caps(struct idpf_adapter *adapter)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void
+idpf_ptp_get_features_access(const struct idpf_adapter *adapter) { }
+
#endif /* CONFIG_PTP_1588_CLOCK */
#endif /* _IDPF_PTP_H */
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
new file mode 100644
index 000000000000..c3c6b920f277
--- /dev/null
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2024 Intel Corporation */
+
+#include "idpf.h"
+#include "idpf_ptp.h"
+#include "idpf_virtchnl.h"
+
+/**
+ * idpf_ptp_get_caps - Send virtchnl get ptp capabilities message
+ * @adapter: Driver specific private structure
+ *
+ * Send virtchnl get PTP capabilities message.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int idpf_ptp_get_caps(struct idpf_adapter *adapter)
+{
+ struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg __free(kfree) = NULL;
+ struct virtchnl2_ptp_get_caps send_ptp_caps_msg = {
+ .caps = cpu_to_le32(VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME |
+ VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME_MB |
+ VIRTCHNL2_CAP_PTP_GET_CROSS_TIME |
+ VIRTCHNL2_CAP_PTP_GET_CROSS_TIME_MB)
+ };
+ struct idpf_vc_xn_params xn_params = {
+ .vc_op = VIRTCHNL2_OP_PTP_GET_CAPS,
+ .send_buf.iov_base = &send_ptp_caps_msg,
+ .send_buf.iov_len = sizeof(send_ptp_caps_msg),
+ .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
+ };
+ struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
+ struct idpf_ptp *ptp = adapter->ptp;
+ enum idpf_ptp_access access_type;
+ u32 temp_offset;
+ int reply_sz;
+
+ recv_ptp_caps_msg = kzalloc(sizeof(struct virtchnl2_ptp_get_caps),
+ GFP_KERNEL);
+ if (!recv_ptp_caps_msg)
+ return -ENOMEM;
+
+ xn_params.recv_buf.iov_base = recv_ptp_caps_msg;
+ xn_params.recv_buf.iov_len = sizeof(*recv_ptp_caps_msg);
+
+ reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
+ if (reply_sz < 0)
+ return reply_sz;
+ else if (reply_sz != sizeof(*recv_ptp_caps_msg))
+ return -EIO;
+
+ ptp->caps = le32_to_cpu(recv_ptp_caps_msg->caps);
+
+ /* Determine the access type for the PTP features */
+ idpf_ptp_get_features_access(adapter);
+
+ access_type = ptp->get_dev_clk_time_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ return 0;
+
+ clock_offsets = recv_ptp_caps_msg->clk_offsets;
+
+ temp_offset = le32_to_cpu(clock_offsets.dev_clk_ns_l);
+ ptp->dev_clk_regs.dev_clk_ns_l = idpf_get_reg_addr(adapter,
+ temp_offset);
+ temp_offset = le32_to_cpu(clock_offsets.dev_clk_ns_h);
+ ptp->dev_clk_regs.dev_clk_ns_h = idpf_get_reg_addr(adapter,
+ temp_offset);
+ temp_offset = le32_to_cpu(clock_offsets.phy_clk_ns_l);
+ ptp->dev_clk_regs.phy_clk_ns_l = idpf_get_reg_addr(adapter,
+ temp_offset);
+ temp_offset = le32_to_cpu(clock_offsets.phy_clk_ns_h);
+ ptp->dev_clk_regs.phy_clk_ns_h = idpf_get_reg_addr(adapter,
+ temp_offset);
+ temp_offset = le32_to_cpu(clock_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = idpf_get_reg_addr(adapter, temp_offset);
+
+ return 0;
+}
--
2.43.5
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v10 iwl-next 06/11] idpf: add mailbox access to read PTP clock time
2025-04-08 10:30 [PATCH v10 iwl-next 00/11] idpf: add initial PTP support Milena Olech
` (4 preceding siblings ...)
2025-04-08 10:30 ` [PATCH v10 iwl-next 05/11] idpf: negotiate PTP capabilities and get PTP clock Milena Olech
@ 2025-04-08 10:30 ` Milena Olech
2025-04-08 21:16 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 21:17 ` Jacob Keller
2025-04-08 10:31 ` [PATCH v10 iwl-next 07/11] idpf: add cross timestamping Milena Olech
` (4 subsequent siblings)
10 siblings, 2 replies; 35+ messages in thread
From: Milena Olech @ 2025-04-08 10:30 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Milena Olech,
Alexander Lobakin, Willem de Bruijn, Samuel Salin
When the access to read PTP clock is specified as mailbox, the driver
needs to send virtchnl message to perform PTP actions. Message is sent
using idpf_mbq_opc_send_msg_to_peer_drv mailbox opcode, with the parameters
received during PTP capabilities negotiation.
Add functions to recognize PTP messages, move them to dedicated secondary
mailbox, read the PTP clock time and cross timestamp using mailbox
messages.
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
---
v9 -> v10: remove cross timestamping
.../ethernet/intel/idpf/idpf_controlq_api.h | 3 ++
drivers/net/ethernet/intel/idpf/idpf_ptp.c | 33 +++++++++++++
drivers/net/ethernet/intel/idpf/idpf_ptp.h | 34 ++++++++++++++
.../net/ethernet/intel/idpf/idpf_virtchnl.c | 47 +++++++++++++++++++
.../ethernet/intel/idpf/idpf_virtchnl_ptp.c | 47 +++++++++++++++++++
5 files changed, 164 insertions(+)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_controlq_api.h b/drivers/net/ethernet/intel/idpf/idpf_controlq_api.h
index e8e046ef2f0d..9642494a67d8 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_controlq_api.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_controlq_api.h
@@ -123,9 +123,12 @@ struct idpf_ctlq_info {
/**
* enum idpf_mbx_opc - PF/VF mailbox commands
* @idpf_mbq_opc_send_msg_to_cp: used by PF or VF to send a message to its CP
+ * @idpf_mbq_opc_send_msg_to_peer_drv: used by PF or VF to send a message to
+ * any peer driver
*/
enum idpf_mbx_opc {
idpf_mbq_opc_send_msg_to_cp = 0x0801,
+ idpf_mbq_opc_send_msg_to_peer_drv = 0x0804,
};
/* API supported for control queue management */
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
index cb27a8ee3acc..5ea9d672a159 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
@@ -92,6 +92,37 @@ static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter,
return ((u64)hi << 32) | lo;
}
+/**
+ * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value through mailbox
+ * @adapter: Driver specific private structure
+ * @sts: Optional parameter for holding a pair of system timestamps from
+ * the system clock. Will be ignored when NULL is given.
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int idpf_ptp_read_src_clk_reg_mailbox(struct idpf_adapter *adapter,
+ struct ptp_system_timestamp *sts,
+ u64 *src_clk)
+{
+ struct idpf_ptp_dev_timers clk_time;
+ int err;
+
+ /* Read the system timestamp pre PHC read */
+ ptp_read_system_prets(sts);
+
+ err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
+ if (err)
+ return err;
+
+ /* Read the system timestamp post PHC read */
+ ptp_read_system_postts(sts);
+
+ *src_clk = clk_time.dev_clk_time_ns;
+
+ return 0;
+}
+
/**
* idpf_ptp_read_src_clk_reg - Read the main timer value
* @adapter: Driver specific private structure
@@ -107,6 +138,8 @@ static int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk,
switch (adapter->ptp->get_dev_clk_time_access) {
case IDPF_PTP_NONE:
return -EOPNOTSUPP;
+ case IDPF_PTP_MAILBOX:
+ return idpf_ptp_read_src_clk_reg_mailbox(adapter, sts, src_clk);
case IDPF_PTP_DIRECT:
*src_clk = idpf_ptp_read_src_clk_reg_direct(adapter, sts);
break;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.h b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
index 955d697e51ab..b536c1cdbb2e 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
@@ -52,6 +52,19 @@ enum idpf_ptp_access {
IDPF_PTP_MAILBOX,
};
+/**
+ * struct idpf_ptp_secondary_mbx - PTP secondary mailbox
+ * @peer_mbx_q_id: PTP mailbox queue ID
+ * @peer_id: Peer ID for PTP Device Control daemon
+ * @valid: indicates whether secondary mailblox is supported by the Control
+ * Plane
+ */
+struct idpf_ptp_secondary_mbx {
+ u16 peer_mbx_q_id;
+ u16 peer_id;
+ bool valid:1;
+};
+
/**
* struct idpf_ptp - PTP parameters
* @info: structure defining PTP hardware capabilities
@@ -62,6 +75,7 @@ enum idpf_ptp_access {
* @caps: PTP capabilities negotiated with the Control Plane
* @get_dev_clk_time_access: access type for getting the device clock time
* @rsv: reserved bits
+ * @secondary_mbx: parameters for using dedicated PTP mailbox
* @read_dev_clk_lock: spinlock protecting access to the device clock read
* operation executed by the HW latch
*/
@@ -74,6 +88,7 @@ struct idpf_ptp {
u32 caps;
enum idpf_ptp_access get_dev_clk_time_access:2;
u32 rsv:30;
+ struct idpf_ptp_secondary_mbx secondary_mbx;
spinlock_t read_dev_clk_lock;
};
@@ -91,11 +106,23 @@ idpf_ptp_info_to_adapter(const struct ptp_clock_info *info)
return ptp->adapter;
}
+/**
+ * struct idpf_ptp_dev_timers - System time and device time values
+ * @sys_time_ns: system time value expressed in nanoseconds
+ * @dev_clk_time_ns: device clock time value expressed in nanoseconds
+ */
+struct idpf_ptp_dev_timers {
+ u64 sys_time_ns;
+ u64 dev_clk_time_ns;
+};
+
#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
int idpf_ptp_init(struct idpf_adapter *adapter);
void idpf_ptp_release(struct idpf_adapter *adapter);
int idpf_ptp_get_caps(struct idpf_adapter *adapter);
void idpf_ptp_get_features_access(const struct idpf_adapter *adapter);
+int idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
+ struct idpf_ptp_dev_timers *dev_clk_time);
#else /* CONFIG_PTP_1588_CLOCK */
static inline int idpf_ptp_init(struct idpf_adapter *adapter)
{
@@ -112,5 +139,12 @@ static inline int idpf_ptp_get_caps(struct idpf_adapter *adapter)
static inline void
idpf_ptp_get_features_access(const struct idpf_adapter *adapter) { }
+static inline int
+idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
+ struct idpf_ptp_dev_timers *dev_clk_time)
+{
+ return -EOPNOTSUPP;
+}
+
#endif /* CONFIG_PTP_1588_CLOCK */
#endif /* _IDPF_PTP_H */
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index a55ff20895ed..139d19353e28 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -154,6 +154,50 @@ static int idpf_mb_clean(struct idpf_adapter *adapter)
return err;
}
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
+/**
+ * idpf_ptp_is_mb_msg - Check if the message is PTP-related
+ * @op: virtchnl opcode
+ *
+ * Return: true if msg is PTP-related, false otherwise.
+ */
+static bool idpf_ptp_is_mb_msg(u32 op)
+{
+ switch (op) {
+ case VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME:
+ case VIRTCHNL2_OP_PTP_GET_CROSS_TIME:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * idpf_prepare_ptp_mb_msg - Prepare PTP related message
+ *
+ * @adapter: Driver specific private structure
+ * @op: virtchnl opcode
+ * @ctlq_msg: Corresponding control queue message
+ */
+static void idpf_prepare_ptp_mb_msg(struct idpf_adapter *adapter, u32 op,
+ struct idpf_ctlq_msg *ctlq_msg)
+{
+ /* If the message is PTP-related and the secondary mailbox is available,
+ * send the message through the secondary mailbox.
+ */
+ if (!idpf_ptp_is_mb_msg(op) || !adapter->ptp->secondary_mbx.valid)
+ return;
+
+ ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_peer_drv;
+ ctlq_msg->func_id = adapter->ptp->secondary_mbx.peer_mbx_q_id;
+ ctlq_msg->host_id = adapter->ptp->secondary_mbx.peer_id;
+}
+#else /* !CONFIG_PTP_1588_CLOCK */
+static void idpf_prepare_ptp_mb_msg(struct idpf_adapter *adapter, u32 op,
+ struct idpf_ctlq_msg *ctlq_msg)
+{ }
+#endif /* CONFIG_PTP_1588_CLOCK */
+
/**
* idpf_send_mb_msg - Send message over mailbox
* @adapter: Driver specific private structure
@@ -197,6 +241,9 @@ int idpf_send_mb_msg(struct idpf_adapter *adapter, u32 op,
ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_cp;
ctlq_msg->func_id = 0;
+
+ idpf_prepare_ptp_mb_msg(adapter, op, ctlq_msg);
+
ctlq_msg->data_len = msg_size;
ctlq_msg->cookie.mbx.chnl_opcode = op;
ctlq_msg->cookie.mbx.chnl_retval = 0;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
index c3c6b920f277..79bd2585703b 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
@@ -29,6 +29,7 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
+ struct idpf_ptp_secondary_mbx *scnd_mbx;
struct idpf_ptp *ptp = adapter->ptp;
enum idpf_ptp_access access_type;
u32 temp_offset;
@@ -50,6 +51,16 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
ptp->caps = le32_to_cpu(recv_ptp_caps_msg->caps);
+ scnd_mbx = &ptp->secondary_mbx;
+ scnd_mbx->peer_mbx_q_id = le16_to_cpu(recv_ptp_caps_msg->peer_mbx_q_id);
+
+ /* if the ptp_mb_q_id holds invalid value (0xffff), the secondary
+ * mailbox is not supported.
+ */
+ scnd_mbx->valid = scnd_mbx->peer_mbx_q_id != 0xffff;
+ if (scnd_mbx->valid)
+ scnd_mbx->peer_id = recv_ptp_caps_msg->peer_id;
+
/* Determine the access type for the PTP features */
idpf_ptp_get_features_access(adapter);
@@ -76,3 +87,39 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
return 0;
}
+
+/**
+ * idpf_ptp_get_dev_clk_time - Send virtchnl get device clk time message
+ * @adapter: Driver specific private structure
+ * @dev_clk_time: Pointer to the device clock structure where the value is set
+ *
+ * Send virtchnl get time message to get the time of the clock.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
+ struct idpf_ptp_dev_timers *dev_clk_time)
+{
+ struct virtchnl2_ptp_get_dev_clk_time get_dev_clk_time_msg;
+ struct idpf_vc_xn_params xn_params = {
+ .vc_op = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME,
+ .send_buf.iov_base = &get_dev_clk_time_msg,
+ .send_buf.iov_len = sizeof(get_dev_clk_time_msg),
+ .recv_buf.iov_base = &get_dev_clk_time_msg,
+ .recv_buf.iov_len = sizeof(get_dev_clk_time_msg),
+ .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
+ };
+ int reply_sz;
+ u64 dev_time;
+
+ reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
+ if (reply_sz < 0)
+ return reply_sz;
+ if (reply_sz != sizeof(get_dev_clk_time_msg))
+ return -EIO;
+
+ dev_time = le64_to_cpu(get_dev_clk_time_msg.dev_time_ns);
+ dev_clk_time->dev_clk_time_ns = dev_time;
+
+ return 0;
+}
--
2.43.5
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v10 iwl-next 07/11] idpf: add cross timestamping
2025-04-08 10:30 [PATCH v10 iwl-next 00/11] idpf: add initial PTP support Milena Olech
` (5 preceding siblings ...)
2025-04-08 10:30 ` [PATCH v10 iwl-next 06/11] idpf: add mailbox access to read PTP clock time Milena Olech
@ 2025-04-08 10:31 ` Milena Olech
2025-04-08 21:18 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:31 ` [PATCH v10 iwl-next 08/11] idpf: add PTP clock configuration Milena Olech
` (3 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Milena Olech @ 2025-04-08 10:31 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Milena Olech
Add cross timestamp support through virtchnl mailbox messages and directly,
through PCIe BAR registers. Cross timestamping assumes that both system
time and device clock time values are cached simultaneously, what is
triggered by HW. Feature is enabled for both ARM and x86 archs.
Signed-off-by: Milena Olech <milena.olech@intel.com>
---
v9 -> v10: add a separate patch for cross timestamping
drivers/net/ethernet/intel/idpf/idpf_ptp.c | 139 ++++++++++++++++++
drivers/net/ethernet/intel/idpf/idpf_ptp.h | 18 ++-
.../ethernet/intel/idpf/idpf_virtchnl_ptp.c | 55 ++++++-
3 files changed, 210 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
index 5ea9d672a159..c00f0a03ccab 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
@@ -41,6 +41,13 @@ void idpf_ptp_get_features_access(const struct idpf_adapter *adapter)
ptp->get_dev_clk_time_access = idpf_ptp_get_access(adapter,
direct,
mailbox);
+
+ /* Get the cross timestamp */
+ direct = VIRTCHNL2_CAP_PTP_GET_CROSS_TIME;
+ mailbox = VIRTCHNL2_CAP_PTP_GET_CROSS_TIME_MB;
+ ptp->get_cross_tstamp_access = idpf_ptp_get_access(adapter,
+ direct,
+ mailbox);
}
/**
@@ -150,6 +157,129 @@ static int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk,
return 0;
}
+#if IS_ENABLED(CONFIG_ARM_ARCH_TIMER) || IS_ENABLED(CONFIG_X86)
+/**
+ * idpf_ptp_get_sync_device_time_direct - Get the cross time stamp values
+ * directly
+ * @adapter: Driver specific private structure
+ * @dev_time: 64bit main timer value
+ * @sys_time: 64bit system time value
+ */
+static void idpf_ptp_get_sync_device_time_direct(struct idpf_adapter *adapter,
+ u64 *dev_time, u64 *sys_time)
+{
+ u32 dev_time_lo, dev_time_hi, sys_time_lo, sys_time_hi;
+ struct idpf_ptp *ptp = adapter->ptp;
+
+ spin_lock(&ptp->read_dev_clk_lock);
+
+ idpf_ptp_enable_shtime(adapter);
+
+ dev_time_lo = readl(ptp->dev_clk_regs.dev_clk_ns_l);
+ dev_time_hi = readl(ptp->dev_clk_regs.dev_clk_ns_h);
+
+ sys_time_lo = readl(ptp->dev_clk_regs.sys_time_ns_l);
+ sys_time_hi = readl(ptp->dev_clk_regs.sys_time_ns_h);
+
+ *dev_time = ((u64)dev_time_hi << 32) | dev_time_lo;
+ *sys_time = ((u64)sys_time_hi << 32) | sys_time_lo;
+
+ spin_unlock(&ptp->read_dev_clk_lock);
+}
+
+/**
+ * idpf_ptp_get_sync_device_time_mailbox - Get the cross time stamp values
+ * through mailbox
+ * @adapter: Driver specific private structure
+ * @dev_time: 64bit main timer value expressed in nanoseconds
+ * @sys_time: 64bit system time value expressed in nanoseconds
+ *
+ * Return: a pair of cross timestamp values on success, -errno otherwise.
+ */
+static int idpf_ptp_get_sync_device_time_mailbox(struct idpf_adapter *adapter,
+ u64 *dev_time, u64 *sys_time)
+{
+ struct idpf_ptp_dev_timers cross_time;
+ int err;
+
+ err = idpf_ptp_get_cross_time(adapter, &cross_time);
+ if (err)
+ return err;
+
+ *dev_time = cross_time.dev_clk_time_ns;
+ *sys_time = cross_time.sys_time_ns;
+
+ return err;
+}
+
+/**
+ * idpf_ptp_get_sync_device_time - Get the cross time stamp info
+ * @device: Current device time
+ * @system: System counter value read synchronously with device time
+ * @ctx: Context provided by timekeeping code
+ *
+ * Return: the device and the system clocks time read simultaneously on success,
+ * -errno otherwise.
+ */
+static int idpf_ptp_get_sync_device_time(ktime_t *device,
+ struct system_counterval_t *system,
+ void *ctx)
+{
+ struct idpf_adapter *adapter = ctx;
+ u64 ns_time_dev, ns_time_sys;
+ int err;
+
+ switch (adapter->ptp->get_cross_tstamp_access) {
+ case IDPF_PTP_NONE:
+ return -EOPNOTSUPP;
+ case IDPF_PTP_DIRECT:
+ idpf_ptp_get_sync_device_time_direct(adapter, &ns_time_dev,
+ &ns_time_sys);
+ break;
+ case IDPF_PTP_MAILBOX:
+ err = idpf_ptp_get_sync_device_time_mailbox(adapter,
+ &ns_time_dev,
+ &ns_time_sys);
+ if (err)
+ return err;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ *device = ns_to_ktime(ns_time_dev);
+
+#if IS_ENABLED(CONFIG_X86)
+ system->cs_id = CSID_X86_ART;
+#else
+ system->cs_id = CSID_ARM_ARCH_COUNTER;
+#endif /* CONFIG_X86 */
+ system->cycles = ns_time_sys;
+ system->use_nsecs = true;
+
+ return 0;
+}
+
+/**
+ * idpf_ptp_get_crosststamp - Capture a device cross timestamp
+ * @info: the driver's PTP info structure
+ * @cts: The memory to fill the cross timestamp info
+ *
+ * Capture a cross timestamp between the system time and the device PTP hardware
+ * clock.
+ *
+ * Return: cross timestamp value on success, -errno on failure.
+ */
+static int idpf_ptp_get_crosststamp(struct ptp_clock_info *info,
+ struct system_device_crosststamp *cts)
+{
+ struct idpf_adapter *adapter = idpf_ptp_info_to_adapter(info);
+
+ return get_device_system_crosststamp(idpf_ptp_get_sync_device_time,
+ adapter, NULL, cts);
+}
+#endif /* CONFIG_ARM_ARCH_TIMER || CONFIG_X86 */
+
/**
* idpf_ptp_gettimex64 - Get the time of the clock
* @info: the driver's PTP info structure
@@ -192,6 +322,15 @@ static void idpf_ptp_set_caps(const struct idpf_adapter *adapter)
info->owner = THIS_MODULE;
info->gettimex64 = idpf_ptp_gettimex64;
+
+#if IS_ENABLED(CONFIG_ARM_ARCH_TIMER)
+ info->getcrosststamp = idpf_ptp_get_crosststamp;
+#elif IS_ENABLED(CONFIG_X86)
+ if (pcie_ptm_enabled(adapter->pdev) &&
+ boot_cpu_has(X86_FEATURE_ART) &&
+ boot_cpu_has(X86_FEATURE_TSC_KNOWN_FREQ))
+ info->getcrosststamp = idpf_ptp_get_crosststamp;
+#endif /* CONFIG_ARM_ARCH_TIMER */
}
/**
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.h b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
index b536c1cdbb2e..b605e779f922 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
@@ -21,6 +21,8 @@ struct idpf_ptp_cmd {
* @dev_clk_ns_h: high part of the device clock register
* @phy_clk_ns_l: low part of the PHY clock register
* @phy_clk_ns_h: high part of the PHY clock register
+ * @sys_time_ns_l: low part of the system time register
+ * @sys_time_ns_h: high part of the system time register
* @cmd: PTP command register
* @phy_cmd: PHY command register
* @cmd_sync: PTP command synchronization register
@@ -34,6 +36,10 @@ struct idpf_ptp_dev_clk_regs {
void __iomem *phy_clk_ns_l;
void __iomem *phy_clk_ns_h;
+ /* System time */
+ void __iomem *sys_time_ns_l;
+ void __iomem *sys_time_ns_h;
+
/* Command */
void __iomem *cmd;
void __iomem *phy_cmd;
@@ -74,6 +80,7 @@ struct idpf_ptp_secondary_mbx {
* @dev_clk_regs: the set of registers to access the device clock
* @caps: PTP capabilities negotiated with the Control Plane
* @get_dev_clk_time_access: access type for getting the device clock time
+ * @get_cross_tstamp_access: access type for the cross timestamping
* @rsv: reserved bits
* @secondary_mbx: parameters for using dedicated PTP mailbox
* @read_dev_clk_lock: spinlock protecting access to the device clock read
@@ -87,7 +94,8 @@ struct idpf_ptp {
struct idpf_ptp_dev_clk_regs dev_clk_regs;
u32 caps;
enum idpf_ptp_access get_dev_clk_time_access:2;
- u32 rsv:30;
+ enum idpf_ptp_access get_cross_tstamp_access:2;
+ u32 rsv:28;
struct idpf_ptp_secondary_mbx secondary_mbx;
spinlock_t read_dev_clk_lock;
};
@@ -123,6 +131,8 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter);
void idpf_ptp_get_features_access(const struct idpf_adapter *adapter);
int idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
struct idpf_ptp_dev_timers *dev_clk_time);
+int idpf_ptp_get_cross_time(struct idpf_adapter *adapter,
+ struct idpf_ptp_dev_timers *cross_time);
#else /* CONFIG_PTP_1588_CLOCK */
static inline int idpf_ptp_init(struct idpf_adapter *adapter)
{
@@ -146,5 +156,11 @@ idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
return -EOPNOTSUPP;
}
+static inline int
+idpf_ptp_get_cross_time(struct idpf_adapter *adapter,
+ struct idpf_ptp_dev_timers *cross_time)
+{
+ return -EOPNOTSUPP;
+}
#endif /* CONFIG_PTP_1588_CLOCK */
#endif /* _IDPF_PTP_H */
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
index 79bd2585703b..e51fa16d13cd 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
@@ -28,6 +28,7 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
.send_buf.iov_len = sizeof(send_ptp_caps_msg),
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
+ struct virtchnl2_ptp_cross_time_reg_offsets cross_tstamp_offsets;
struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
struct idpf_ptp_secondary_mbx *scnd_mbx;
struct idpf_ptp *ptp = adapter->ptp;
@@ -66,7 +67,7 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
access_type = ptp->get_dev_clk_time_access;
if (access_type != IDPF_PTP_DIRECT)
- return 0;
+ goto cross_tstamp;
clock_offsets = recv_ptp_caps_msg->clk_offsets;
@@ -85,6 +86,22 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
temp_offset = le32_to_cpu(clock_offsets.cmd_sync_trigger);
ptp->dev_clk_regs.cmd_sync = idpf_get_reg_addr(adapter, temp_offset);
+cross_tstamp:
+ access_type = ptp->get_cross_tstamp_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ return 0;
+
+ cross_tstamp_offsets = recv_ptp_caps_msg->cross_time_offsets;
+
+ temp_offset = le32_to_cpu(cross_tstamp_offsets.sys_time_ns_l);
+ ptp->dev_clk_regs.sys_time_ns_l = idpf_get_reg_addr(adapter,
+ temp_offset);
+ temp_offset = le32_to_cpu(cross_tstamp_offsets.sys_time_ns_h);
+ ptp->dev_clk_regs.sys_time_ns_h = idpf_get_reg_addr(adapter,
+ temp_offset);
+ temp_offset = le32_to_cpu(cross_tstamp_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = idpf_get_reg_addr(adapter, temp_offset);
+
return 0;
}
@@ -123,3 +140,39 @@ int idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
return 0;
}
+
+/**
+ * idpf_ptp_get_cross_time - Send virtchnl get cross time message
+ * @adapter: Driver specific private structure
+ * @cross_time: Pointer to the device clock structure where the value is set
+ *
+ * Send virtchnl get cross time message to get the time of the clock and the
+ * system time.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int idpf_ptp_get_cross_time(struct idpf_adapter *adapter,
+ struct idpf_ptp_dev_timers *cross_time)
+{
+ struct virtchnl2_ptp_get_cross_time cross_time_msg;
+ struct idpf_vc_xn_params xn_params = {
+ .vc_op = VIRTCHNL2_OP_PTP_GET_CROSS_TIME,
+ .send_buf.iov_base = &cross_time_msg,
+ .send_buf.iov_len = sizeof(cross_time_msg),
+ .recv_buf.iov_base = &cross_time_msg,
+ .recv_buf.iov_len = sizeof(cross_time_msg),
+ .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
+ };
+ int reply_sz;
+
+ reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
+ if (reply_sz < 0)
+ return reply_sz;
+ if (reply_sz != sizeof(cross_time_msg))
+ return -EIO;
+
+ cross_time->dev_clk_time_ns = le64_to_cpu(cross_time_msg.dev_time_ns);
+ cross_time->sys_time_ns = le64_to_cpu(cross_time_msg.sys_time_ns);
+
+ return 0;
+}
--
2.43.5
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v10 iwl-next 08/11] idpf: add PTP clock configuration
2025-04-08 10:30 [PATCH v10 iwl-next 00/11] idpf: add initial PTP support Milena Olech
` (6 preceding siblings ...)
2025-04-08 10:31 ` [PATCH v10 iwl-next 07/11] idpf: add cross timestamping Milena Olech
@ 2025-04-08 10:31 ` Milena Olech
2025-04-08 21:20 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:31 ` [PATCH v10 iwl-next 09/11] idpf: add Tx timestamp capabilities negotiation Milena Olech
` (2 subsequent siblings)
10 siblings, 1 reply; 35+ messages in thread
From: Milena Olech @ 2025-04-08 10:31 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Milena Olech,
Alexander Lobakin, Willem de Bruijn, Mina Almasry, Samuel Salin
PTP clock configuration operations - set time, adjust time and adjust
frequency are required to control the clock and maintain synchronization
process.
Extend get PTP capabilities function to request for the clock adjustments
and add functions to enable these actions using dedicated virtchnl
messages.
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Tested-by: Mina Almasry <almasrymina@google.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
---
v8 -> v9: minor - remove unnecessary dot in the function description
drivers/net/ethernet/intel/idpf/idpf_ptp.c | 191 ++++++++++++++++++
drivers/net/ethernet/intel/idpf/idpf_ptp.h | 52 ++++-
.../net/ethernet/intel/idpf/idpf_virtchnl.c | 3 +
.../ethernet/intel/idpf/idpf_virtchnl_ptp.c | 143 ++++++++++++-
4 files changed, 386 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
index c00f0a03ccab..ea6ebd1391e1 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
@@ -48,6 +48,20 @@ void idpf_ptp_get_features_access(const struct idpf_adapter *adapter)
ptp->get_cross_tstamp_access = idpf_ptp_get_access(adapter,
direct,
mailbox);
+
+ /* Set the device clock time */
+ direct = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME;
+ mailbox = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME;
+ ptp->set_dev_clk_time_access = idpf_ptp_get_access(adapter,
+ direct,
+ mailbox);
+
+ /* Adjust the device clock time */
+ direct = VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK;
+ mailbox = VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB;
+ ptp->adj_dev_clk_time_access = idpf_ptp_get_access(adapter,
+ direct,
+ mailbox);
}
/**
@@ -307,6 +321,154 @@ static int idpf_ptp_gettimex64(struct ptp_clock_info *info,
return 0;
}
+/**
+ * idpf_ptp_settime64 - Set the time of the clock
+ * @info: the driver's PTP info structure
+ * @ts: timespec64 structure that holds the new time value
+ *
+ * Set the device clock to the user input value. The conversion from timespec
+ * to ns happens in the write function.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int idpf_ptp_settime64(struct ptp_clock_info *info,
+ const struct timespec64 *ts)
+{
+ struct idpf_adapter *adapter = idpf_ptp_info_to_adapter(info);
+ enum idpf_ptp_access access;
+ int err;
+ u64 ns;
+
+ access = adapter->ptp->set_dev_clk_time_access;
+ if (access != IDPF_PTP_MAILBOX)
+ return -EOPNOTSUPP;
+
+ ns = timespec64_to_ns(ts);
+
+ err = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (err) {
+ pci_err(adapter->pdev, "Failed to set the time, err: %pe\n", ERR_PTR(err));
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * idpf_ptp_adjtime_nonatomic - Do a non-atomic clock adjustment
+ * @info: the driver's PTP info structure
+ * @delta: Offset in nanoseconds to adjust the time by
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int idpf_ptp_adjtime_nonatomic(struct ptp_clock_info *info, s64 delta)
+{
+ struct timespec64 now, then;
+ int err;
+
+ err = idpf_ptp_gettimex64(info, &now, NULL);
+ if (err)
+ return err;
+
+ then = ns_to_timespec64(delta);
+ now = timespec64_add(now, then);
+
+ return idpf_ptp_settime64(info, &now);
+}
+
+/**
+ * idpf_ptp_adjtime - Adjust the time of the clock by the indicated delta
+ * @info: the driver's PTP info structure
+ * @delta: Offset in nanoseconds to adjust the time by
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int idpf_ptp_adjtime(struct ptp_clock_info *info, s64 delta)
+{
+ struct idpf_adapter *adapter = idpf_ptp_info_to_adapter(info);
+ enum idpf_ptp_access access;
+ int err;
+
+ access = adapter->ptp->adj_dev_clk_time_access;
+ if (access != IDPF_PTP_MAILBOX)
+ return -EOPNOTSUPP;
+
+ /* Hardware only supports atomic adjustments using signed 32-bit
+ * integers. For any adjustment outside this range, perform
+ * a non-atomic get->adjust->set flow.
+ */
+ if (delta > S32_MAX || delta < S32_MIN)
+ return idpf_ptp_adjtime_nonatomic(info, delta);
+
+ err = idpf_ptp_adj_dev_clk_time(adapter, delta);
+ if (err) {
+ pci_err(adapter->pdev, "Failed to adjust the clock with delta %lld err: %pe\n", delta, ERR_PTR(err));
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * idpf_ptp_adjfine - Adjust clock increment rate
+ * @info: the driver's PTP info structure
+ * @scaled_ppm: Parts per million with 16-bit fractional field
+ *
+ * Adjust the frequency of the clock by the indicated scaled ppm from the
+ * base frequency.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int idpf_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm)
+{
+ struct idpf_adapter *adapter = idpf_ptp_info_to_adapter(info);
+ enum idpf_ptp_access access;
+ u64 incval, diff;
+ int err;
+
+ access = adapter->ptp->adj_dev_clk_time_access;
+ if (access != IDPF_PTP_MAILBOX)
+ return -EOPNOTSUPP;
+
+ incval = adapter->ptp->base_incval;
+
+ diff = adjust_by_scaled_ppm(incval, scaled_ppm);
+ err = idpf_ptp_adj_dev_clk_fine(adapter, diff);
+ if (err)
+ pci_err(adapter->pdev, "Failed to adjust clock increment rate for scaled ppm %ld %pe\n", scaled_ppm, ERR_PTR(err));
+
+ return 0;
+}
+
+/**
+ * idpf_ptp_verify_pin - Verify if pin supports requested pin function
+ * @info: the driver's PTP info structure
+ * @pin: Pin index
+ * @func: Assigned function
+ * @chan: Assigned channel
+ *
+ * Return: EOPNOTSUPP as not supported yet.
+ */
+static int idpf_ptp_verify_pin(struct ptp_clock_info *info, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan)
+{
+ return -EOPNOTSUPP;
+}
+
+/**
+ * idpf_ptp_gpio_enable - Enable/disable ancillary features of PHC
+ * @info: the driver's PTP info structure
+ * @rq: The requested feature to change
+ * @on: Enable/disable flag
+ *
+ * Return: EOPNOTSUPP as not supported yet.
+ */
+static int idpf_ptp_gpio_enable(struct ptp_clock_info *info,
+ struct ptp_clock_request *rq, int on)
+{
+ return -EOPNOTSUPP;
+}
+
/**
* idpf_ptp_set_caps - Set PTP capabilities
* @adapter: Driver specific private structure
@@ -321,7 +483,13 @@ static void idpf_ptp_set_caps(const struct idpf_adapter *adapter)
KBUILD_MODNAME, pci_name(adapter->pdev));
info->owner = THIS_MODULE;
+ info->max_adj = adapter->ptp->max_adj;
info->gettimex64 = idpf_ptp_gettimex64;
+ info->settime64 = idpf_ptp_settime64;
+ info->adjfine = idpf_ptp_adjfine;
+ info->adjtime = idpf_ptp_adjtime;
+ info->verify = idpf_ptp_verify_pin;
+ info->enable = idpf_ptp_gpio_enable;
#if IS_ENABLED(CONFIG_ARM_ARCH_TIMER)
info->getcrosststamp = idpf_ptp_get_crosststamp;
@@ -372,6 +540,7 @@ static int idpf_ptp_create_clock(const struct idpf_adapter *adapter)
*/
int idpf_ptp_init(struct idpf_adapter *adapter)
{
+ struct timespec64 ts;
int err;
if (!idpf_is_cap_ena(adapter, IDPF_OTHER_CAPS, VIRTCHNL2_CAP_PTP)) {
@@ -399,12 +568,34 @@ int idpf_ptp_init(struct idpf_adapter *adapter)
if (err)
goto free_ptp;
+ /* Write the default increment time value if the clock adjustments
+ * are enabled.
+ */
+ if (adapter->ptp->adj_dev_clk_time_access != IDPF_PTP_NONE) {
+ err = idpf_ptp_adj_dev_clk_fine(adapter,
+ adapter->ptp->base_incval);
+ if (err)
+ goto remove_clock;
+ }
+
+ /* Write the initial time value if the set time operation is enabled */
+ if (adapter->ptp->set_dev_clk_time_access != IDPF_PTP_NONE) {
+ ts = ktime_to_timespec64(ktime_get_real());
+ err = idpf_ptp_settime64(&adapter->ptp->info, &ts);
+ if (err)
+ goto remove_clock;
+ }
+
spin_lock_init(&adapter->ptp->read_dev_clk_lock);
pci_dbg(adapter->pdev, "PTP init successful\n");
return 0;
+remove_clock:
+ ptp_clock_unregister(adapter->ptp->clock);
+ adapter->ptp->clock = NULL;
+
free_ptp:
kfree(adapter->ptp);
adapter->ptp = NULL;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.h b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
index b605e779f922..8022ed31defd 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
@@ -23,6 +23,14 @@ struct idpf_ptp_cmd {
* @phy_clk_ns_h: high part of the PHY clock register
* @sys_time_ns_l: low part of the system time register
* @sys_time_ns_h: high part of the system time register
+ * @incval_l: low part of the increment value register
+ * @incval_h: high part of the increment value register
+ * @shadj_l: low part of the shadow adjust register
+ * @shadj_h: high part of the shadow adjust register
+ * phy_incval_l: low part of the PHY increment value register
+ * phy_incval_h: high part of the PHY increment value register
+ * phy_shadj_l: low part of the PHY shadow adjust register
+ * phy_shadj_h: high part of the PHY shadow adjust register
* @cmd: PTP command register
* @phy_cmd: PHY command register
* @cmd_sync: PTP command synchronization register
@@ -40,6 +48,18 @@ struct idpf_ptp_dev_clk_regs {
void __iomem *sys_time_ns_l;
void __iomem *sys_time_ns_h;
+ /* Main timer adjustments */
+ void __iomem *incval_l;
+ void __iomem *incval_h;
+ void __iomem *shadj_l;
+ void __iomem *shadj_h;
+
+ /* PHY timer adjustments */
+ void __iomem *phy_incval_l;
+ void __iomem *phy_incval_h;
+ void __iomem *phy_shadj_l;
+ void __iomem *phy_shadj_h;
+
/* Command */
void __iomem *cmd;
void __iomem *phy_cmd;
@@ -76,11 +96,15 @@ struct idpf_ptp_secondary_mbx {
* @info: structure defining PTP hardware capabilities
* @clock: pointer to registered PTP clock device
* @adapter: back pointer to the adapter
+ * @base_incval: base increment value of the PTP clock
+ * @max_adj: maximum adjustment of the PTP clock
* @cmd: HW specific command masks
* @dev_clk_regs: the set of registers to access the device clock
* @caps: PTP capabilities negotiated with the Control Plane
* @get_dev_clk_time_access: access type for getting the device clock time
* @get_cross_tstamp_access: access type for the cross timestamping
+ * @set_dev_clk_time_access: access type for setting the device clock time
+ * @adj_dev_clk_time_access: access type for the adjusting the device clock
* @rsv: reserved bits
* @secondary_mbx: parameters for using dedicated PTP mailbox
* @read_dev_clk_lock: spinlock protecting access to the device clock read
@@ -90,12 +114,16 @@ struct idpf_ptp {
struct ptp_clock_info info;
struct ptp_clock *clock;
struct idpf_adapter *adapter;
+ u64 base_incval;
+ u64 max_adj;
struct idpf_ptp_cmd cmd;
struct idpf_ptp_dev_clk_regs dev_clk_regs;
u32 caps;
enum idpf_ptp_access get_dev_clk_time_access:2;
enum idpf_ptp_access get_cross_tstamp_access:2;
- u32 rsv:28;
+ enum idpf_ptp_access set_dev_clk_time_access:2;
+ enum idpf_ptp_access adj_dev_clk_time_access:2;
+ u8 rsv;
struct idpf_ptp_secondary_mbx secondary_mbx;
spinlock_t read_dev_clk_lock;
};
@@ -133,6 +161,9 @@ int idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
struct idpf_ptp_dev_timers *dev_clk_time);
int idpf_ptp_get_cross_time(struct idpf_adapter *adapter,
struct idpf_ptp_dev_timers *cross_time);
+int idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, u64 time);
+int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, u64 incval);
+int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, s64 delta);
#else /* CONFIG_PTP_1588_CLOCK */
static inline int idpf_ptp_init(struct idpf_adapter *adapter)
{
@@ -162,5 +193,24 @@ idpf_ptp_get_cross_time(struct idpf_adapter *adapter,
{
return -EOPNOTSUPP;
}
+
+static inline int idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter,
+ u64 time)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter,
+ u64 incval)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter,
+ s64 delta)
+{
+ return -EOPNOTSUPP;
+}
+
#endif /* CONFIG_PTP_1588_CLOCK */
#endif /* _IDPF_PTP_H */
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index 139d19353e28..b7c9c5d84350 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -166,6 +166,9 @@ static bool idpf_ptp_is_mb_msg(u32 op)
switch (op) {
case VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME:
case VIRTCHNL2_OP_PTP_GET_CROSS_TIME:
+ case VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME:
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE:
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME:
return true;
default:
return false;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
index e51fa16d13cd..b8185778cd07 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
@@ -20,7 +20,9 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
.caps = cpu_to_le32(VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME |
VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME_MB |
VIRTCHNL2_CAP_PTP_GET_CROSS_TIME |
- VIRTCHNL2_CAP_PTP_GET_CROSS_TIME_MB)
+ VIRTCHNL2_CAP_PTP_GET_CROSS_TIME_MB |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB)
};
struct idpf_vc_xn_params xn_params = {
.vc_op = VIRTCHNL2_OP_PTP_GET_CAPS,
@@ -29,6 +31,7 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
};
struct virtchnl2_ptp_cross_time_reg_offsets cross_tstamp_offsets;
+ struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
struct idpf_ptp_secondary_mbx *scnd_mbx;
struct idpf_ptp *ptp = adapter->ptp;
@@ -51,6 +54,8 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
return -EIO;
ptp->caps = le32_to_cpu(recv_ptp_caps_msg->caps);
+ ptp->base_incval = le64_to_cpu(recv_ptp_caps_msg->base_incval);
+ ptp->max_adj = le32_to_cpu(recv_ptp_caps_msg->max_adj);
scnd_mbx = &ptp->secondary_mbx;
scnd_mbx->peer_mbx_q_id = le16_to_cpu(recv_ptp_caps_msg->peer_mbx_q_id);
@@ -89,7 +94,7 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
cross_tstamp:
access_type = ptp->get_cross_tstamp_access;
if (access_type != IDPF_PTP_DIRECT)
- return 0;
+ goto discipline_clock;
cross_tstamp_offsets = recv_ptp_caps_msg->cross_time_offsets;
@@ -102,6 +107,39 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
temp_offset = le32_to_cpu(cross_tstamp_offsets.cmd_sync_trigger);
ptp->dev_clk_regs.cmd_sync = idpf_get_reg_addr(adapter, temp_offset);
+discipline_clock:
+ access_type = ptp->adj_dev_clk_time_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ return 0;
+
+ clk_adj_offsets = recv_ptp_caps_msg->clk_adj_offsets;
+
+ /* Device clock offsets */
+ temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_cmd_type);
+ ptp->dev_clk_regs.cmd = idpf_get_reg_addr(adapter, temp_offset);
+ temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_incval_l);
+ ptp->dev_clk_regs.incval_l = idpf_get_reg_addr(adapter, temp_offset);
+ temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_incval_h);
+ ptp->dev_clk_regs.incval_h = idpf_get_reg_addr(adapter, temp_offset);
+ temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_shadj_l);
+ ptp->dev_clk_regs.shadj_l = idpf_get_reg_addr(adapter, temp_offset);
+ temp_offset = le32_to_cpu(clk_adj_offsets.dev_clk_shadj_h);
+ ptp->dev_clk_regs.shadj_h = idpf_get_reg_addr(adapter, temp_offset);
+
+ /* PHY clock offsets */
+ temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_cmd_type);
+ ptp->dev_clk_regs.phy_cmd = idpf_get_reg_addr(adapter, temp_offset);
+ temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_incval_l);
+ ptp->dev_clk_regs.phy_incval_l = idpf_get_reg_addr(adapter,
+ temp_offset);
+ temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_incval_h);
+ ptp->dev_clk_regs.phy_incval_h = idpf_get_reg_addr(adapter,
+ temp_offset);
+ temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_shadj_l);
+ ptp->dev_clk_regs.phy_shadj_l = idpf_get_reg_addr(adapter, temp_offset);
+ temp_offset = le32_to_cpu(clk_adj_offsets.phy_clk_shadj_h);
+ ptp->dev_clk_regs.phy_shadj_h = idpf_get_reg_addr(adapter, temp_offset);
+
return 0;
}
@@ -168,6 +206,7 @@ int idpf_ptp_get_cross_time(struct idpf_adapter *adapter,
reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
if (reply_sz < 0)
return reply_sz;
+
if (reply_sz != sizeof(cross_time_msg))
return -EIO;
@@ -176,3 +215,103 @@ int idpf_ptp_get_cross_time(struct idpf_adapter *adapter,
return 0;
}
+
+/**
+ * idpf_ptp_set_dev_clk_time - Send virtchnl set device time message
+ * @adapter: Driver specific private structure
+ * @time: New time value
+ *
+ * Send virtchnl set time message to set the time of the clock.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, u64 time)
+{
+ struct virtchnl2_ptp_set_dev_clk_time set_dev_clk_time_msg = {
+ .dev_time_ns = cpu_to_le64(time),
+ };
+ struct idpf_vc_xn_params xn_params = {
+ .vc_op = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME,
+ .send_buf.iov_base = &set_dev_clk_time_msg,
+ .send_buf.iov_len = sizeof(set_dev_clk_time_msg),
+ .recv_buf.iov_base = &set_dev_clk_time_msg,
+ .recv_buf.iov_len = sizeof(set_dev_clk_time_msg),
+ .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
+ };
+ int reply_sz;
+
+ reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
+ if (reply_sz < 0)
+ return reply_sz;
+ if (reply_sz != sizeof(set_dev_clk_time_msg))
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * idpf_ptp_adj_dev_clk_time - Send virtchnl adj device clock time message
+ * @adapter: Driver specific private structure
+ * @delta: Offset in nanoseconds to adjust the time by
+ *
+ * Send virtchnl adj time message to adjust the clock by the indicated delta.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, s64 delta)
+{
+ struct virtchnl2_ptp_adj_dev_clk_time adj_dev_clk_time_msg = {
+ .delta = cpu_to_le64(delta),
+ };
+ struct idpf_vc_xn_params xn_params = {
+ .vc_op = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME,
+ .send_buf.iov_base = &adj_dev_clk_time_msg,
+ .send_buf.iov_len = sizeof(adj_dev_clk_time_msg),
+ .recv_buf.iov_base = &adj_dev_clk_time_msg,
+ .recv_buf.iov_len = sizeof(adj_dev_clk_time_msg),
+ .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
+ };
+ int reply_sz;
+
+ reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
+ if (reply_sz < 0)
+ return reply_sz;
+ if (reply_sz != sizeof(adj_dev_clk_time_msg))
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * idpf_ptp_adj_dev_clk_fine - Send virtchnl adj time message
+ * @adapter: Driver specific private structure
+ * @incval: Source timer increment value per clock cycle
+ *
+ * Send virtchnl adj fine message to adjust the frequency of the clock by
+ * incval.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, u64 incval)
+{
+ struct virtchnl2_ptp_adj_dev_clk_fine adj_dev_clk_fine_msg = {
+ .incval = cpu_to_le64(incval),
+ };
+ struct idpf_vc_xn_params xn_params = {
+ .vc_op = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE,
+ .send_buf.iov_base = &adj_dev_clk_fine_msg,
+ .send_buf.iov_len = sizeof(adj_dev_clk_fine_msg),
+ .recv_buf.iov_base = &adj_dev_clk_fine_msg,
+ .recv_buf.iov_len = sizeof(adj_dev_clk_fine_msg),
+ .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
+ };
+ int reply_sz;
+
+ reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
+ if (reply_sz < 0)
+ return reply_sz;
+ if (reply_sz != sizeof(adj_dev_clk_fine_msg))
+ return -EIO;
+
+ return 0;
+}
--
2.43.5
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v10 iwl-next 09/11] idpf: add Tx timestamp capabilities negotiation
2025-04-08 10:30 [PATCH v10 iwl-next 00/11] idpf: add initial PTP support Milena Olech
` (7 preceding siblings ...)
2025-04-08 10:31 ` [PATCH v10 iwl-next 08/11] idpf: add PTP clock configuration Milena Olech
@ 2025-04-08 10:31 ` Milena Olech
2025-04-08 21:23 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:31 ` [PATCH v10 iwl-next 10/11] idpf: add Tx timestamp flows Milena Olech
2025-04-08 10:31 ` [PATCH v10 iwl-next 11/11] idpf: add support for Rx timestamping Milena Olech
10 siblings, 1 reply; 35+ messages in thread
From: Milena Olech @ 2025-04-08 10:31 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Milena Olech,
Alexander Lobakin, Emil Tantilov, Pavan Kumar Linga, Samuel Salin
Tx timestamp capabilities are negotiated for the uplink Vport.
Driver receives information about the number of available Tx timestamp
latches, the size of Tx timestamp value and the set of indexes used
for Tx timestamping.
Add function to get the Tx timestamp capabilities and parse the uplink
vport flag.
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Co-developed-by: Emil Tantilov <emil.s.tantilov@intel.com>
Signed-off-by: Emil Tantilov <emil.s.tantilov@intel.com>
Co-developed-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
---
v7 -> v8: refactor idpf_for_each_vport, change a function that
checks if Tx timestamp for a given vport is enabled
v2 -> v3: revert changes in idpf_for_each_vport, fix minor issues
v1 -> v2: change the idpf_for_each_vport macro
drivers/net/ethernet/intel/idpf/idpf.h | 10 ++
drivers/net/ethernet/intel/idpf/idpf_ptp.c | 65 +++++++++
drivers/net/ethernet/intel/idpf/idpf_ptp.h | 95 ++++++++++++-
.../net/ethernet/intel/idpf/idpf_virtchnl.c | 11 ++
.../ethernet/intel/idpf/idpf_virtchnl_ptp.c | 128 +++++++++++++++++-
drivers/net/ethernet/intel/idpf/virtchnl2.h | 12 +-
6 files changed, 317 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h
index a5be660a8cf8..b45754e874f5 100644
--- a/drivers/net/ethernet/intel/idpf/idpf.h
+++ b/drivers/net/ethernet/intel/idpf/idpf.h
@@ -292,6 +292,7 @@ struct idpf_port_stats {
* @port_stats: per port csum, header split, and other offload stats
* @link_up: True if link is up
* @sw_marker_wq: workqueue for marker packets
+ * @tx_tstamp_caps: Capabilities negotiated for Tx timestamping
*/
struct idpf_vport {
u16 num_txq;
@@ -336,6 +337,8 @@ struct idpf_vport {
bool link_up;
wait_queue_head_t sw_marker_wq;
+
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
};
/**
@@ -480,6 +483,13 @@ struct idpf_vport_config {
struct idpf_vc_xn_manager;
+#define idpf_for_each_vport(adapter, iter) \
+ for (struct idpf_vport **__##iter = &(adapter)->vports[0], \
+ *iter = (adapter)->max_vports ? *__##iter : NULL; \
+ iter; \
+ iter = (++__##iter) < &(adapter)->vports[(adapter)->max_vports] ? \
+ *__##iter : NULL)
+
/**
* struct idpf_adapter - Device data struct generated on probe
* @pdev: PCI device struct given on probe
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
index ea6ebd1391e1..2506f6fbe33c 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
@@ -62,6 +62,13 @@ void idpf_ptp_get_features_access(const struct idpf_adapter *adapter)
ptp->adj_dev_clk_time_access = idpf_ptp_get_access(adapter,
direct,
mailbox);
+
+ /* Tx timestamping */
+ direct = VIRTCHNL2_CAP_PTP_TX_TSTAMPS;
+ mailbox = VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB;
+ ptp->tx_tstamp_access = idpf_ptp_get_access(adapter,
+ direct,
+ mailbox);
}
/**
@@ -528,6 +535,61 @@ static int idpf_ptp_create_clock(const struct idpf_adapter *adapter)
return 0;
}
+/**
+ * idpf_ptp_release_vport_tstamp - Release the Tx timestamps trakcers for a
+ * given vport.
+ * @vport: Virtual port structure
+ *
+ * Remove the queues and delete lists that tracks Tx timestamp entries for a
+ * given vport.
+ */
+static void idpf_ptp_release_vport_tstamp(struct idpf_vport *vport)
+{
+ struct idpf_ptp_tx_tstamp *ptp_tx_tstamp, *tmp;
+ struct list_head *head;
+
+ /* Remove list with free latches */
+ spin_lock(&vport->tx_tstamp_caps->lock_free);
+
+ head = &vport->tx_tstamp_caps->latches_free;
+ list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
+ list_del(&ptp_tx_tstamp->list_member);
+ kfree(ptp_tx_tstamp);
+ }
+
+ spin_unlock(&vport->tx_tstamp_caps->lock_free);
+
+ /* Remove list with latches in use */
+ spin_lock(&vport->tx_tstamp_caps->lock_in_use);
+
+ head = &vport->tx_tstamp_caps->latches_in_use;
+ list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
+ list_del(&ptp_tx_tstamp->list_member);
+ kfree(ptp_tx_tstamp);
+ }
+
+ spin_unlock(&vport->tx_tstamp_caps->lock_in_use);
+
+ kfree(vport->tx_tstamp_caps);
+ vport->tx_tstamp_caps = NULL;
+}
+
+/**
+ * idpf_ptp_release_tstamp - Release the Tx timestamps trackers
+ * @adapter: Driver specific private structure
+ *
+ * Remove the queues and delete lists that tracks Tx timestamp entries.
+ */
+static void idpf_ptp_release_tstamp(struct idpf_adapter *adapter)
+{
+ idpf_for_each_vport(adapter, vport) {
+ if (!idpf_ptp_is_vport_tx_tstamp_ena(vport))
+ continue;
+
+ idpf_ptp_release_vport_tstamp(vport);
+ }
+}
+
/**
* idpf_ptp_init - Initialize PTP hardware clock support
* @adapter: Driver specific private structure
@@ -614,6 +676,9 @@ void idpf_ptp_release(struct idpf_adapter *adapter)
if (!ptp)
return;
+ if (ptp->tx_tstamp_access != IDPF_PTP_NONE)
+ idpf_ptp_release_tstamp(adapter);
+
if (ptp->clock)
ptp_clock_unregister(ptp->clock);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.h b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
index 8022ed31defd..f0234f798853 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
@@ -91,6 +91,70 @@ struct idpf_ptp_secondary_mbx {
bool valid:1;
};
+/**
+ * enum idpf_ptp_tx_tstamp_state - Tx timestamp states
+ * @IDPF_PTP_FREE: Tx timestamp index free to use
+ * @IDPF_PTP_REQUEST: Tx timestamp index set to the Tx descriptor
+ * @IDPF_PTP_READ_VALUE: Tx timestamp value ready to be read
+ */
+enum idpf_ptp_tx_tstamp_state {
+ IDPF_PTP_FREE,
+ IDPF_PTP_REQUEST,
+ IDPF_PTP_READ_VALUE,
+};
+
+/**
+ * struct idpf_ptp_tx_tstamp_status - Parameters to track Tx timestamp
+ * @skb: the pointer to the SKB that received the completion tag
+ * @state: the state of the Tx timestamp
+ */
+struct idpf_ptp_tx_tstamp_status {
+ struct sk_buff *skb;
+ enum idpf_ptp_tx_tstamp_state state;
+};
+
+/**
+ * struct idpf_ptp_tx_tstamp - Parameters for Tx timestamping
+ * @list_member: the list member structure
+ * @tx_latch_reg_offset_l: Tx tstamp latch low register offset
+ * @tx_latch_reg_offset_h: Tx tstamp latch high register offset
+ * @skb: the pointer to the SKB for this timestamp request
+ * @tstamp: the Tx tstamp value
+ * @idx: the index of the Tx tstamp
+ */
+struct idpf_ptp_tx_tstamp {
+ struct list_head list_member;
+ u32 tx_latch_reg_offset_l;
+ u32 tx_latch_reg_offset_h;
+ struct sk_buff *skb;
+ u64 tstamp;
+ u32 idx;
+};
+
+/**
+ * struct idpf_ptp_vport_tx_tstamp_caps - Tx timestamp capabilities
+ * @vport_id: the vport id
+ * @num_entries: the number of negotiated Tx timestamp entries
+ * @tstamp_ns_lo_bit: first bit for nanosecond part of the timestamp
+ * @lock_in_use: the lock to the used latches list
+ * @lock_free: the lock to free the latches list
+ * @lock_status: the lock to the status tracker
+ * @latches_free: the list of the free Tx timestamps latches
+ * @latches_in_use: the list of the used Tx timestamps latches
+ * @tx_tstamp_status: Tx tstamp status tracker
+ */
+struct idpf_ptp_vport_tx_tstamp_caps {
+ u32 vport_id;
+ u16 num_entries;
+ u16 tstamp_ns_lo_bit;
+ spinlock_t lock_in_use;
+ spinlock_t lock_free;
+ spinlock_t lock_status;
+ struct list_head latches_free;
+ struct list_head latches_in_use;
+ struct idpf_ptp_tx_tstamp_status tx_tstamp_status[];
+};
+
/**
* struct idpf_ptp - PTP parameters
* @info: structure defining PTP hardware capabilities
@@ -105,6 +169,7 @@ struct idpf_ptp_secondary_mbx {
* @get_cross_tstamp_access: access type for the cross timestamping
* @set_dev_clk_time_access: access type for setting the device clock time
* @adj_dev_clk_time_access: access type for the adjusting the device clock
+ * @tx_tstamp_access: access type for the Tx timestamp value read
* @rsv: reserved bits
* @secondary_mbx: parameters for using dedicated PTP mailbox
* @read_dev_clk_lock: spinlock protecting access to the device clock read
@@ -123,7 +188,8 @@ struct idpf_ptp {
enum idpf_ptp_access get_cross_tstamp_access:2;
enum idpf_ptp_access set_dev_clk_time_access:2;
enum idpf_ptp_access adj_dev_clk_time_access:2;
- u8 rsv;
+ enum idpf_ptp_access tx_tstamp_access:2;
+ u8 rsv:6;
struct idpf_ptp_secondary_mbx secondary_mbx;
spinlock_t read_dev_clk_lock;
};
@@ -152,6 +218,27 @@ struct idpf_ptp_dev_timers {
u64 dev_clk_time_ns;
};
+/**
+ * idpf_ptp_is_vport_tx_tstamp_ena - Verify the Tx timestamping enablement for
+ * a given vport.
+ * @vport: Virtual port structure
+ *
+ * Tx timestamp capabilities are negotiated with the Control Plane only if the
+ * device clock value can be read, Tx timestamp access type is different than
+ * NONE, and the PTP clock for the adapter is created. When all those conditions
+ * are satisfied, Tx timestamp feature is enabled and tx_tstamp_caps is
+ * allocated and fulfilled.
+ *
+ * Return: true if the Tx timestamping is enabled, false otherwise.
+ */
+static inline bool idpf_ptp_is_vport_tx_tstamp_ena(struct idpf_vport *vport)
+{
+ if (!vport->tx_tstamp_caps)
+ return false;
+ else
+ return true;
+}
+
#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
int idpf_ptp_init(struct idpf_adapter *adapter);
void idpf_ptp_release(struct idpf_adapter *adapter);
@@ -164,6 +251,7 @@ int idpf_ptp_get_cross_time(struct idpf_adapter *adapter,
int idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, u64 time);
int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, u64 incval);
int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, s64 delta);
+int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport);
#else /* CONFIG_PTP_1588_CLOCK */
static inline int idpf_ptp_init(struct idpf_adapter *adapter)
{
@@ -212,5 +300,10 @@ static inline int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter,
return -EOPNOTSUPP;
}
+static inline int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
+{
+ return -EOPNOTSUPP;
+}
+
#endif /* CONFIG_PTP_1588_CLOCK */
#endif /* _IDPF_PTP_H */
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index b7c9c5d84350..9f369bbea0fe 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -169,6 +169,8 @@ static bool idpf_ptp_is_mb_msg(u32 op)
case VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME:
case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE:
case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME:
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
return true;
default:
return false;
@@ -3133,6 +3135,7 @@ void idpf_vport_init(struct idpf_vport *vport, struct idpf_vport_max_q *max_q)
u16 rx_itr[] = {2, 8, 32, 96, 128};
struct idpf_rss_data *rss_data;
u16 idx = vport->idx;
+ int err;
vport_config = adapter->vport_config[idx];
rss_data = &vport_config->user_config.rss_data;
@@ -3167,6 +3170,14 @@ void idpf_vport_init(struct idpf_vport *vport, struct idpf_vport_max_q *max_q)
idpf_vport_alloc_vec_indexes(vport);
vport->crc_enable = adapter->crc_enable;
+
+ if (!(vport_msg->vport_flags &
+ cpu_to_le16(VIRTCHNL2_VPORT_UPLINK_PORT)))
+ return;
+
+ err = idpf_ptp_get_vport_tstamps_caps(vport);
+ if (err)
+ pci_dbg(vport->adapter->pdev, "Tx timestamping not supported\n");
}
/**
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
index b8185778cd07..71b8e39d20d6 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
@@ -22,7 +22,8 @@ int idpf_ptp_get_caps(struct idpf_adapter *adapter)
VIRTCHNL2_CAP_PTP_GET_CROSS_TIME |
VIRTCHNL2_CAP_PTP_GET_CROSS_TIME_MB |
VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
- VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB)
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB)
};
struct idpf_vc_xn_params xn_params = {
.vc_op = VIRTCHNL2_OP_PTP_GET_CAPS,
@@ -315,3 +316,128 @@ int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, u64 incval)
return 0;
}
+
+/**
+ * idpf_ptp_get_vport_tstamps_caps - Send virtchnl to get tstamps caps for vport
+ * @vport: Virtual port structure
+ *
+ * Send virtchnl get vport tstamps caps message to receive the set of tstamp
+ * capabilities per vport.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
+{
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps send_tx_tstamp_caps;
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps *rcv_tx_tstamp_caps;
+ struct idpf_ptp_tx_tstamp *ptp_tx_tstamp, *ptp_tx_tstamps, *tmp;
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
+ struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
+ struct idpf_vc_xn_params xn_params = {
+ .vc_op = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS,
+ .send_buf.iov_base = &send_tx_tstamp_caps,
+ .send_buf.iov_len = sizeof(send_tx_tstamp_caps),
+ .recv_buf.iov_len = IDPF_CTLQ_MAX_BUF_LEN,
+ .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
+ };
+ enum idpf_ptp_access tstamp_access, get_dev_clk_access;
+ struct idpf_ptp *ptp = vport->adapter->ptp;
+ struct list_head *head;
+ int err = 0, reply_sz;
+ u16 num_latches;
+ u32 size;
+
+ if (!ptp)
+ return -EOPNOTSUPP;
+
+ tstamp_access = ptp->tx_tstamp_access;
+ get_dev_clk_access = ptp->get_dev_clk_time_access;
+ if (tstamp_access == IDPF_PTP_NONE ||
+ get_dev_clk_access == IDPF_PTP_NONE)
+ return -EOPNOTSUPP;
+
+ rcv_tx_tstamp_caps = kzalloc(IDPF_CTLQ_MAX_BUF_LEN, GFP_KERNEL);
+ if (!rcv_tx_tstamp_caps)
+ return -ENOMEM;
+
+ send_tx_tstamp_caps.vport_id = cpu_to_le32(vport->vport_id);
+ xn_params.recv_buf.iov_base = rcv_tx_tstamp_caps;
+
+ reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);
+ if (reply_sz < 0) {
+ err = reply_sz;
+ goto get_tstamp_caps_out;
+ }
+
+ num_latches = le16_to_cpu(rcv_tx_tstamp_caps->num_latches);
+ size = struct_size(rcv_tx_tstamp_caps, tstamp_latches, num_latches);
+ if (reply_sz != size) {
+ err = -EIO;
+ goto get_tstamp_caps_out;
+ }
+
+ size = struct_size(tstamp_caps, tx_tstamp_status, num_latches);
+ tstamp_caps = kzalloc(size, GFP_KERNEL);
+ if (!tstamp_caps) {
+ err = -ENOMEM;
+ goto get_tstamp_caps_out;
+ }
+
+ tstamp_caps->num_entries = num_latches;
+ INIT_LIST_HEAD(&tstamp_caps->latches_in_use);
+ INIT_LIST_HEAD(&tstamp_caps->latches_free);
+
+ spin_lock_init(&tstamp_caps->lock_free);
+ spin_lock_init(&tstamp_caps->lock_in_use);
+ spin_lock_init(&tstamp_caps->lock_status);
+
+ tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ ptp_tx_tstamps = kcalloc(tstamp_caps->num_entries,
+ sizeof(*ptp_tx_tstamp), GFP_KERNEL);
+ if (!ptp_tx_tstamps) {
+ err = -ENOMEM;
+ goto err_free_ptp_tx_stamp_list;
+ }
+
+ for (u16 i = 0; i < tstamp_caps->num_entries; i++) {
+ __le32 offset_l, offset_h;
+
+ ptp_tx_tstamp = ptp_tx_tstamps + i;
+ tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i];
+
+ if (tstamp_access != IDPF_PTP_DIRECT)
+ goto skip_offsets;
+
+ offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
+ offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
+ ptp_tx_tstamp->tx_latch_reg_offset_l = le32_to_cpu(offset_l);
+ ptp_tx_tstamp->tx_latch_reg_offset_h = le32_to_cpu(offset_h);
+
+skip_offsets:
+ ptp_tx_tstamp->idx = tx_tstamp_latch_caps.index;
+
+ list_add(&ptp_tx_tstamp->list_member,
+ &tstamp_caps->latches_free);
+
+ tstamp_caps->tx_tstamp_status[i].state = IDPF_PTP_FREE;
+ }
+
+ vport->tx_tstamp_caps = tstamp_caps;
+ kfree(rcv_tx_tstamp_caps);
+
+ return 0;
+
+err_free_ptp_tx_stamp_list:
+ head = &tstamp_caps->latches_free;
+ list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
+ list_del(&ptp_tx_tstamp->list_member);
+ kfree(ptp_tx_tstamp);
+ }
+
+ kfree(tstamp_caps);
+get_tstamp_caps_out:
+ kfree(rcv_tx_tstamp_caps);
+
+ return err;
+}
diff --git a/drivers/net/ethernet/intel/idpf/virtchnl2.h b/drivers/net/ethernet/intel/idpf/virtchnl2.h
index 44a5ee84ed60..07235f1b4c5c 100644
--- a/drivers/net/ethernet/intel/idpf/virtchnl2.h
+++ b/drivers/net/ethernet/intel/idpf/virtchnl2.h
@@ -569,6 +569,14 @@ struct virtchnl2_queue_reg_chunks {
};
VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_queue_reg_chunks);
+/**
+ * enum virtchnl2_vport_flags - Vport flags that indicate vport capabilities.
+ * @VIRTCHNL2_VPORT_UPLINK_PORT: Representatives of underlying physical ports
+ */
+enum virtchnl2_vport_flags {
+ VIRTCHNL2_VPORT_UPLINK_PORT = BIT(0),
+};
+
/**
* struct virtchnl2_create_vport - Create vport config info.
* @vport_type: See enum virtchnl2_vport_type.
@@ -587,7 +595,7 @@ VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_queue_reg_chunks);
* @max_mtu: Max MTU. CP populates this field on response.
* @vport_id: Vport id. CP populates this field on response.
* @default_mac_addr: Default MAC address.
- * @pad: Padding.
+ * @vport_flags: See enum virtchnl2_vport_flags.
* @rx_desc_ids: See VIRTCHNL2_RX_DESC_IDS definitions.
* @tx_desc_ids: See VIRTCHNL2_TX_DESC_IDS definitions.
* @pad1: Padding.
@@ -620,7 +628,7 @@ struct virtchnl2_create_vport {
__le16 max_mtu;
__le32 vport_id;
u8 default_mac_addr[ETH_ALEN];
- __le16 pad;
+ __le16 vport_flags;
__le64 rx_desc_ids;
__le64 tx_desc_ids;
u8 pad1[72];
--
2.43.5
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v10 iwl-next 10/11] idpf: add Tx timestamp flows
2025-04-08 10:30 [PATCH v10 iwl-next 00/11] idpf: add initial PTP support Milena Olech
` (8 preceding siblings ...)
2025-04-08 10:31 ` [PATCH v10 iwl-next 09/11] idpf: add Tx timestamp capabilities negotiation Milena Olech
@ 2025-04-08 10:31 ` Milena Olech
2025-04-08 21:31 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:31 ` [PATCH v10 iwl-next 11/11] idpf: add support for Rx timestamping Milena Olech
10 siblings, 1 reply; 35+ messages in thread
From: Milena Olech @ 2025-04-08 10:31 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Milena Olech,
Josh Hay, Samuel Salin
Add functions to request Tx timestamp for the PTP packets, read the Tx
timestamp when the completion tag for that packet is being received,
extend the Tx timestamp value and set the supported timestamping modes.
Tx timestamp is requested for the PTP packets by setting a TSYN bit and
index value in the Tx context descriptor. The driver assumption is that
the Tx timestamp value is ready to be read when the completion tag is
received. Then the driver schedules delayed work and the Tx timestamp
value read is requested through virtchnl message. At the end, the Tx
timestamp value is extended to 64-bit and provided back to the skb.
Co-developed-by: Josh Hay <joshua.a.hay@intel.com>
Signed-off-by: Josh Hay <joshua.a.hay@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
---
v8 -> v9: update kernel_hwtstamp_config when timestamp mode is set,
check link status in timestamp callbacks idpf_hwtstamp_get/set
v7 -> v8: change the type of delta calculated when the timestamp is
extended to 64 bit based on the device clock value
v6 -> v7: change the method for preparing flow desciptor to set
tstamp fields to 0 indirectly
v5 -> v6: change locking mechanism in get_ts_info, clean timestamp
fields when preparing flow descriptor to prevent collisions with
PHY timestamping
v4 -> v5: fix the spin_unlock_bh when the Tx timestamp is requested
and the list of free latches is empty
v3 -> v4: change Tx timestamp filters based on the PTP capabilities,
use list_for_each_entry_safe when deleting list items, rewrite
function that extends Tx timestamp value to 64 bits, minor fixes
v2 -> v3: change get_timestamp_filter function name, split stats
into vport-based and tx queue-based
v1 -> v2: add timestamping stats, use ndo_hwtamp_get/ndo_hwstamp_set
drivers/net/ethernet/intel/idpf/idpf.h | 20 ++
.../net/ethernet/intel/idpf/idpf_ethtool.c | 74 +++++-
.../net/ethernet/intel/idpf/idpf_lan_txrx.h | 13 +-
drivers/net/ethernet/intel/idpf/idpf_lib.c | 45 ++++
drivers/net/ethernet/intel/idpf/idpf_ptp.c | 238 +++++++++++++++++-
drivers/net/ethernet/intel/idpf/idpf_ptp.h | 51 ++++
drivers/net/ethernet/intel/idpf/idpf_txrx.c | 141 ++++++++++-
drivers/net/ethernet/intel/idpf/idpf_txrx.h | 11 +-
.../net/ethernet/intel/idpf/idpf_virtchnl.c | 6 +-
.../ethernet/intel/idpf/idpf_virtchnl_ptp.c | 235 +++++++++++++++++
10 files changed, 819 insertions(+), 15 deletions(-)
diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h
index b45754e874f5..313a44507d04 100644
--- a/drivers/net/ethernet/intel/idpf/idpf.h
+++ b/drivers/net/ethernet/intel/idpf/idpf.h
@@ -246,9 +246,23 @@ struct idpf_port_stats {
u64_stats_t tx_busy;
u64_stats_t tx_drops;
u64_stats_t tx_dma_map_errs;
+ u64_stats_t tx_hwtstamp_skipped;
struct virtchnl2_vport_stats vport_stats;
};
+/**
+ * struct idpf_tx_tstamp_stats - Tx timestamp statistics
+ * @tx_hwtstamp_lock: Lock to protect Tx tstamp stats
+ * @tx_hwtstamp_discarded: Number of Tx skbs discarded due to cached PHC time
+ * being too old to correctly extend timestamp
+ * @tx_hwtstamp_flushed: Number of Tx skbs flushed due to interface closed
+ */
+struct idpf_tx_tstamp_stats {
+ struct mutex tx_hwtstamp_lock;
+ u32 tx_hwtstamp_discarded;
+ u32 tx_hwtstamp_flushed;
+};
+
/**
* struct idpf_vport - Handle for netdevices and queue resources
* @num_txq: Number of allocated TX queues
@@ -293,6 +307,9 @@ struct idpf_port_stats {
* @link_up: True if link is up
* @sw_marker_wq: workqueue for marker packets
* @tx_tstamp_caps: Capabilities negotiated for Tx timestamping
+ * @tstamp_config: The Tx tstamp config
+ * @tstamp_task: Tx timestamping task
+ * @tstamp_stats: Tx timestamping statistics
*/
struct idpf_vport {
u16 num_txq;
@@ -339,6 +356,9 @@ struct idpf_vport {
wait_queue_head_t sw_marker_wq;
struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ struct kernel_hwtstamp_config tstamp_config;
+ struct work_struct tstamp_task;
+ struct idpf_tx_tstamp_stats tstamp_stats;
};
/**
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c
index 59b1a1a09996..ec4183a609c4 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c
@@ -2,6 +2,7 @@
/* Copyright (C) 2023 Intel Corporation */
#include "idpf.h"
+#include "idpf_ptp.h"
/**
* idpf_get_rxnfc - command to get RX flow classification rules
@@ -479,6 +480,9 @@ static const struct idpf_stats idpf_gstrings_port_stats[] = {
IDPF_PORT_STAT("tx-unicast_pkts", port_stats.vport_stats.tx_unicast),
IDPF_PORT_STAT("tx-multicast_pkts", port_stats.vport_stats.tx_multicast),
IDPF_PORT_STAT("tx-broadcast_pkts", port_stats.vport_stats.tx_broadcast),
+ IDPF_PORT_STAT("tx-hwtstamp_skipped", port_stats.tx_hwtstamp_skipped),
+ IDPF_PORT_STAT("tx-hwtstamp_flushed", tstamp_stats.tx_hwtstamp_flushed),
+ IDPF_PORT_STAT("tx-hwtstamp_discarded", tstamp_stats.tx_hwtstamp_discarded),
};
#define IDPF_PORT_STATS_LEN ARRAY_SIZE(idpf_gstrings_port_stats)
@@ -780,6 +784,7 @@ static void idpf_collect_queue_stats(struct idpf_vport *vport)
u64_stats_set(&pstats->tx_busy, 0);
u64_stats_set(&pstats->tx_drops, 0);
u64_stats_set(&pstats->tx_dma_map_errs, 0);
+ u64_stats_set(&pstats->tx_hwtstamp_skipped, 0);
u64_stats_update_end(&pstats->stats_sync);
for (i = 0; i < vport->num_rxq_grp; i++) {
@@ -828,7 +833,7 @@ static void idpf_collect_queue_stats(struct idpf_vport *vport)
struct idpf_txq_group *txq_grp = &vport->txq_grps[i];
for (j = 0; j < txq_grp->num_txq; j++) {
- u64 linearize, qbusy, skb_drops, dma_map_errs;
+ u64 linearize, qbusy, skb_drops, dma_map_errs, tstamp;
struct idpf_tx_queue *txq = txq_grp->txqs[j];
struct idpf_tx_queue_stats *stats;
unsigned int start;
@@ -844,6 +849,7 @@ static void idpf_collect_queue_stats(struct idpf_vport *vport)
qbusy = u64_stats_read(&stats->q_busy);
skb_drops = u64_stats_read(&stats->skb_drops);
dma_map_errs = u64_stats_read(&stats->dma_map_errs);
+ tstamp = u64_stats_read(&stats->tstamp_skipped);
} while (u64_stats_fetch_retry(&txq->stats_sync, start));
u64_stats_update_begin(&pstats->stats_sync);
@@ -851,6 +857,7 @@ static void idpf_collect_queue_stats(struct idpf_vport *vport)
u64_stats_add(&pstats->tx_busy, qbusy);
u64_stats_add(&pstats->tx_drops, skb_drops);
u64_stats_add(&pstats->tx_dma_map_errs, dma_map_errs);
+ u64_stats_add(&pstats->tx_hwtstamp_skipped, tstamp);
u64_stats_update_end(&pstats->stats_sync);
}
}
@@ -1312,6 +1319,70 @@ static int idpf_get_link_ksettings(struct net_device *netdev,
return 0;
}
+/**
+ * idpf_get_timestamp_filters - Get the supported timestamping mode
+ * @vport: Virtual port structure
+ * @info: ethtool timestamping info structure
+ *
+ * Get the Tx/Rx timestamp filters.
+ */
+static void idpf_get_timestamp_filters(const struct idpf_vport *vport,
+ struct kernel_ethtool_ts_info *info)
+{
+ info->so_timestamping = SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ info->tx_types = BIT(HWTSTAMP_TX_OFF);
+
+ if (!vport->tx_tstamp_caps ||
+ vport->adapter->ptp->tx_tstamp_access == IDPF_PTP_NONE)
+ return;
+
+ info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
+ SOF_TIMESTAMPING_TX_HARDWARE;
+
+ info->tx_types |= BIT(HWTSTAMP_TX_ON);
+}
+
+/**
+ * idpf_get_ts_info - Get device PHC association
+ * @netdev: network interface device structure
+ * @info: ethtool timestamping info structure
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int idpf_get_ts_info(struct net_device *netdev,
+ struct kernel_ethtool_ts_info *info)
+{
+ struct idpf_netdev_priv *np = netdev_priv(netdev);
+ struct idpf_vport *vport;
+ int err = 0;
+
+ if (!mutex_trylock(&np->adapter->vport_ctrl_lock))
+ return -EBUSY;
+
+ vport = idpf_netdev_to_vport(netdev);
+
+ if (!vport->adapter->ptp) {
+ err = -EOPNOTSUPP;
+ goto unlock;
+ }
+
+ if (idpf_is_cap_ena(vport->adapter, IDPF_OTHER_CAPS, VIRTCHNL2_CAP_PTP) &&
+ vport->adapter->ptp->clock) {
+ info->phc_index = ptp_clock_index(vport->adapter->ptp->clock);
+ idpf_get_timestamp_filters(vport, info);
+ } else {
+ pci_dbg(vport->adapter->pdev, "PTP clock not detected\n");
+ err = ethtool_op_get_ts_info(netdev, info);
+ }
+
+unlock:
+ mutex_unlock(&np->adapter->vport_ctrl_lock);
+
+ return err;
+}
+
static const struct ethtool_ops idpf_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_USE_ADAPTIVE,
@@ -1336,6 +1407,7 @@ static const struct ethtool_ops idpf_ethtool_ops = {
.get_ringparam = idpf_get_ringparam,
.set_ringparam = idpf_set_ringparam,
.get_link_ksettings = idpf_get_link_ksettings,
+ .get_ts_info = idpf_get_ts_info,
};
/**
diff --git a/drivers/net/ethernet/intel/idpf/idpf_lan_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_lan_txrx.h
index 8c7f8ef8f1a1..7492d1713243 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_lan_txrx.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_lan_txrx.h
@@ -282,7 +282,18 @@ struct idpf_flex_tx_tso_ctx_qw {
u8 flex;
};
-struct idpf_flex_tx_ctx_desc {
+union idpf_flex_tx_ctx_desc {
+ /* DTYPE = IDPF_TX_DESC_DTYPE_CTX (0x01) */
+ struct {
+ __le64 qw0;
+#define IDPF_TX_CTX_L2TAG2_M GENMASK_ULL(47, 32)
+ __le64 qw1;
+#define IDPF_TX_CTX_DTYPE_M GENMASK_ULL(3, 0)
+#define IDPF_TX_CTX_CMD_M GENMASK_ULL(15, 4)
+#define IDPF_TX_CTX_TSYN_REG_M GENMASK_ULL(47, 30)
+#define IDPF_TX_CTX_MSS_M GENMASK_ULL(50, 63)
+ } tsyn;
+
/* DTYPE = IDPF_TX_DESC_DTYPE_FLEX_TSO_CTX (0x05) */
struct {
struct idpf_flex_tx_tso_ctx_qw qw0;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c
index 730a9c7a59f2..ccceda18c392 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c
@@ -3,6 +3,7 @@
#include "idpf.h"
#include "idpf_virtchnl.h"
+#include "idpf_ptp.h"
static const struct net_device_ops idpf_netdev_ops;
@@ -2339,6 +2340,48 @@ void idpf_free_dma_mem(struct idpf_hw *hw, struct idpf_dma_mem *mem)
mem->pa = 0;
}
+static int idpf_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
+{
+ struct idpf_vport *vport;
+ int err;
+
+ idpf_vport_ctrl_lock(netdev);
+ vport = idpf_netdev_to_vport(netdev);
+
+ if (!idpf_ptp_is_vport_tx_tstamp_ena(vport)) {
+ idpf_vport_ctrl_unlock(netdev);
+ return -EOPNOTSUPP;
+ }
+
+ err = idpf_ptp_set_timestamp_mode(vport, config);
+
+ idpf_vport_ctrl_unlock(netdev);
+
+ return err;
+}
+
+static int idpf_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config)
+{
+ struct idpf_vport *vport;
+
+ idpf_vport_ctrl_lock(netdev);
+ vport = idpf_netdev_to_vport(netdev);
+
+ if (!idpf_ptp_is_vport_tx_tstamp_ena(vport)) {
+ idpf_vport_ctrl_unlock(netdev);
+ return 0;
+ }
+
+ *config = vport->tstamp_config;
+
+ idpf_vport_ctrl_unlock(netdev);
+
+ return 0;
+}
+
static const struct net_device_ops idpf_netdev_ops = {
.ndo_open = idpf_open,
.ndo_stop = idpf_stop,
@@ -2351,4 +2394,6 @@ static const struct net_device_ops idpf_netdev_ops = {
.ndo_get_stats64 = idpf_get_stats64,
.ndo_set_features = idpf_set_features,
.ndo_tx_timeout = idpf_tx_timeout,
+ .ndo_hwtstamp_get = idpf_hwtstamp_get,
+ .ndo_hwtstamp_set = idpf_hwtstamp_set,
};
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
index 2506f6fbe33c..63c51684f60d 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
@@ -328,6 +328,37 @@ static int idpf_ptp_gettimex64(struct ptp_clock_info *info,
return 0;
}
+/**
+ * idpf_ptp_update_cached_phctime - Update the cached PHC time values
+ * @adapter: Driver specific private structure
+ *
+ * This function updates the system time values which are cached in the adapter
+ * structure.
+ *
+ * This function must be called periodically to ensure that the cached value
+ * is never more than 2 seconds old.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int idpf_ptp_update_cached_phctime(struct idpf_adapter *adapter)
+{
+ u64 systime;
+ int err;
+
+ err = idpf_ptp_read_src_clk_reg(adapter, &systime, NULL);
+ if (err)
+ return -EACCES;
+
+ /* Update the cached PHC time stored in the adapter structure.
+ * These values are used to extend Tx timestamp values to 64 bit
+ * expected by the stack.
+ */
+ WRITE_ONCE(adapter->ptp->cached_phc_time, systime);
+ WRITE_ONCE(adapter->ptp->cached_phc_jiffies, jiffies);
+
+ return 0;
+}
+
/**
* idpf_ptp_settime64 - Set the time of the clock
* @info: the driver's PTP info structure
@@ -358,6 +389,11 @@ static int idpf_ptp_settime64(struct ptp_clock_info *info,
return err;
}
+ err = idpf_ptp_update_cached_phctime(adapter);
+ if (err)
+ pci_warn(adapter->pdev,
+ "Unable to immediately update cached PHC time\n");
+
return 0;
}
@@ -413,6 +449,11 @@ static int idpf_ptp_adjtime(struct ptp_clock_info *info, s64 delta)
return err;
}
+ err = idpf_ptp_update_cached_phctime(adapter);
+ if (err)
+ pci_warn(adapter->pdev,
+ "Unable to immediately update cached PHC time\n");
+
return 0;
}
@@ -476,6 +517,159 @@ static int idpf_ptp_gpio_enable(struct ptp_clock_info *info,
return -EOPNOTSUPP;
}
+/**
+ * idpf_ptp_tstamp_extend_32b_to_64b - Convert a 32b nanoseconds Tx or Rx
+ * timestamp value to 64b.
+ * @cached_phc_time: recently cached copy of PHC time
+ * @in_timestamp: Ingress/egress 32b nanoseconds timestamp value
+ *
+ * Hardware captures timestamps which contain only 32 bits of nominal
+ * nanoseconds, as opposed to the 64bit timestamps that the stack expects.
+ *
+ * Return: Tx timestamp value extended to 64 bits based on cached PHC time.
+ */
+u64 idpf_ptp_tstamp_extend_32b_to_64b(u64 cached_phc_time, u32 in_timestamp)
+{
+ s64 delta;
+
+ delta = in_timestamp - lower_32_bits(cached_phc_time);
+
+ return cached_phc_time + delta;
+}
+
+/**
+ * idpf_ptp_extend_ts - Convert a 40b timestamp to 64b nanoseconds
+ * @vport: Virtual port structure
+ * @in_tstamp: Ingress/egress timestamp value
+ *
+ * It is assumed that the caller verifies the timestamp is valid prior to
+ * calling this function.
+ *
+ * Extract the 32bit nominal nanoseconds and extend them. Use the cached PHC
+ * time stored in the device private PTP structure as the basis for timestamp
+ * extension.
+ *
+ * Return: Tx timestamp value extended to 64 bits.
+ */
+u64 idpf_ptp_extend_ts(struct idpf_vport *vport, u64 in_tstamp)
+{
+ struct idpf_ptp *ptp = vport->adapter->ptp;
+ unsigned long discard_time;
+
+ discard_time = ptp->cached_phc_jiffies + 2 * HZ;
+
+ if (time_is_before_jiffies(discard_time)) {
+ mutex_lock(&vport->tstamp_stats.tx_hwtstamp_lock);
+ vport->tstamp_stats.tx_hwtstamp_discarded++;
+ mutex_unlock(&vport->tstamp_stats.tx_hwtstamp_lock);
+
+ return 0;
+ }
+
+ return idpf_ptp_tstamp_extend_32b_to_64b(ptp->cached_phc_time,
+ lower_32_bits(in_tstamp));
+}
+
+/**
+ * idpf_ptp_request_ts - Request an available Tx timestamp index
+ * @tx_q: Transmit queue on which the Tx timestamp is requested
+ * @skb: The SKB to associate with this timestamp request
+ * @idx: Index of the Tx timestamp latch
+ *
+ * Request tx timestamp index negotiated during PTP init that will be set into
+ * Tx descriptor.
+ *
+ * Return: 0 and the index that can be provided to Tx descriptor on success,
+ * -errno otherwise.
+ */
+int idpf_ptp_request_ts(struct idpf_tx_queue *tx_q, struct sk_buff *skb,
+ u32 *idx)
+{
+ struct idpf_ptp_tx_tstamp *ptp_tx_tstamp;
+ struct list_head *head;
+
+ /* Get the index from the free latches list */
+ spin_lock_bh(&tx_q->cached_tstamp_caps->lock_free);
+
+ head = &tx_q->cached_tstamp_caps->latches_free;
+ if (list_empty(head)) {
+ spin_unlock_bh(&tx_q->cached_tstamp_caps->lock_free);
+ return -ENOBUFS;
+ }
+
+ ptp_tx_tstamp = list_first_entry(head, struct idpf_ptp_tx_tstamp,
+ list_member);
+ list_del(&ptp_tx_tstamp->list_member);
+ spin_unlock_bh(&tx_q->cached_tstamp_caps->lock_free);
+
+ ptp_tx_tstamp->skb = skb_get(skb);
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+
+ /* Move the element to the used latches list */
+ spin_lock_bh(&tx_q->cached_tstamp_caps->lock_in_use);
+ list_add(&ptp_tx_tstamp->list_member,
+ &tx_q->cached_tstamp_caps->latches_in_use);
+ spin_unlock_bh(&tx_q->cached_tstamp_caps->lock_in_use);
+
+ *idx = ptp_tx_tstamp->idx;
+
+ return 0;
+}
+
+/**
+ * idpf_ptp_set_timestamp_mode - Setup driver for requested timestamp mode
+ * @vport: Virtual port structure
+ * @config: Hwtstamp settings requested or saved
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int idpf_ptp_set_timestamp_mode(struct idpf_vport *vport,
+ struct kernel_hwtstamp_config *config)
+{
+ switch (config->tx_type) {
+ case HWTSTAMP_TX_OFF:
+ break;
+ case HWTSTAMP_TX_ON:
+ if (!idpf_ptp_is_vport_tx_tstamp_ena(vport))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ vport->tstamp_config.tx_type = config->tx_type;
+
+ return 0;
+}
+
+/**
+ * idpf_tstamp_task - Delayed task to handle Tx tstamps
+ * @work: work_struct handle
+ */
+void idpf_tstamp_task(struct work_struct *work)
+{
+ struct idpf_vport *vport;
+
+ vport = container_of(work, struct idpf_vport, tstamp_task);
+
+ idpf_ptp_get_tx_tstamp(vport);
+}
+
+/**
+ * idpf_ptp_do_aux_work - Do PTP periodic work
+ * @info: Driver's PTP info structure
+ *
+ * Return: Number of jiffies to periodic work.
+ */
+static long idpf_ptp_do_aux_work(struct ptp_clock_info *info)
+{
+ struct idpf_adapter *adapter = idpf_ptp_info_to_adapter(info);
+
+ idpf_ptp_update_cached_phctime(adapter);
+
+ return msecs_to_jiffies(500);
+}
+
/**
* idpf_ptp_set_caps - Set PTP capabilities
* @adapter: Driver specific private structure
@@ -497,6 +691,7 @@ static void idpf_ptp_set_caps(const struct idpf_adapter *adapter)
info->adjtime = idpf_ptp_adjtime;
info->verify = idpf_ptp_verify_pin;
info->enable = idpf_ptp_gpio_enable;
+ info->do_aux_work = idpf_ptp_do_aux_work;
#if IS_ENABLED(CONFIG_ARM_ARCH_TIMER)
info->getcrosststamp = idpf_ptp_get_crosststamp;
@@ -548,6 +743,8 @@ static void idpf_ptp_release_vport_tstamp(struct idpf_vport *vport)
struct idpf_ptp_tx_tstamp *ptp_tx_tstamp, *tmp;
struct list_head *head;
+ cancel_work_sync(&vport->tstamp_task);
+
/* Remove list with free latches */
spin_lock(&vport->tx_tstamp_caps->lock_free);
@@ -560,16 +757,21 @@ static void idpf_ptp_release_vport_tstamp(struct idpf_vport *vport)
spin_unlock(&vport->tx_tstamp_caps->lock_free);
/* Remove list with latches in use */
+ mutex_lock(&vport->tstamp_stats.tx_hwtstamp_lock);
spin_lock(&vport->tx_tstamp_caps->lock_in_use);
head = &vport->tx_tstamp_caps->latches_in_use;
list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
+ vport->tstamp_stats.tx_hwtstamp_flushed++;
list_del(&ptp_tx_tstamp->list_member);
kfree(ptp_tx_tstamp);
}
spin_unlock(&vport->tx_tstamp_caps->lock_in_use);
+ mutex_unlock(&vport->tstamp_stats.tx_hwtstamp_lock);
+ mutex_destroy(&vport->tstamp_stats.tx_hwtstamp_lock);
+
kfree(vport->tx_tstamp_caps);
vport->tx_tstamp_caps = NULL;
}
@@ -590,6 +792,27 @@ static void idpf_ptp_release_tstamp(struct idpf_adapter *adapter)
}
}
+/**
+ * idpf_ptp_get_txq_tstamp_capability - Verify the timestamping capability
+ * for a given tx queue.
+ * @txq: Transmit queue
+ *
+ * Since performing timestamp flows requires reading the device clock value and
+ * the support in the Control Plane, the function checks both factors and
+ * summarizes the support for the timestamping.
+ *
+ * Return: true if the timestamping is supported, false otherwise.
+ */
+bool idpf_ptp_get_txq_tstamp_capability(struct idpf_tx_queue *txq)
+{
+ if (!txq || !txq->cached_tstamp_caps)
+ return false;
+ else if (txq->cached_tstamp_caps->access)
+ return true;
+ else
+ return false;
+}
+
/**
* idpf_ptp_init - Initialize PTP hardware clock support
* @adapter: Driver specific private structure
@@ -630,6 +853,9 @@ int idpf_ptp_init(struct idpf_adapter *adapter)
if (err)
goto free_ptp;
+ if (adapter->ptp->get_dev_clk_time_access != IDPF_PTP_NONE)
+ ptp_schedule_worker(adapter->ptp->clock, 0);
+
/* Write the default increment time value if the clock adjustments
* are enabled.
*/
@@ -655,6 +881,9 @@ int idpf_ptp_init(struct idpf_adapter *adapter)
return 0;
remove_clock:
+ if (adapter->ptp->get_dev_clk_time_access != IDPF_PTP_NONE)
+ ptp_cancel_worker_sync(adapter->ptp->clock);
+
ptp_clock_unregister(adapter->ptp->clock);
adapter->ptp->clock = NULL;
@@ -676,11 +905,16 @@ void idpf_ptp_release(struct idpf_adapter *adapter)
if (!ptp)
return;
- if (ptp->tx_tstamp_access != IDPF_PTP_NONE)
+ if (ptp->tx_tstamp_access != IDPF_PTP_NONE &&
+ ptp->get_dev_clk_time_access != IDPF_PTP_NONE)
idpf_ptp_release_tstamp(adapter);
- if (ptp->clock)
+ if (ptp->clock) {
+ if (adapter->ptp->get_dev_clk_time_access != IDPF_PTP_NONE)
+ ptp_cancel_worker_sync(adapter->ptp->clock);
+
ptp_clock_unregister(ptp->clock);
+ }
kfree(ptp);
adapter->ptp = NULL;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.h b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
index f0234f798853..ea533d90f0cf 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
@@ -139,6 +139,7 @@ struct idpf_ptp_tx_tstamp {
* @lock_in_use: the lock to the used latches list
* @lock_free: the lock to free the latches list
* @lock_status: the lock to the status tracker
+ * @access: indicates an access to Tx timestamp
* @latches_free: the list of the free Tx timestamps latches
* @latches_in_use: the list of the used Tx timestamps latches
* @tx_tstamp_status: Tx tstamp status tracker
@@ -150,6 +151,7 @@ struct idpf_ptp_vport_tx_tstamp_caps {
spinlock_t lock_in_use;
spinlock_t lock_free;
spinlock_t lock_status;
+ bool access:1;
struct list_head latches_free;
struct list_head latches_in_use;
struct idpf_ptp_tx_tstamp_status tx_tstamp_status[];
@@ -163,6 +165,8 @@ struct idpf_ptp_vport_tx_tstamp_caps {
* @base_incval: base increment value of the PTP clock
* @max_adj: maximum adjustment of the PTP clock
* @cmd: HW specific command masks
+ * @cached_phc_time: a cached copy of the PHC time for timestamp extension
+ * @cached_phc_jiffies: jiffies when cached_phc_time was last updated
* @dev_clk_regs: the set of registers to access the device clock
* @caps: PTP capabilities negotiated with the Control Plane
* @get_dev_clk_time_access: access type for getting the device clock time
@@ -182,6 +186,8 @@ struct idpf_ptp {
u64 base_incval;
u64 max_adj;
struct idpf_ptp_cmd cmd;
+ u64 cached_phc_time;
+ unsigned long cached_phc_jiffies;
struct idpf_ptp_dev_clk_regs dev_clk_regs;
u32 caps;
enum idpf_ptp_access get_dev_clk_time_access:2;
@@ -244,6 +250,7 @@ int idpf_ptp_init(struct idpf_adapter *adapter);
void idpf_ptp_release(struct idpf_adapter *adapter);
int idpf_ptp_get_caps(struct idpf_adapter *adapter);
void idpf_ptp_get_features_access(const struct idpf_adapter *adapter);
+bool idpf_ptp_get_txq_tstamp_capability(struct idpf_tx_queue *txq);
int idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
struct idpf_ptp_dev_timers *dev_clk_time);
int idpf_ptp_get_cross_time(struct idpf_adapter *adapter,
@@ -252,6 +259,14 @@ int idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, u64 time);
int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, u64 incval);
int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, s64 delta);
int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport);
+int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
+int idpf_ptp_set_timestamp_mode(struct idpf_vport *vport,
+ struct kernel_hwtstamp_config *config);
+u64 idpf_ptp_extend_ts(struct idpf_vport *vport, u64 in_tstamp);
+u64 idpf_ptp_tstamp_extend_32b_to_64b(u64 cached_phc_time, u32 in_timestamp);
+int idpf_ptp_request_ts(struct idpf_tx_queue *tx_q, struct sk_buff *skb,
+ u32 *idx);
+void idpf_tstamp_task(struct work_struct *work);
#else /* CONFIG_PTP_1588_CLOCK */
static inline int idpf_ptp_init(struct idpf_adapter *adapter)
{
@@ -268,6 +283,12 @@ static inline int idpf_ptp_get_caps(struct idpf_adapter *adapter)
static inline void
idpf_ptp_get_features_access(const struct idpf_adapter *adapter) { }
+static inline bool
+idpf_ptp_get_txq_tstamp_capability(struct idpf_tx_queue *txq)
+{
+ return false;
+}
+
static inline int
idpf_ptp_get_dev_clk_time(struct idpf_adapter *adapter,
struct idpf_ptp_dev_timers *dev_clk_time)
@@ -305,5 +326,35 @@ static inline int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
return -EOPNOTSUPP;
}
+static inline int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int
+idpf_ptp_set_timestamp_mode(struct idpf_vport *vport,
+ struct kernel_hwtstamp_config *config)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline u64 idpf_ptp_extend_ts(struct idpf_vport *vport, u32 in_tstamp)
+{
+ return 0;
+}
+
+static inline u64 idpf_ptp_tstamp_extend_32b_to_64b(u64 cached_phc_time,
+ u32 in_timestamp)
+{
+ return 0;
+}
+
+static inline int idpf_ptp_request_ts(struct idpf_tx_queue *tx_q,
+ struct sk_buff *skb, u32 *idx)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void idpf_tstamp_task(struct work_struct *work) { }
#endif /* CONFIG_PTP_1588_CLOCK */
#endif /* _IDPF_PTP_H */
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
index bdf52cef3891..0aa0680e57ad 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
@@ -5,6 +5,7 @@
#include <net/libeth/tx.h>
#include "idpf.h"
+#include "idpf_ptp.h"
#include "idpf_virtchnl.h"
struct idpf_tx_stash {
@@ -1107,6 +1108,8 @@ void idpf_vport_queues_rel(struct idpf_vport *vport)
*/
static int idpf_vport_init_fast_path_txqs(struct idpf_vport *vport)
{
+ struct idpf_ptp_vport_tx_tstamp_caps *caps = vport->tx_tstamp_caps;
+ struct work_struct *tstamp_task = &vport->tstamp_task;
int i, j, k = 0;
vport->txqs = kcalloc(vport->num_txq, sizeof(*vport->txqs),
@@ -1121,6 +1124,12 @@ static int idpf_vport_init_fast_path_txqs(struct idpf_vport *vport)
for (j = 0; j < tx_grp->num_txq; j++, k++) {
vport->txqs[k] = tx_grp->txqs[j];
vport->txqs[k]->idx = k;
+
+ if (!caps)
+ continue;
+
+ vport->txqs[k]->cached_tstamp_caps = caps;
+ vport->txqs[k]->tstamp_task = tstamp_task;
}
}
@@ -1654,6 +1663,40 @@ static void idpf_tx_handle_sw_marker(struct idpf_tx_queue *tx_q)
wake_up(&vport->sw_marker_wq);
}
+/**
+ * idpf_tx_read_tstamp - schedule a work to read Tx timestamp value
+ * @txq: queue to read the timestamp from
+ * @skb: socket buffer to provide Tx timestamp value
+ *
+ * Schedule a work to read Tx timestamp value generated once the packet is
+ * transmitted.
+ */
+static void idpf_tx_read_tstamp(struct idpf_tx_queue *txq, struct sk_buff *skb)
+{
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ struct idpf_ptp_tx_tstamp_status *tx_tstamp_status;
+
+ tx_tstamp_caps = txq->cached_tstamp_caps;
+ spin_lock_bh(&tx_tstamp_caps->lock_status);
+
+ for (u32 i = 0; i < tx_tstamp_caps->num_entries; i++) {
+ tx_tstamp_status = &tx_tstamp_caps->tx_tstamp_status[i];
+ if (tx_tstamp_status->state != IDPF_PTP_FREE)
+ continue;
+
+ tx_tstamp_status->skb = skb;
+ tx_tstamp_status->state = IDPF_PTP_REQUEST;
+
+ /* Fetch timestamp from completion descriptor through
+ * virtchnl msg to report to stack.
+ */
+ queue_work(system_unbound_wq, txq->tstamp_task);
+ break;
+ }
+
+ spin_unlock_bh(&tx_tstamp_caps->lock_status);
+}
+
/**
* idpf_tx_clean_stashed_bufs - clean bufs that were stored for
* out of order completions
@@ -1682,6 +1725,11 @@ static void idpf_tx_clean_stashed_bufs(struct idpf_tx_queue *txq,
continue;
hash_del(&stash->hlist);
+
+ if (stash->buf.type == LIBETH_SQE_SKB &&
+ (skb_shinfo(stash->buf.skb)->tx_flags & SKBTX_IN_PROGRESS))
+ idpf_tx_read_tstamp(txq, stash->buf.skb);
+
libeth_tx_complete(&stash->buf, &cp);
/* Push shadow buf back onto stack */
@@ -1876,8 +1924,12 @@ static bool idpf_tx_clean_buf_ring(struct idpf_tx_queue *txq, u16 compl_tag,
idpf_tx_buf_compl_tag(tx_buf) != compl_tag))
return false;
- if (tx_buf->type == LIBETH_SQE_SKB)
+ if (tx_buf->type == LIBETH_SQE_SKB) {
+ if (skb_shinfo(tx_buf->skb)->tx_flags & SKBTX_IN_PROGRESS)
+ idpf_tx_read_tstamp(txq, tx_buf->skb);
+
libeth_tx_complete(tx_buf, &cp);
+ }
idpf_tx_clean_buf_ring_bump_ntc(txq, idx, tx_buf);
@@ -2127,7 +2179,7 @@ void idpf_tx_splitq_build_flow_desc(union idpf_tx_flex_desc *desc,
struct idpf_tx_splitq_params *params,
u16 td_cmd, u16 size)
{
- desc->flow.qw1.cmd_dtype = (u16)params->dtype | td_cmd;
+ *(u32 *)&desc->flow.qw1.cmd_dtype = (u8)(params->dtype | td_cmd);
desc->flow.qw1.rxr_bufsize = cpu_to_le16((u16)size);
desc->flow.qw1.compl_tag = cpu_to_le16(params->compl_tag);
}
@@ -2296,7 +2348,7 @@ void idpf_tx_dma_map_error(struct idpf_tx_queue *txq, struct sk_buff *skb,
* descriptor. Reset that here.
*/
tx_desc = &txq->flex_tx[idx];
- memset(tx_desc, 0, sizeof(struct idpf_flex_tx_ctx_desc));
+ memset(tx_desc, 0, sizeof(*tx_desc));
if (idx == 0)
idx = txq->desc_count;
idx--;
@@ -2699,10 +2751,10 @@ static bool idpf_chk_linearize(struct sk_buff *skb, unsigned int max_bufs,
* Since the TX buffer rings mimics the descriptor ring, update the tx buffer
* ring entry to reflect that this index is a context descriptor
*/
-static struct idpf_flex_tx_ctx_desc *
+static union idpf_flex_tx_ctx_desc *
idpf_tx_splitq_get_ctx_desc(struct idpf_tx_queue *txq)
{
- struct idpf_flex_tx_ctx_desc *desc;
+ union idpf_flex_tx_ctx_desc *desc;
int i = txq->next_to_use;
txq->tx_buf[i].type = LIBETH_SQE_CTX;
@@ -2732,6 +2784,73 @@ netdev_tx_t idpf_tx_drop_skb(struct idpf_tx_queue *tx_q, struct sk_buff *skb)
return NETDEV_TX_OK;
}
+#if (IS_ENABLED(CONFIG_PTP_1588_CLOCK))
+/**
+ * idpf_tx_tstamp - set up context descriptor for hardware timestamp
+ * @tx_q: queue to send buffer on
+ * @skb: pointer to the SKB we're sending
+ * @off: pointer to the offload struct
+ *
+ * Return: Positive index number on success, negative otherwise.
+ */
+static int idpf_tx_tstamp(struct idpf_tx_queue *tx_q, struct sk_buff *skb,
+ struct idpf_tx_offload_params *off)
+{
+ int err, idx;
+
+ /* only timestamp the outbound packet if the user has requested it */
+ if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)))
+ return -1;
+
+ if (!idpf_ptp_get_txq_tstamp_capability(tx_q))
+ return -1;
+
+ /* Tx timestamps cannot be sampled when doing TSO */
+ if (off->tx_flags & IDPF_TX_FLAGS_TSO)
+ return -1;
+
+ /* Grab an open timestamp slot */
+ err = idpf_ptp_request_ts(tx_q, skb, &idx);
+ if (err) {
+ u64_stats_update_begin(&tx_q->stats_sync);
+ u64_stats_inc(&tx_q->q_stats.tstamp_skipped);
+ u64_stats_update_end(&tx_q->stats_sync);
+
+ return -1;
+ }
+
+ off->tx_flags |= IDPF_TX_FLAGS_TSYN;
+
+ return idx;
+}
+
+/**
+ * idpf_tx_set_tstamp_desc - Set the Tx descriptor fields needed to generate
+ * PHY Tx timestamp
+ * @ctx_desc: Context descriptor
+ * @idx: Index of the Tx timestamp latch
+ */
+static void idpf_tx_set_tstamp_desc(union idpf_flex_tx_ctx_desc *ctx_desc,
+ u32 idx)
+{
+ ctx_desc->tsyn.qw1 = le64_encode_bits(IDPF_TX_DESC_DTYPE_CTX,
+ IDPF_TX_CTX_DTYPE_M) |
+ le64_encode_bits(IDPF_TX_CTX_DESC_TSYN,
+ IDPF_TX_CTX_CMD_M) |
+ le64_encode_bits(idx, IDPF_TX_CTX_TSYN_REG_M);
+}
+#else /* CONFIG_PTP_1588_CLOCK */
+static int idpf_tx_tstamp(struct idpf_tx_queue *tx_q, struct sk_buff *skb,
+ struct idpf_tx_offload_params *off)
+{
+ return -1;
+}
+
+static void idpf_tx_set_tstamp_desc(union idpf_flex_tx_ctx_desc *ctx_desc,
+ u32 idx)
+{ }
+#endif /* CONFIG_PTP_1588_CLOCK */
+
/**
* idpf_tx_splitq_frame - Sends buffer on Tx ring using flex descriptors
* @skb: send buffer
@@ -2743,9 +2862,10 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb,
struct idpf_tx_queue *tx_q)
{
struct idpf_tx_splitq_params tx_params = { };
+ union idpf_flex_tx_ctx_desc *ctx_desc;
struct idpf_tx_buf *first;
unsigned int count;
- int tso;
+ int tso, idx;
count = idpf_tx_desc_count_required(tx_q, skb);
if (unlikely(!count))
@@ -2765,8 +2885,7 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb,
if (tso) {
/* If tso is needed, set up context desc */
- struct idpf_flex_tx_ctx_desc *ctx_desc =
- idpf_tx_splitq_get_ctx_desc(tx_q);
+ ctx_desc = idpf_tx_splitq_get_ctx_desc(tx_q);
ctx_desc->tso.qw1.cmd_dtype =
cpu_to_le16(IDPF_TX_DESC_DTYPE_FLEX_TSO_CTX |
@@ -2784,6 +2903,12 @@ static netdev_tx_t idpf_tx_splitq_frame(struct sk_buff *skb,
u64_stats_update_end(&tx_q->stats_sync);
}
+ idx = idpf_tx_tstamp(tx_q, skb, &tx_params.offload);
+ if (idx != -1) {
+ ctx_desc = idpf_tx_splitq_get_ctx_desc(tx_q);
+ idpf_tx_set_tstamp_desc(ctx_desc, idx);
+ }
+
/* record the location of the first descriptor for this packet */
first = &tx_q->tx_buf[tx_q->next_to_use];
first->skb = skb;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
index b029f566e57c..9b7aa72e1f32 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
@@ -142,6 +142,7 @@ do { \
#define IDPF_TX_FLAGS_IPV4 BIT(1)
#define IDPF_TX_FLAGS_IPV6 BIT(2)
#define IDPF_TX_FLAGS_TUNNEL BIT(3)
+#define IDPF_TX_FLAGS_TSYN BIT(4)
union idpf_tx_flex_desc {
struct idpf_flex_tx_desc q; /* queue based scheduling */
@@ -443,6 +444,7 @@ struct idpf_tx_queue_stats {
u64_stats_t q_busy;
u64_stats_t skb_drops;
u64_stats_t dma_map_errs;
+ u64_stats_t tstamp_skipped;
};
#define IDPF_ITR_DYNAMIC 1
@@ -617,6 +619,8 @@ libeth_cacheline_set_assert(struct idpf_rx_queue, 64,
* @compl_tag_bufid_m: Completion tag buffer id mask
* @compl_tag_cur_gen: Used to keep track of current completion tag generation
* @compl_tag_gen_max: To determine when compl_tag_cur_gen should be reset
+ * @cached_tstamp_caps: Tx timestamp capabilities negotiated with the CP
+ * @tstamp_task: Work that handles Tx timestamp read
* @stats_sync: See struct u64_stats_sync
* @q_stats: See union idpf_tx_queue_stats
* @q_id: Queue id
@@ -630,7 +634,7 @@ struct idpf_tx_queue {
struct idpf_base_tx_desc *base_tx;
struct idpf_base_tx_ctx_desc *base_ctx;
union idpf_tx_flex_desc *flex_tx;
- struct idpf_flex_tx_ctx_desc *flex_ctx;
+ union idpf_flex_tx_ctx_desc *flex_ctx;
void *desc_ring;
};
@@ -666,6 +670,9 @@ struct idpf_tx_queue {
u16 compl_tag_cur_gen;
u16 compl_tag_gen_max;
+ struct idpf_ptp_vport_tx_tstamp_caps *cached_tstamp_caps;
+ struct work_struct *tstamp_task;
+
struct u64_stats_sync stats_sync;
struct idpf_tx_queue_stats q_stats;
__cacheline_group_end_aligned(read_write);
@@ -679,7 +686,7 @@ struct idpf_tx_queue {
__cacheline_group_end_aligned(cold);
};
libeth_cacheline_set_assert(struct idpf_tx_queue, 64,
- 88 + sizeof(struct u64_stats_sync),
+ 112 + sizeof(struct u64_stats_sync),
24);
/**
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index 9f369bbea0fe..f1f6b63bfeb0 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -3176,8 +3176,12 @@ void idpf_vport_init(struct idpf_vport *vport, struct idpf_vport_max_q *max_q)
return;
err = idpf_ptp_get_vport_tstamps_caps(vport);
- if (err)
+ if (err) {
pci_dbg(vport->adapter->pdev, "Tx timestamping not supported\n");
+ return;
+ }
+
+ INIT_WORK(&vport->tstamp_task, idpf_tstamp_task);
}
/**
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
index 71b8e39d20d6..e757de5a1da4 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl_ptp.c
@@ -383,7 +383,9 @@ int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
goto get_tstamp_caps_out;
}
+ tstamp_caps->access = true;
tstamp_caps->num_entries = num_latches;
+
INIT_LIST_HEAD(&tstamp_caps->latches_in_use);
INIT_LIST_HEAD(&tstamp_caps->latches_free);
@@ -426,6 +428,9 @@ int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
vport->tx_tstamp_caps = tstamp_caps;
kfree(rcv_tx_tstamp_caps);
+ /* Init mutex to protect tstamp statistics */
+ mutex_init(&vport->tstamp_stats.tx_hwtstamp_lock);
+
return 0;
err_free_ptp_tx_stamp_list:
@@ -441,3 +446,233 @@ int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
return err;
}
+
+/**
+ * idpf_ptp_update_tstamp_tracker - Update the Tx timestamp tracker based on
+ * the skb compatibility.
+ * @caps: Tx timestamp capabilities that monitor the latch status
+ * @skb: skb for which the tstamp value is returned through virtchnl message
+ * @current_state: Current state of the Tx timestamp latch
+ * @expected_state: Expected state of the Tx timestamp latch
+ *
+ * Find a proper skb tracker for which the Tx timestamp is received and change
+ * the state to expected value.
+ *
+ * Return: true if the tracker has been found and updated, false otherwise.
+ */
+static bool
+idpf_ptp_update_tstamp_tracker(struct idpf_ptp_vport_tx_tstamp_caps *caps,
+ struct sk_buff *skb,
+ enum idpf_ptp_tx_tstamp_state current_state,
+ enum idpf_ptp_tx_tstamp_state expected_state)
+{
+ bool updated = false;
+
+ spin_lock(&caps->lock_status);
+ for (u16 i = 0; i < caps->num_entries; i++) {
+ struct idpf_ptp_tx_tstamp_status *status;
+
+ status = &caps->tx_tstamp_status[i];
+
+ if (skb == status->skb && status->state == current_state) {
+ status->state = expected_state;
+ updated = true;
+ break;
+ }
+ }
+ spin_unlock(&caps->lock_status);
+
+ return updated;
+}
+
+/**
+ * idpf_ptp_get_tstamp_value - Get the Tx timestamp value and provide it
+ * back to the skb.
+ * @vport: Virtual port structure
+ * @tstamp_latch: Tx timestamp latch structure fulfilled by the Control Plane
+ * @ptp_tx_tstamp: Tx timestamp latch to add to the free list
+ *
+ * Read the value of the Tx timestamp for a given latch received from the
+ * Control Plane, extend it to 64 bit and provide back to the skb.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int
+idpf_ptp_get_tstamp_value(struct idpf_vport *vport,
+ struct virtchnl2_ptp_tx_tstamp_latch *tstamp_latch,
+ struct idpf_ptp_tx_tstamp *ptp_tx_tstamp)
+{
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ struct skb_shared_hwtstamps shhwtstamps;
+ bool state_upd = false;
+ u8 tstamp_ns_lo_bit;
+ u64 tstamp;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ ptp_tx_tstamp->tstamp = le64_to_cpu(tstamp_latch->tstamp);
+ ptp_tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
+
+ state_upd = idpf_ptp_update_tstamp_tracker(tx_tstamp_caps,
+ ptp_tx_tstamp->skb,
+ IDPF_PTP_READ_VALUE,
+ IDPF_PTP_FREE);
+ if (!state_upd)
+ return -EINVAL;
+
+ tstamp = idpf_ptp_extend_ts(vport, ptp_tx_tstamp->tstamp);
+ shhwtstamps.hwtstamp = ns_to_ktime(tstamp);
+ skb_tstamp_tx(ptp_tx_tstamp->skb, &shhwtstamps);
+ consume_skb(ptp_tx_tstamp->skb);
+
+ spin_lock(&tx_tstamp_caps->lock_free);
+ list_add(&ptp_tx_tstamp->list_member,
+ &tx_tstamp_caps->latches_free);
+ spin_unlock(&tx_tstamp_caps->lock_free);
+
+ return 0;
+}
+
+/**
+ * idpf_ptp_get_tx_tstamp_async_handler - Async callback for getting Tx tstamps
+ * @adapter: Driver specific private structure
+ * @xn: transaction for message
+ * @ctlq_msg: received message
+ *
+ * Read the tstamps Tx tstamp values from a received message and put them
+ * directly to the skb. The number of timestamps to read is specified by
+ * the virtchnl message.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int
+idpf_ptp_get_tx_tstamp_async_handler(struct idpf_adapter *adapter,
+ struct idpf_vc_xn *xn,
+ const struct idpf_ctlq_msg *ctlq_msg)
+{
+ struct virtchnl2_ptp_get_vport_tx_tstamp_latches *recv_tx_tstamp_msg;
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ struct virtchnl2_ptp_tx_tstamp_latch tstamp_latch;
+ struct idpf_ptp_tx_tstamp *tx_tstamp, *tmp;
+ struct idpf_vport *tstamp_vport = NULL;
+ struct list_head *head;
+ u16 num_latches;
+ u32 vport_id;
+ int err = 0;
+
+ recv_tx_tstamp_msg = ctlq_msg->ctx.indirect.payload->va;
+ vport_id = le32_to_cpu(recv_tx_tstamp_msg->vport_id);
+
+ idpf_for_each_vport(adapter, vport) {
+ if (!vport)
+ continue;
+
+ if (vport->vport_id == vport_id) {
+ tstamp_vport = vport;
+ break;
+ }
+ }
+
+ if (!tstamp_vport || !tstamp_vport->tx_tstamp_caps)
+ return -EINVAL;
+
+ tx_tstamp_caps = tstamp_vport->tx_tstamp_caps;
+ num_latches = le16_to_cpu(recv_tx_tstamp_msg->num_latches);
+
+ head = &tx_tstamp_caps->latches_in_use;
+ spin_lock(&tx_tstamp_caps->lock_in_use);
+
+ for (u16 i = 0; i < num_latches; i++) {
+ tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[i];
+
+ if (!tstamp_latch.valid)
+ continue;
+
+ if (list_empty(head)) {
+ err = -ENOBUFS;
+ goto unlock;
+ }
+
+ list_for_each_entry_safe(tx_tstamp, tmp, head, list_member) {
+ if (tstamp_latch.index == tx_tstamp->idx) {
+ list_del(&tx_tstamp->list_member);
+ err = idpf_ptp_get_tstamp_value(tstamp_vport,
+ &tstamp_latch,
+ tx_tstamp);
+ if (err)
+ goto unlock;
+
+ break;
+ }
+ }
+ }
+
+unlock:
+ spin_unlock(&tx_tstamp_caps->lock_in_use);
+
+ return err;
+}
+
+/**
+ * idpf_ptp_get_tx_tstamp - Send virtchnl get Tx timestamp latches message
+ * @vport: Virtual port structure
+ *
+ * Send virtchnl get Tx tstamp message to read the value of the HW timestamp.
+ * The message contains a list of indexes set in the Tx descriptors.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport)
+{
+ struct virtchnl2_ptp_get_vport_tx_tstamp_latches *send_tx_tstamp_msg;
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ struct idpf_vc_xn_params xn_params = {
+ .vc_op = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP,
+ .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
+ .async = true,
+ .async_handler = idpf_ptp_get_tx_tstamp_async_handler,
+ };
+ struct idpf_ptp_tx_tstamp *ptp_tx_tstamp;
+ int reply_sz, size, msg_size;
+ struct list_head *head;
+ bool state_upd;
+ u16 id = 0;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ head = &tx_tstamp_caps->latches_in_use;
+
+ size = struct_size(send_tx_tstamp_msg, tstamp_latches,
+ tx_tstamp_caps->num_entries);
+ send_tx_tstamp_msg = kzalloc(size, GFP_KERNEL);
+ if (!send_tx_tstamp_msg)
+ return -ENOMEM;
+
+ spin_lock(&tx_tstamp_caps->lock_in_use);
+ list_for_each_entry(ptp_tx_tstamp, head, list_member) {
+ u8 idx;
+
+ state_upd = idpf_ptp_update_tstamp_tracker(tx_tstamp_caps,
+ ptp_tx_tstamp->skb,
+ IDPF_PTP_REQUEST,
+ IDPF_PTP_READ_VALUE);
+ if (!state_upd)
+ continue;
+
+ idx = ptp_tx_tstamp->idx;
+ send_tx_tstamp_msg->tstamp_latches[id].index = idx;
+ id++;
+ }
+ spin_unlock(&tx_tstamp_caps->lock_in_use);
+
+ msg_size = struct_size(send_tx_tstamp_msg, tstamp_latches, id);
+ send_tx_tstamp_msg->vport_id = cpu_to_le32(vport->vport_id);
+ send_tx_tstamp_msg->num_latches = cpu_to_le16(id);
+ xn_params.send_buf.iov_base = send_tx_tstamp_msg;
+ xn_params.send_buf.iov_len = msg_size;
+
+ reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);
+ kfree(send_tx_tstamp_msg);
+
+ return min(reply_sz, 0);
+}
--
2.43.5
^ permalink raw reply related [flat|nested] 35+ messages in thread
* [PATCH v10 iwl-next 11/11] idpf: add support for Rx timestamping
2025-04-08 10:30 [PATCH v10 iwl-next 00/11] idpf: add initial PTP support Milena Olech
` (9 preceding siblings ...)
2025-04-08 10:31 ` [PATCH v10 iwl-next 10/11] idpf: add Tx timestamp flows Milena Olech
@ 2025-04-08 10:31 ` Milena Olech
2025-04-08 21:31 ` [Intel-wired-lan] " Jacob Keller
10 siblings, 1 reply; 35+ messages in thread
From: Milena Olech @ 2025-04-08 10:31 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Milena Olech,
YiFei Zhu, Mina Almasry, Samuel Salin
Add Rx timestamp function when the Rx timestamp value is read directly
from the Rx descriptor. In order to extend the Rx timestamp value to 64
bit in hot path, the PHC time is cached in the receive groups.
Add supported Rx timestamp modes.
Signed-off-by: Milena Olech <milena.olech@intel.com>
Tested-by: YiFei Zhu <zhuyifei@google.com>
Tested-by: Mina Almasry <almasrymina@google.com>
Tested-by: Samuel Salin <Samuel.salin@intel.com>
---
v8 -> v9: upscale Rx filters to HWTSTAMP_FILTER_ALL if Rx filter is
different than HWTSTAMP_FILTER_NONE
v7 -> v8: add a function to check if the Rx timestamp for a given vport
is enabled
v5 -> v6: add Rx filter
v2 -> v3: add disable Rx timestamp
v1 -> v2: extend commit message
.../net/ethernet/intel/idpf/idpf_ethtool.c | 1 +
drivers/net/ethernet/intel/idpf/idpf_lib.c | 6 +-
drivers/net/ethernet/intel/idpf/idpf_ptp.c | 86 ++++++++++++++++++-
drivers/net/ethernet/intel/idpf/idpf_ptp.h | 21 +++++
drivers/net/ethernet/intel/idpf/idpf_txrx.c | 30 +++++++
drivers/net/ethernet/intel/idpf/idpf_txrx.h | 7 +-
6 files changed, 147 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c
index ec4183a609c4..7a4793749bc5 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c
@@ -1333,6 +1333,7 @@ static void idpf_get_timestamp_filters(const struct idpf_vport *vport,
SOF_TIMESTAMPING_RAW_HARDWARE;
info->tx_types = BIT(HWTSTAMP_TX_OFF);
+ info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
if (!vport->tx_tstamp_caps ||
vport->adapter->ptp->tx_tstamp_access == IDPF_PTP_NONE)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c
index ccceda18c392..db53c9c9a5e5 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c
@@ -2350,7 +2350,8 @@ static int idpf_hwtstamp_set(struct net_device *netdev,
idpf_vport_ctrl_lock(netdev);
vport = idpf_netdev_to_vport(netdev);
- if (!idpf_ptp_is_vport_tx_tstamp_ena(vport)) {
+ if (!idpf_ptp_is_vport_tx_tstamp_ena(vport) &&
+ !idpf_ptp_is_vport_rx_tstamp_ena(vport)) {
idpf_vport_ctrl_unlock(netdev);
return -EOPNOTSUPP;
}
@@ -2370,7 +2371,8 @@ static int idpf_hwtstamp_get(struct net_device *netdev,
idpf_vport_ctrl_lock(netdev);
vport = idpf_netdev_to_vport(netdev);
- if (!idpf_ptp_is_vport_tx_tstamp_ena(vport)) {
+ if (!idpf_ptp_is_vport_tx_tstamp_ena(vport) &&
+ !idpf_ptp_is_vport_rx_tstamp_ena(vport)) {
idpf_vport_ctrl_unlock(netdev);
return 0;
}
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
index 63c51684f60d..5be816bd4714 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
@@ -328,12 +328,41 @@ static int idpf_ptp_gettimex64(struct ptp_clock_info *info,
return 0;
}
+/**
+ * idpf_ptp_update_phctime_rxq_grp - Update the cached PHC time for a given Rx
+ * queue group.
+ * @grp: receive queue group in which Rx timestamp is enabled
+ * @split: Indicates whether the queue model is split or single queue
+ * @systime: Cached system time
+ */
+static void
+idpf_ptp_update_phctime_rxq_grp(const struct idpf_rxq_group *grp, bool split,
+ u64 systime)
+{
+ struct idpf_rx_queue *rxq;
+ u16 i;
+
+ if (!split) {
+ for (i = 0; i < grp->singleq.num_rxq; i++) {
+ rxq = grp->singleq.rxqs[i];
+ if (rxq)
+ WRITE_ONCE(rxq->cached_phc_time, systime);
+ }
+ } else {
+ for (i = 0; i < grp->splitq.num_rxq_sets; i++) {
+ rxq = &grp->splitq.rxq_sets[i]->rxq;
+ if (rxq)
+ WRITE_ONCE(rxq->cached_phc_time, systime);
+ }
+ }
+}
+
/**
* idpf_ptp_update_cached_phctime - Update the cached PHC time values
* @adapter: Driver specific private structure
*
* This function updates the system time values which are cached in the adapter
- * structure.
+ * structure and the Rx queues.
*
* This function must be called periodically to ensure that the cached value
* is never more than 2 seconds old.
@@ -356,6 +385,21 @@ static int idpf_ptp_update_cached_phctime(struct idpf_adapter *adapter)
WRITE_ONCE(adapter->ptp->cached_phc_time, systime);
WRITE_ONCE(adapter->ptp->cached_phc_jiffies, jiffies);
+ idpf_for_each_vport(adapter, vport) {
+ bool split;
+
+ if (!vport || !vport->rxq_grps)
+ continue;
+
+ split = idpf_is_queue_model_split(vport->rxq_model);
+
+ for (u16 i = 0; i < vport->num_rxq_grp; i++) {
+ struct idpf_rxq_group *grp = &vport->rxq_grps[i];
+
+ idpf_ptp_update_phctime_rxq_grp(grp, split, systime);
+ }
+ }
+
return 0;
}
@@ -616,6 +660,45 @@ int idpf_ptp_request_ts(struct idpf_tx_queue *tx_q, struct sk_buff *skb,
return 0;
}
+/**
+ * idpf_ptp_set_rx_tstamp - Enable or disable Rx timestamping
+ * @vport: Virtual port structure
+ * @rx_filter: bool value for whether timestamps are enabled or disabled
+ */
+static void idpf_ptp_set_rx_tstamp(struct idpf_vport *vport, int rx_filter)
+{
+ bool enable = true, splitq;
+
+ vport->tstamp_config.rx_filter = rx_filter;
+ splitq = idpf_is_queue_model_split(vport->rxq_model);
+
+ if (rx_filter == HWTSTAMP_FILTER_NONE)
+ enable = false;
+
+ for (u16 i = 0; i < vport->num_rxq_grp; i++) {
+ struct idpf_rxq_group *grp = &vport->rxq_grps[i];
+ struct idpf_rx_queue *rx_queue;
+ u16 j, num_rxq;
+
+ if (splitq)
+ num_rxq = grp->splitq.num_rxq_sets;
+ else
+ num_rxq = grp->singleq.num_rxq;
+
+ for (j = 0; j < num_rxq; j++) {
+ if (splitq)
+ rx_queue = &grp->splitq.rxq_sets[j]->rxq;
+ else
+ rx_queue = grp->singleq.rxqs[j];
+
+ if (enable)
+ idpf_queue_set(PTP, rx_queue);
+ else
+ idpf_queue_clear(PTP, rx_queue);
+ }
+ }
+}
+
/**
* idpf_ptp_set_timestamp_mode - Setup driver for requested timestamp mode
* @vport: Virtual port structure
@@ -638,6 +721,7 @@ int idpf_ptp_set_timestamp_mode(struct idpf_vport *vport,
}
vport->tstamp_config.tx_type = config->tx_type;
+ idpf_ptp_set_rx_tstamp(vport, config->rx_filter);
return 0;
}
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.h b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
index ea533d90f0cf..a05364ae9031 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.h
@@ -245,6 +245,27 @@ static inline bool idpf_ptp_is_vport_tx_tstamp_ena(struct idpf_vport *vport)
return true;
}
+/**
+ * idpf_ptp_is_vport_rx_tstamp_ena - Verify the Rx timestamping enablement for
+ * a given vport.
+ * @vport: Virtual port structure
+ *
+ * Rx timestamp feature is enabled if the PTP clock for the adapter is created
+ * and it is possible to read the value of the device clock. The second
+ * assumption comes from the need to extend the Rx timestamp value to 64 bit
+ * based on the current device clock time.
+ *
+ * Return: true if the Rx timestamping is enabled, false otherwise.
+ */
+static inline bool idpf_ptp_is_vport_rx_tstamp_ena(struct idpf_vport *vport)
+{
+ if (!vport->adapter->ptp ||
+ vport->adapter->ptp->get_dev_clk_time_access == IDPF_PTP_NONE)
+ return false;
+ else
+ return true;
+}
+
#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
int idpf_ptp_init(struct idpf_adapter *adapter);
void idpf_ptp_release(struct idpf_adapter *adapter);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
index 0aa0680e57ad..5137e9d15adb 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
@@ -3170,6 +3170,33 @@ static int idpf_rx_rsc(struct idpf_rx_queue *rxq, struct sk_buff *skb,
return 0;
}
+/**
+ * idpf_rx_hwtstamp - check for an RX timestamp and pass up the stack
+ * @rxq: pointer to the rx queue that receives the timestamp
+ * @rx_desc: pointer to rx descriptor containing timestamp
+ * @skb: skb to put timestamp in
+ */
+static void
+idpf_rx_hwtstamp(const struct idpf_rx_queue *rxq,
+ const struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc,
+ struct sk_buff *skb)
+{
+ u64 cached_time, ts_ns;
+ u32 ts_high;
+
+ if (!(rx_desc->ts_low & VIRTCHNL2_RX_FLEX_TSTAMP_VALID))
+ return;
+
+ cached_time = READ_ONCE(rxq->cached_phc_time);
+
+ ts_high = le32_to_cpu(rx_desc->ts_high);
+ ts_ns = idpf_ptp_tstamp_extend_32b_to_64b(cached_time, ts_high);
+
+ *skb_hwtstamps(skb) = (struct skb_shared_hwtstamps) {
+ .hwtstamp = ns_to_ktime(ts_ns),
+ };
+}
+
/**
* idpf_rx_process_skb_fields - Populate skb header fields from Rx descriptor
* @rxq: Rx descriptor ring packet is being transacted on
@@ -3195,6 +3222,9 @@ idpf_rx_process_skb_fields(struct idpf_rx_queue *rxq, struct sk_buff *skb,
/* process RSS/hash */
idpf_rx_hash(rxq, skb, rx_desc, decoded);
+ if (idpf_queue_has(PTP, rxq))
+ idpf_rx_hwtstamp(rxq, rx_desc, skb);
+
skb->protocol = eth_type_trans(skb, rxq->netdev);
skb_record_rx_queue(skb, rxq->idx);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
index 9b7aa72e1f32..c779fe71df99 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
@@ -290,6 +290,8 @@ struct idpf_ptype_state {
* @__IDPF_Q_POLL_MODE: Enable poll mode
* @__IDPF_Q_CRC_EN: enable CRC offload in singleq mode
* @__IDPF_Q_HSPLIT_EN: enable header split on Rx (splitq)
+ * @__IDPF_Q_PTP: indicates whether the Rx timestamping is enabled for the
+ * queue
* @__IDPF_Q_FLAGS_NBITS: Must be last
*/
enum idpf_queue_flags_t {
@@ -300,6 +302,7 @@ enum idpf_queue_flags_t {
__IDPF_Q_POLL_MODE,
__IDPF_Q_CRC_EN,
__IDPF_Q_HSPLIT_EN,
+ __IDPF_Q_PTP,
__IDPF_Q_FLAGS_NBITS,
};
@@ -496,6 +499,7 @@ struct idpf_txq_stash {
* @next_to_alloc: RX buffer to allocate at
* @skb: Pointer to the skb
* @truesize: data buffer truesize in singleq
+ * @cached_phc_time: Cached PHC time for the Rx queue
* @stats_sync: See struct u64_stats_sync
* @q_stats: See union idpf_rx_queue_stats
* @q_id: Queue id
@@ -543,6 +547,7 @@ struct idpf_rx_queue {
struct sk_buff *skb;
u32 truesize;
+ u64 cached_phc_time;
struct u64_stats_sync stats_sync;
struct idpf_rx_queue_stats q_stats;
@@ -562,7 +567,7 @@ struct idpf_rx_queue {
__cacheline_group_end_aligned(cold);
};
libeth_cacheline_set_assert(struct idpf_rx_queue, 64,
- 80 + sizeof(struct u64_stats_sync),
+ 88 + sizeof(struct u64_stats_sync),
32);
/**
--
2.43.5
^ permalink raw reply related [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 01/11] idpf: change the method for mailbox workqueue allocation
2025-04-08 10:30 ` [PATCH v10 iwl-next 01/11] idpf: change the method for mailbox workqueue allocation Milena Olech
@ 2025-04-08 21:01 ` Jacob Keller
0 siblings, 0 replies; 35+ messages in thread
From: Jacob Keller @ 2025-04-08 21:01 UTC (permalink / raw)
To: Milena Olech, intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Alexander Lobakin,
Samuel Salin
On 4/8/2025 3:30 AM, Milena Olech wrote:
> Since workqueues are created per CPU, the works scheduled to this
> workqueues are run on the CPU they were assigned. It may result in
> overloaded CPU that is not able to handle virtchnl messages in
> relatively short time. Allocating workqueue with WQ_UNBOUND and
> WQ_HIGHPRI flags allows scheduler to queue virtchl messages on less loaded
> CPUs, what eliminates delays.
>
> Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
> Signed-off-by: Milena Olech <milena.olech@intel.com>
> Tested-by: Samuel Salin <Samuel.salin@intel.com>
> ---
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 02/11] idpf: add initial PTP support
2025-04-08 10:30 ` [PATCH v10 iwl-next 02/11] idpf: add initial PTP support Milena Olech
@ 2025-04-08 21:02 ` Jacob Keller
0 siblings, 0 replies; 35+ messages in thread
From: Jacob Keller @ 2025-04-08 21:02 UTC (permalink / raw)
To: Milena Olech, intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Alexander Lobakin,
Vadim Fedorenko, Willem de Bruijn, Mina Almasry, Samuel Salin
On 4/8/2025 3:30 AM, Milena Olech wrote:
> PTP feature is supported if the VIRTCHNL2_CAP_PTP is negotiated during the
> capabilities recognition. Initial PTP support includes PTP initialization
> and registration of the clock.
>
> Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
> Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
> Reviewed-by: Willem de Bruijn <willemb@google.com>
> Signed-off-by: Milena Olech <milena.olech@intel.com>
> Tested-by: Mina Almasry <almasrymina@google.com>
> Tested-by: Samuel Salin <Samuel.salin@intel.com>
> ---
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 03/11] virtchnl: add PTP virtchnl definitions
2025-04-08 10:30 ` [PATCH v10 iwl-next 03/11] virtchnl: add PTP virtchnl definitions Milena Olech
@ 2025-04-08 21:12 ` Jacob Keller
2025-04-09 11:51 ` Olech, Milena
0 siblings, 1 reply; 35+ messages in thread
From: Jacob Keller @ 2025-04-08 21:12 UTC (permalink / raw)
To: Milena Olech, intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Alexander Lobakin,
Willem de Bruijn, Mina Almasry, Samuel Salin
On 4/8/2025 3:30 AM, Milena Olech wrote:
> PTP capabilities are negotiated using virtchnl commands. There are two
> available modes of the PTP support: direct and mailbox. When the direct
> access to PTP resources is negotiated, virtchnl messages returns a set
> of registers that allow read/write directly. When the mailbox access to
> PTP resources is negotiated, virtchnl messages are used to access
> PTP clock and to read the timestamp values.
>
> Virtchnl API covers both modes and exposes a set of PTP capabilities.
>
> Using virtchnl API, the driver recognizes also HW abilities - maximum
> adjustment of the clock and the basic increment value.
>
> Additionally, API allows to configure the secondary mailbox, dedicated
> exclusively for PTP purposes.
>
> Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
> Reviewed-by: Willem de Bruijn <willemb@google.com>
> Signed-off-by: Milena Olech <milena.olech@intel.com>
> Tested-by: Mina Almasry <almasrymina@google.com>
> Tested-by: Samuel Salin <Samuel.salin@intel.com>
Couple of comments, but no real objection. I think the decisions here
are acceptable trade offs.
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
> +/**
> + * struct virtchnl2_ptp_get_cross_time: Associated with message
> + * VIRTCHNL2_OP_PTP_GET_CROSS_TIME.
> + * @sys_time_ns: System counter value expressed in nanoseconds, read
> + * synchronously with device time
> + * @dev_time_ns: Device clock time value expressed in nanoseconds
> + *
> + * PF/VF sends this message to receive the cross time.
> + */
> +struct virtchnl2_ptp_get_cross_time {
> + __le64 sys_time_ns;
> + __le64 dev_time_ns;
> +};
These are in nano seconds, and there's no room left for extension in the
structure.. However, 64bits of nanoseconds is 584 years give or take.
Even if we start from the Unix epoch thats a pretty long way in the
future. Additionally, it is likely that some sort of software-based
rollover could be used since the roll-over period would be hundreds of
years. Ok. I don't think we need to waste additional space for extension
here. This also applies to the other __le64 fields with nanosecond time.
> +VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_cross_time);
> +
> +/**
> + * struct virtchnl2_ptp_set_dev_clk_time: Associated with message
> + * VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME.
> + * @dev_time_ns: Device time value expressed in nanoseconds to set
> + *
> + * PF/VF sends this message to set the time of the main timer.
> + */
> +struct virtchnl2_ptp_set_dev_clk_time {
> + __le64 dev_time_ns;
> +};
> +VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_set_dev_clk_time);
> +
> +/**
> + * struct virtchnl2_ptp_adj_dev_clk_fine: Associated with message
> + * VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE.
> + * @incval: Source timer increment value per clock cycle
> + *
> + * PF/VF sends this message to adjust the frequency of the main timer by the
> + * indicated scaled ppm.
> + */
Do we want to encode scaled_ppm here in the virtchnl interface? I
suppose its not that big a deal but it is kind of an implementation
quirk of the Linux APIs. We could use parts per trillion or something
similar..
I suppose there is little value in translating from scaled_ppm to some
other format, due to accumulated error, and scaled_ppm is higher
precision than ppb. Ok.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 04/11] idpf: move virtchnl structures to the header file
2025-04-08 10:30 ` [PATCH v10 iwl-next 04/11] idpf: move virtchnl structures to the header file Milena Olech
@ 2025-04-08 21:12 ` Jacob Keller
0 siblings, 0 replies; 35+ messages in thread
From: Jacob Keller @ 2025-04-08 21:12 UTC (permalink / raw)
To: Milena Olech, intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Alexander Lobakin,
Willem de Bruijn, Mina Almasry, Samuel Salin
On 4/8/2025 3:30 AM, Milena Olech wrote:
> Move virtchnl structures to the header file to expose them for the PTP
> virtchnl file.
>
> Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
> Reviewed-by: Willem de Bruijn <willemb@google.com>
> Signed-off-by: Milena Olech <milena.olech@intel.com>
> Tested-by: Mina Almasry <almasrymina@google.com>
> Tested-by: Samuel Salin <Samuel.salin@intel.com>
> ---
> v1 -> v2: fix commit message title
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 05/11] idpf: negotiate PTP capabilities and get PTP clock
2025-04-08 10:30 ` [PATCH v10 iwl-next 05/11] idpf: negotiate PTP capabilities and get PTP clock Milena Olech
@ 2025-04-08 21:15 ` Jacob Keller
2025-04-09 12:55 ` Olech, Milena
0 siblings, 1 reply; 35+ messages in thread
From: Jacob Keller @ 2025-04-08 21:15 UTC (permalink / raw)
To: Milena Olech, intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Willem de Bruijn,
Mina Almasry, Samuel Salin
On 4/8/2025 3:30 AM, Milena Olech wrote:
> +static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter,
> + struct ptp_system_timestamp *sts)
> +{
> + struct idpf_ptp *ptp = adapter->ptp;
> + u32 hi, lo;
> +
> + spin_lock(&ptp->read_dev_clk_lock);
> +
> + /* Read the system timestamp pre PHC read */
> + ptp_read_system_prets(sts);
> +
> + idpf_ptp_enable_shtime(adapter);
> +
> + /* Read the system timestamp post PHC read */
> + ptp_read_system_postts(sts);
> +
> + lo = readl(ptp->dev_clk_regs.dev_clk_ns_l);
> + hi = readl(ptp->dev_clk_regs.dev_clk_ns_h);
> +
> + spin_unlock(&ptp->read_dev_clk_lock);
> +
> + return ((u64)hi << 32) | lo;
> +}
v9 had comments regarding the latching of the registers for direct
access. Can you confirm whether this is known to be safe, or if you need
to implement a 3-part read like we do in ice and other hardware? Even
with a spinlock there could be issues with rollover in the hardware I think?
Thanks,
Jake
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 06/11] idpf: add mailbox access to read PTP clock time
2025-04-08 10:30 ` [PATCH v10 iwl-next 06/11] idpf: add mailbox access to read PTP clock time Milena Olech
@ 2025-04-08 21:16 ` Jacob Keller
2025-04-09 13:01 ` Olech, Milena
2025-04-08 21:17 ` Jacob Keller
1 sibling, 1 reply; 35+ messages in thread
From: Jacob Keller @ 2025-04-08 21:16 UTC (permalink / raw)
To: Milena Olech, intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Alexander Lobakin,
Willem de Bruijn, Samuel Salin
On 4/8/2025 3:30 AM, Milena Olech wrote:
> +static int idpf_ptp_read_src_clk_reg_mailbox(struct idpf_adapter *adapter,
> + struct ptp_system_timestamp *sts,
> + u64 *src_clk)
> +{
> + struct idpf_ptp_dev_timers clk_time;
> + int err;
> +
> + /* Read the system timestamp pre PHC read */
> + ptp_read_system_prets(sts);
> +
> + err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
> + if (err)
> + return err;
> +
> + /* Read the system timestamp post PHC read */
> + ptp_read_system_postts(sts);
> +
> + *src_clk = clk_time.dev_clk_time_ns;
> +
> + return 0;
> +}
Not an objection, since its obvious using the direct register access is
preferable when it is available. This will result in a fair amount of
jitter since mailbox timing is unlikely to be consistent. We also cannot
have sts be filled in somehow by the host because it might not be
operating on the same system clock.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 06/11] idpf: add mailbox access to read PTP clock time
2025-04-08 10:30 ` [PATCH v10 iwl-next 06/11] idpf: add mailbox access to read PTP clock time Milena Olech
2025-04-08 21:16 ` [Intel-wired-lan] " Jacob Keller
@ 2025-04-08 21:17 ` Jacob Keller
1 sibling, 0 replies; 35+ messages in thread
From: Jacob Keller @ 2025-04-08 21:17 UTC (permalink / raw)
To: Milena Olech, intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Alexander Lobakin,
Willem de Bruijn, Samuel Salin
On 4/8/2025 3:30 AM, Milena Olech wrote:
> When the access to read PTP clock is specified as mailbox, the driver
> needs to send virtchnl message to perform PTP actions. Message is sent
> using idpf_mbq_opc_send_msg_to_peer_drv mailbox opcode, with the parameters
> received during PTP capabilities negotiation.
>
> Add functions to recognize PTP messages, move them to dedicated secondary
> mailbox, read the PTP clock time and cross timestamp using mailbox
> messages.
>
> Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
> Reviewed-by: Willem de Bruijn <willemb@google.com>
> Signed-off-by: Milena Olech <milena.olech@intel.com>
> Tested-by: Samuel Salin <Samuel.salin@intel.com>
> ---
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 07/11] idpf: add cross timestamping
2025-04-08 10:31 ` [PATCH v10 iwl-next 07/11] idpf: add cross timestamping Milena Olech
@ 2025-04-08 21:18 ` Jacob Keller
0 siblings, 0 replies; 35+ messages in thread
From: Jacob Keller @ 2025-04-08 21:18 UTC (permalink / raw)
To: Milena Olech, intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel
On 4/8/2025 3:31 AM, Milena Olech wrote:
> Add cross timestamp support through virtchnl mailbox messages and directly,
> through PCIe BAR registers. Cross timestamping assumes that both system
> time and device clock time values are cached simultaneously, what is
> triggered by HW. Feature is enabled for both ARM and x86 archs.
>
> Signed-off-by: Milena Olech <milena.olech@intel.com>
> ---
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 08/11] idpf: add PTP clock configuration
2025-04-08 10:31 ` [PATCH v10 iwl-next 08/11] idpf: add PTP clock configuration Milena Olech
@ 2025-04-08 21:20 ` Jacob Keller
0 siblings, 0 replies; 35+ messages in thread
From: Jacob Keller @ 2025-04-08 21:20 UTC (permalink / raw)
To: Milena Olech, intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Alexander Lobakin,
Willem de Bruijn, Mina Almasry, Samuel Salin
On 4/8/2025 3:31 AM, Milena Olech wrote:
> PTP clock configuration operations - set time, adjust time and adjust
> frequency are required to control the clock and maintain synchronization
> process.
>
> Extend get PTP capabilities function to request for the clock adjustments
> and add functions to enable these actions using dedicated virtchnl
> messages.
>
> Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
> Reviewed-by: Willem de Bruijn <willemb@google.com>
> Signed-off-by: Milena Olech <milena.olech@intel.com>
> Tested-by: Mina Almasry <almasrymina@google.com>
> Tested-by: Samuel Salin <Samuel.salin@intel.com>
> ---
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 09/11] idpf: add Tx timestamp capabilities negotiation
2025-04-08 10:31 ` [PATCH v10 iwl-next 09/11] idpf: add Tx timestamp capabilities negotiation Milena Olech
@ 2025-04-08 21:23 ` Jacob Keller
2025-04-09 14:04 ` Olech, Milena
0 siblings, 1 reply; 35+ messages in thread
From: Jacob Keller @ 2025-04-08 21:23 UTC (permalink / raw)
To: Milena Olech, intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Alexander Lobakin,
Emil Tantilov, Pavan Kumar Linga, Samuel Salin
On 4/8/2025 3:31 AM, Milena Olech wrote:
> +static void idpf_ptp_release_vport_tstamp(struct idpf_vport *vport)
> +{
> + struct idpf_ptp_tx_tstamp *ptp_tx_tstamp, *tmp;
> + struct list_head *head;
> +
> + /* Remove list with free latches */
> + spin_lock(&vport->tx_tstamp_caps->lock_free);
> +
> + head = &vport->tx_tstamp_caps->latches_free;
> + list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
> + list_del(&ptp_tx_tstamp->list_member);
> + kfree(ptp_tx_tstamp);
> + }
> +
> + spin_unlock(&vport->tx_tstamp_caps->lock_free);
> +
> + /* Remove list with latches in use */
> + spin_lock(&vport->tx_tstamp_caps->lock_in_use);
> +
> + head = &vport->tx_tstamp_caps->latches_in_use;
> + list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
> + list_del(&ptp_tx_tstamp->list_member);
> + kfree(ptp_tx_tstamp);
> + }
> +
> + spin_unlock(&vport->tx_tstamp_caps->lock_in_use);
> +
> + kfree(vport->tx_tstamp_caps);
> + vport->tx_tstamp_caps = NULL;
> +}
Could you provide a summary and overview of the locking scheme used
here? I see you have multiple spin locks for both the free bits and the
in-use bits, and its a bit hard to grasp the reasoning behind this. We
had a lot of issues getting locking for Tx timestamps correct in ice,
though most of that had to do with quirks in the hardware.
Thanks,
Jake
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 10/11] idpf: add Tx timestamp flows
2025-04-08 10:31 ` [PATCH v10 iwl-next 10/11] idpf: add Tx timestamp flows Milena Olech
@ 2025-04-08 21:31 ` Jacob Keller
2025-04-10 13:28 ` Olech, Milena
0 siblings, 1 reply; 35+ messages in thread
From: Jacob Keller @ 2025-04-08 21:31 UTC (permalink / raw)
To: Milena Olech, intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, Josh Hay,
Samuel Salin
On 4/8/2025 3:31 AM, Milena Olech wrote:
> +/**
> + * idpf_ptp_tstamp_extend_32b_to_64b - Convert a 32b nanoseconds Tx or Rx
> + * timestamp value to 64b.
> + * @cached_phc_time: recently cached copy of PHC time
> + * @in_timestamp: Ingress/egress 32b nanoseconds timestamp value
> + *
> + * Hardware captures timestamps which contain only 32 bits of nominal
> + * nanoseconds, as opposed to the 64bit timestamps that the stack expects.
> + *
> + * Return: Tx timestamp value extended to 64 bits based on cached PHC time.
> + */
> +u64 idpf_ptp_tstamp_extend_32b_to_64b(u64 cached_phc_time, u32 in_timestamp)
> +{
> + s64 delta;
> +
> + delta = in_timestamp - lower_32_bits(cached_phc_time);
> +
> + return cached_phc_time + delta;
> +}
This logic looks quite different from what we did in ice and iavf, which
was based on the math from timecounters. It looks like you do check if
the value is too old which is good to verify. Perhaps I'm just
misunderstanding the math.
For clarity, here's what we have in ice:
> static u64 ice_ptp_extend_32b_ts(u64 cached_phc_time, u32 in_tstamp)
> {
> u32 delta, phc_time_lo;
> u64 ns;
>
> /* Extract the lower 32 bits of the PHC time */
> phc_time_lo = (u32)cached_phc_time;
>
> /* Calculate the delta between the lower 32bits of the cached PHC
> * time and the in_tstamp value
> */
> delta = (in_tstamp - phc_time_lo);
>
> /* Do not assume that the in_tstamp is always more recent than the
> * cached PHC time. If the delta is large, it indicates that the
> * in_tstamp was taken in the past, and should be converted
> * forward.
> */
> if (delta > (U32_MAX / 2)) {
> /* reverse the delta calculation here */
> delta = (phc_time_lo - in_tstamp);
> ns = cached_phc_time - delta;
> } else {
> ns = cached_phc_time + delta;
> }
>
> return ns;
> }
In particular, this ensures that we correctly handle the case where a
timestamp is captured just before an update to the cached PHC time.
Without that check, you can't guarantee that the timestamp is updated
correctly with lockess PHC updating.
With these checks, as long as the timestamp is recent, we can extend it
safely without worrying about whether the cached PHC time we are using
is slightly old or not. (As long as its no more than 2 seconds old).
Could you explain why this was changed for idpf?
Bonus points if we extracted this method into libie/libeth and shared it
across ice, idpf, and iavf, which I believe recently gained support for
timestamping as well.
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 11/11] idpf: add support for Rx timestamping
2025-04-08 10:31 ` [PATCH v10 iwl-next 11/11] idpf: add support for Rx timestamping Milena Olech
@ 2025-04-08 21:31 ` Jacob Keller
0 siblings, 0 replies; 35+ messages in thread
From: Jacob Keller @ 2025-04-08 21:31 UTC (permalink / raw)
To: Milena Olech, intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, YiFei Zhu,
Mina Almasry, Samuel Salin
On 4/8/2025 3:31 AM, Milena Olech wrote:
> Add Rx timestamp function when the Rx timestamp value is read directly
> from the Rx descriptor. In order to extend the Rx timestamp value to 64
> bit in hot path, the PHC time is cached in the receive groups.
> Add supported Rx timestamp modes.
>
> Signed-off-by: Milena Olech <milena.olech@intel.com>
> Tested-by: YiFei Zhu <zhuyifei@google.com>
> Tested-by: Mina Almasry <almasrymina@google.com>
> Tested-by: Samuel Salin <Samuel.salin@intel.com>
> ---
Besides my question in 10/11 about the timestamp extension logic, this
looks ok to me.
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
^ permalink raw reply [flat|nested] 35+ messages in thread
* RE: [Intel-wired-lan] [PATCH v10 iwl-next 03/11] virtchnl: add PTP virtchnl definitions
2025-04-08 21:12 ` [Intel-wired-lan] " Jacob Keller
@ 2025-04-09 11:51 ` Olech, Milena
2025-04-09 18:11 ` Jacob Keller
0 siblings, 1 reply; 35+ messages in thread
From: Olech, Milena @ 2025-04-09 11:51 UTC (permalink / raw)
To: Keller, Jacob E, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Nguyen, Anthony L, Kitszel, Przemyslaw,
Lobakin, Aleksander, Willem de Bruijn, Mina Almasry,
Salin, Samuel
On 4/8/2025 11:12 PM, Jacob Keller wrote:
>On 4/8/2025 3:30 AM, Milena Olech wrote:
>> PTP capabilities are negotiated using virtchnl commands. There are two
>> available modes of the PTP support: direct and mailbox. When the direct
>> access to PTP resources is negotiated, virtchnl messages returns a set
>> of registers that allow read/write directly. When the mailbox access to
>> PTP resources is negotiated, virtchnl messages are used to access
>> PTP clock and to read the timestamp values.
>>
>> Virtchnl API covers both modes and exposes a set of PTP capabilities.
>>
>> Using virtchnl API, the driver recognizes also HW abilities - maximum
>> adjustment of the clock and the basic increment value.
>>
>> Additionally, API allows to configure the secondary mailbox, dedicated
>> exclusively for PTP purposes.
>>
>> Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
>> Reviewed-by: Willem de Bruijn <willemb@google.com>
>> Signed-off-by: Milena Olech <milena.olech@intel.com>
>> Tested-by: Mina Almasry <almasrymina@google.com>
>> Tested-by: Samuel Salin <Samuel.salin@intel.com>
>
>
>Couple of comments, but no real objection. I think the decisions here
>are acceptable trade offs.
>
>Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
>
>> +/**
>> + * struct virtchnl2_ptp_get_cross_time: Associated with message
>> + * VIRTCHNL2_OP_PTP_GET_CROSS_TIME.
>> + * @sys_time_ns: System counter value expressed in nanoseconds, read
>> + * synchronously with device time
>> + * @dev_time_ns: Device clock time value expressed in nanoseconds
>> + *
>> + * PF/VF sends this message to receive the cross time.
>> + */
>> +struct virtchnl2_ptp_get_cross_time {
>> + __le64 sys_time_ns;
>> + __le64 dev_time_ns;
>> +};
>
>These are in nano seconds, and there's no room left for extension in the
>structure.. However, 64bits of nanoseconds is 584 years give or take.
>Even if we start from the Unix epoch thats a pretty long way in the
>future. Additionally, it is likely that some sort of software-based
>rollover could be used since the roll-over period would be hundreds of
>years. Ok. I don't think we need to waste additional space for extension
>here. This also applies to the other __le64 fields with nanosecond time.
Right, I've also considered it during creating virtchnl API, but at the
end of the day I agree that we don't need to waste space for extensions.
>
>> +VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_cross_time);
>> +
>> +/**
>> + * struct virtchnl2_ptp_set_dev_clk_time: Associated with message
>> + * VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME.
>> + * @dev_time_ns: Device time value expressed in nanoseconds to set
>> + *
>> + * PF/VF sends this message to set the time of the main timer.
>> + */
>> +struct virtchnl2_ptp_set_dev_clk_time {
>> + __le64 dev_time_ns;
>> +};
>> +VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_ptp_set_dev_clk_time);
>> +
>> +/**
>> + * struct virtchnl2_ptp_adj_dev_clk_fine: Associated with message
>> + * VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE.
>> + * @incval: Source timer increment value per clock cycle
>> + *
>> + * PF/VF sends this message to adjust the frequency of the main timer by the
>> + * indicated scaled ppm.
>> + */
>
>Do we want to encode scaled_ppm here in the virtchnl interface? I
>suppose its not that big a deal but it is kind of an implementation
>quirk of the Linux APIs. We could use parts per trillion or something
>similar..
>
>I suppose there is little value in translating from scaled_ppm to some
>other format, due to accumulated error, and scaled_ppm is higher
>precision than ppb. Ok.
I'm not sure I fully understand your concern, but you think that we
could use another naming convention, or provide to control plane raw
scaled ppm value?
Please notice that in current shape, we negotiate also basic increment
value in PTP capabilities, to adjust scaled ppm - as it is done in any
other product - and then the diff is sent through virtchnl message.
Thanks,
Milena
^ permalink raw reply [flat|nested] 35+ messages in thread
* RE: [Intel-wired-lan] [PATCH v10 iwl-next 05/11] idpf: negotiate PTP capabilities and get PTP clock
2025-04-08 21:15 ` [Intel-wired-lan] " Jacob Keller
@ 2025-04-09 12:55 ` Olech, Milena
2025-04-09 18:14 ` Jacob Keller
0 siblings, 1 reply; 35+ messages in thread
From: Olech, Milena @ 2025-04-09 12:55 UTC (permalink / raw)
To: Keller, Jacob E, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Nguyen, Anthony L, Kitszel, Przemyslaw,
Willem de Bruijn, Mina Almasry, Salin, Samuel
On 4/8/2025 11:15 PM, Jacob Keller wrote:
>On 4/8/2025 3:30 AM, Milena Olech wrote:
>> +static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter,
>> + struct ptp_system_timestamp *sts)
>> +{
>> + struct idpf_ptp *ptp = adapter->ptp;
>> + u32 hi, lo;
>> +
>> + spin_lock(&ptp->read_dev_clk_lock);
>> +
>> + /* Read the system timestamp pre PHC read */
>> + ptp_read_system_prets(sts);
>> +
>> + idpf_ptp_enable_shtime(adapter);
>> +
>> + /* Read the system timestamp post PHC read */
>> + ptp_read_system_postts(sts);
>> +
>> + lo = readl(ptp->dev_clk_regs.dev_clk_ns_l);
>> + hi = readl(ptp->dev_clk_regs.dev_clk_ns_h);
>> +
>> + spin_unlock(&ptp->read_dev_clk_lock);
>> +
>> + return ((u64)hi << 32) | lo;
>> +}
>v9 had comments regarding the latching of the registers for direct
>access. Can you confirm whether this is known to be safe, or if you need
>to implement a 3-part read like we do in ice and other hardware? Even
>with a spinlock there could be issues with rollover in the hardware I think?
>
So in this model we have shadow registers and we trigger HW - by writes
executed in idpf_ptp_enable_shtime - to latch the value. I've made some
experiments, where I removed this function call, and values in hi/lo
registers are constantly the same.
In other words, it is safe to read values from hi/lo registers until
the next latch.
To my best knowledge, ice does not have any HW support, that's why all
these actions are required.
Milena
>Thanks,
>Jake
>
^ permalink raw reply [flat|nested] 35+ messages in thread
* RE: [Intel-wired-lan] [PATCH v10 iwl-next 06/11] idpf: add mailbox access to read PTP clock time
2025-04-08 21:16 ` [Intel-wired-lan] " Jacob Keller
@ 2025-04-09 13:01 ` Olech, Milena
0 siblings, 0 replies; 35+ messages in thread
From: Olech, Milena @ 2025-04-09 13:01 UTC (permalink / raw)
To: Keller, Jacob E, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Nguyen, Anthony L, Kitszel, Przemyslaw,
Lobakin, Aleksander, Willem de Bruijn, Salin, Samuel
On 4/8/2025 11:17 PM, Jacob Keller wrote:
>On 4/8/2025 3:30 AM, Milena Olech wrote:
>> +static int idpf_ptp_read_src_clk_reg_mailbox(struct idpf_adapter *adapter,
>> + struct ptp_system_timestamp *sts,
>> + u64 *src_clk)
>> +{
>> + struct idpf_ptp_dev_timers clk_time;
>> + int err;
>> +
>> + /* Read the system timestamp pre PHC read */
>> + ptp_read_system_prets(sts);
>> +
>> + err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
>> + if (err)
>> + return err;
>> +
>> + /* Read the system timestamp post PHC read */
>> + ptp_read_system_postts(sts);
>> +
>> + *src_clk = clk_time.dev_clk_time_ns;
>> +
>> + return 0;
>> +}
>Not an objection, since its obvious using the direct register access is
>preferable when it is available. This will result in a fair amount of
>jitter since mailbox timing is unlikely to be consistent. We also cannot
>have sts be filled in somehow by the host because it might not be
>operating on the same system clock.
>
You're 100% right, reading the time of the clock through mailbox is not
efficient. But as you mentioned, direct register access is definitely
preferable.
Thanks,
Milena
^ permalink raw reply [flat|nested] 35+ messages in thread
* RE: [Intel-wired-lan] [PATCH v10 iwl-next 09/11] idpf: add Tx timestamp capabilities negotiation
2025-04-08 21:23 ` [Intel-wired-lan] " Jacob Keller
@ 2025-04-09 14:04 ` Olech, Milena
2025-04-09 18:08 ` Jacob Keller
0 siblings, 1 reply; 35+ messages in thread
From: Olech, Milena @ 2025-04-09 14:04 UTC (permalink / raw)
To: Keller, Jacob E, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Nguyen, Anthony L, Kitszel, Przemyslaw,
Lobakin, Aleksander, Tantilov, Emil S, Linga, Pavan Kumar,
Salin, Samuel
On 4/8/2025 11:23 PM, Jacob Keller wrote:
>On 4/8/2025 3:31 AM, Milena Olech wrote:
>> +static void idpf_ptp_release_vport_tstamp(struct idpf_vport *vport)
>> +{
>> + struct idpf_ptp_tx_tstamp *ptp_tx_tstamp, *tmp;
>> + struct list_head *head;
>> +
>> + /* Remove list with free latches */
>> + spin_lock(&vport->tx_tstamp_caps->lock_free);
>> +
>> + head = &vport->tx_tstamp_caps->latches_free;
>> + list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
>> + list_del(&ptp_tx_tstamp->list_member);
>> + kfree(ptp_tx_tstamp);
>> + }
>> +
>> + spin_unlock(&vport->tx_tstamp_caps->lock_free);
>> +
>> + /* Remove list with latches in use */
>> + spin_lock(&vport->tx_tstamp_caps->lock_in_use);
>> +
>> + head = &vport->tx_tstamp_caps->latches_in_use;
>> + list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
>> + list_del(&ptp_tx_tstamp->list_member);
>> + kfree(ptp_tx_tstamp);
>> + }
>> +
>> + spin_unlock(&vport->tx_tstamp_caps->lock_in_use);
>> +
>> + kfree(vport->tx_tstamp_caps);
>> + vport->tx_tstamp_caps = NULL;
>> +}
>Could you provide a summary and overview of the locking scheme used
>here? I see you have multiple spin locks for both the free bits and the
>in-use bits, and its a bit hard to grasp the reasoning behind this. We
>had a lot of issues getting locking for Tx timestamps correct in ice,
>though most of that had to do with quirks in the hardware.
>
Ofc :) So the main idea is to have a list of free latches (indexes) and a
list of latches that are being used - by used I mean that the timestamp
for this index is requested and being processed.
So at the beginning, the driver negotiates the list of latches with the CP
and adds them to the free list. When the timestamp is requested, driver
takes the first item of the free latches and moves it to 'in-use' list.
Similarly, when the timestamp is read, driver moves the index from
'in use' to 'free'.
Regards,
Milena
>Thanks,
>Jake
>
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 09/11] idpf: add Tx timestamp capabilities negotiation
2025-04-09 14:04 ` Olech, Milena
@ 2025-04-09 18:08 ` Jacob Keller
2025-04-10 14:11 ` Olech, Milena
0 siblings, 1 reply; 35+ messages in thread
From: Jacob Keller @ 2025-04-09 18:08 UTC (permalink / raw)
To: Olech, Milena, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Nguyen, Anthony L, Kitszel, Przemyslaw,
Lobakin, Aleksander, Tantilov, Emil S, Linga, Pavan Kumar,
Salin, Samuel
On 4/9/2025 7:04 AM, Olech, Milena wrote:
> On 4/8/2025 11:23 PM, Jacob Keller wrote:
>
>> On 4/8/2025 3:31 AM, Milena Olech wrote:
>>> +static void idpf_ptp_release_vport_tstamp(struct idpf_vport *vport)
>>> +{
>>> + struct idpf_ptp_tx_tstamp *ptp_tx_tstamp, *tmp;
>>> + struct list_head *head;
>>> +
>>> + /* Remove list with free latches */
>>> + spin_lock(&vport->tx_tstamp_caps->lock_free);
>>> +
>>> + head = &vport->tx_tstamp_caps->latches_free;
>>> + list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
>>> + list_del(&ptp_tx_tstamp->list_member);
>>> + kfree(ptp_tx_tstamp);
>>> + }
>>> +
>>> + spin_unlock(&vport->tx_tstamp_caps->lock_free);
>>> +
>>> + /* Remove list with latches in use */
>>> + spin_lock(&vport->tx_tstamp_caps->lock_in_use);
>>> +
>>> + head = &vport->tx_tstamp_caps->latches_in_use;
>>> + list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
>>> + list_del(&ptp_tx_tstamp->list_member);
>>> + kfree(ptp_tx_tstamp);
>>> + }
>>> +
>>> + spin_unlock(&vport->tx_tstamp_caps->lock_in_use);
>>> +
>>> + kfree(vport->tx_tstamp_caps);
>>> + vport->tx_tstamp_caps = NULL;
>>> +}
>> Could you provide a summary and overview of the locking scheme used
>> here? I see you have multiple spin locks for both the free bits and the
>> in-use bits, and its a bit hard to grasp the reasoning behind this. We
>> had a lot of issues getting locking for Tx timestamps correct in ice,
>> though most of that had to do with quirks in the hardware.
>>
>
> Ofc :) So the main idea is to have a list of free latches (indexes) and a
> list of latches that are being used - by used I mean that the timestamp
> for this index is requested and being processed.
>
> So at the beginning, the driver negotiates the list of latches with the CP
> and adds them to the free list. When the timestamp is requested, driver
> takes the first item of the free latches and moves it to 'in-use' list.
> Similarly, when the timestamp is read, driver moves the index from
> 'in use' to 'free'.
>
Ok. Is there a reason these need separate locks instead of just sharing
the same lock?
> Regards,
> Milena
>
>> Thanks,
>> Jake
>>
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 03/11] virtchnl: add PTP virtchnl definitions
2025-04-09 11:51 ` Olech, Milena
@ 2025-04-09 18:11 ` Jacob Keller
2025-04-10 13:18 ` Olech, Milena
0 siblings, 1 reply; 35+ messages in thread
From: Jacob Keller @ 2025-04-09 18:11 UTC (permalink / raw)
To: Olech, Milena, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Nguyen, Anthony L, Kitszel, Przemyslaw,
Lobakin, Aleksander, Willem de Bruijn, Mina Almasry,
Salin, Samuel
On 4/9/2025 4:51 AM, Olech, Milena wrote:
> On 4/8/2025 11:12 PM, Jacob Keller wrote:
>> On 4/8/2025 3:30 AM, Milena Olech wrote:
>>> +/**
>>> + * struct virtchnl2_ptp_adj_dev_clk_fine: Associated with message
>>> + * VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE.
>>> + * @incval: Source timer increment value per clock cycle
>>> + *
>>> + * PF/VF sends this message to adjust the frequency of the main timer by the
>>> + * indicated scaled ppm.
>>> + */
This comment should be rephrased then. The text implies the value being
sent is the scaled PPM value.
>>
>> Do we want to encode scaled_ppm here in the virtchnl interface? I
>> suppose its not that big a deal but it is kind of an implementation
>> quirk of the Linux APIs. We could use parts per trillion or something
>> similar..
>>
>> I suppose there is little value in translating from scaled_ppm to some
>> other format, due to accumulated error, and scaled_ppm is higher
>> precision than ppb. Ok.
>
> I'm not sure I fully understand your concern, but you think that we
> could use another naming convention, or provide to control plane raw
> scaled ppm value?
>
> Please notice that in current shape, we negotiate also basic increment
> value in PTP capabilities, to adjust scaled ppm - as it is done in any
> other product - and then the diff is sent through virtchnl message.
>
No. What I am trying to get at is that i don't think it makes sense to
encode the use of scaled_ppm in the virtchnl message. You didn't do that
which is good. But the comment makes it seem like you did, because it
seems like the message itself adjusts the main timer by the scaled PPM
indicated within the message. In fact the driver calculates the new
invcalue and sends it.
Its not a big deal either way to me, I just misinterpreted the meaning
of the comment.
> Thanks,
> Milena
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [Intel-wired-lan] [PATCH v10 iwl-next 05/11] idpf: negotiate PTP capabilities and get PTP clock
2025-04-09 12:55 ` Olech, Milena
@ 2025-04-09 18:14 ` Jacob Keller
0 siblings, 0 replies; 35+ messages in thread
From: Jacob Keller @ 2025-04-09 18:14 UTC (permalink / raw)
To: Olech, Milena, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Nguyen, Anthony L, Kitszel, Przemyslaw,
Willem de Bruijn, Mina Almasry, Salin, Samuel
On 4/9/2025 5:55 AM, Olech, Milena wrote:
> On 4/8/2025 11:15 PM, Jacob Keller wrote:
>
>> On 4/8/2025 3:30 AM, Milena Olech wrote:
>>> +static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter,
>>> + struct ptp_system_timestamp *sts)
>>> +{
>>> + struct idpf_ptp *ptp = adapter->ptp;
>>> + u32 hi, lo;
>>> +
>>> + spin_lock(&ptp->read_dev_clk_lock);
>>> +
>>> + /* Read the system timestamp pre PHC read */
>>> + ptp_read_system_prets(sts);
>>> +
>>> + idpf_ptp_enable_shtime(adapter);
>>> +
Aha, I see it now. You snapshot the time value here.
>>> + /* Read the system timestamp post PHC read */
>>> + ptp_read_system_postts(sts);
>>> +
>>> + lo = readl(ptp->dev_clk_regs.dev_clk_ns_l);
>>> + hi = readl(ptp->dev_clk_regs.dev_clk_ns_h);
>>> +
And this is just reading it out of the snapshot shadow registers. Ok.
>>> + spin_unlock(&ptp->read_dev_clk_lock);
>>> +
>>> + return ((u64)hi << 32) | lo;
>>> +}
>> v9 had comments regarding the latching of the registers for direct
>> access. Can you confirm whether this is known to be safe, or if you need
>> to implement a 3-part read like we do in ice and other hardware? Even
>> with a spinlock there could be issues with rollover in the hardware I think?
>>
>
> So in this model we have shadow registers and we trigger HW - by writes
> executed in idpf_ptp_enable_shtime - to latch the value. I've made some
> experiments, where I removed this function call, and values in hi/lo
> registers are constantly the same.
>
> In other words, it is safe to read values from hi/lo registers until
> the next latch.
>
> To my best knowledge, ice does not have any HW support, that's why all
> these actions are required.
>
Yep, ice doesn't have a snapshot like this, and neither does our old
hardware. This is much better: it improves the accuracy of the sts
values, and is simpler. Nice.
Now that I understand what the idpf_ptp_enable_shtime does its a lot
more clear.
Thanks!
> Milena
>
>> Thanks,
>> Jake
>>
^ permalink raw reply [flat|nested] 35+ messages in thread
* RE: [Intel-wired-lan] [PATCH v10 iwl-next 03/11] virtchnl: add PTP virtchnl definitions
2025-04-09 18:11 ` Jacob Keller
@ 2025-04-10 13:18 ` Olech, Milena
0 siblings, 0 replies; 35+ messages in thread
From: Olech, Milena @ 2025-04-10 13:18 UTC (permalink / raw)
To: Keller, Jacob E, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Nguyen, Anthony L, Kitszel, Przemyslaw,
Lobakin, Aleksander, Willem de Bruijn, Mina Almasry,
Salin, Samuel
On 4/9/2025 8:12 PM, Jacob Keller wrote:
>On 4/9/2025 4:51 AM, Olech, Milena wrote:
>> On 4/8/2025 11:12 PM, Jacob Keller wrote:
>>> On 4/8/2025 3:30 AM, Milena Olech wrote:
>>>> +/**
>>>> + * struct virtchnl2_ptp_adj_dev_clk_fine: Associated with message
>>>> + * VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE.
>>>> + * @incval: Source timer increment value per clock cycle
>>>> + *
>>>> + * PF/VF sends this message to adjust the frequency of the main timer by the
>>>> + * indicated scaled ppm.
>>>> + */
>
>This comment should be rephrased then. The text implies the value being
>sent is the scaled PPM value.
Ok I see, I will update the comment in the next version.
Milena
>
>>>
>>> Do we want to encode scaled_ppm here in the virtchnl interface? I
>>> suppose its not that big a deal but it is kind of an implementation
>>> quirk of the Linux APIs. We could use parts per trillion or something
>>> similar..
>>>
>>> I suppose there is little value in translating from scaled_ppm to some
>>> other format, due to accumulated error, and scaled_ppm is higher
>>> precision than ppb. Ok.
>>
>> I'm not sure I fully understand your concern, but you think that we
>> could use another naming convention, or provide to control plane raw
>> scaled ppm value?
>>
>> Please notice that in current shape, we negotiate also basic increment
>> value in PTP capabilities, to adjust scaled ppm - as it is done in any
>> other product - and then the diff is sent through virtchnl message.
>>
>
>No. What I am trying to get at is that i don't think it makes sense to
>encode the use of scaled_ppm in the virtchnl message. You didn't do that
>which is good. But the comment makes it seem like you did, because it
>seems like the message itself adjusts the main timer by the scaled PPM
>indicated within the message. In fact the driver calculates the new
>invcalue and sends it.
>
>Its not a big deal either way to me, I just misinterpreted the meaning
>of the comment.
>
>> Thanks,
>> Milena
>
^ permalink raw reply [flat|nested] 35+ messages in thread
* RE: [Intel-wired-lan] [PATCH v10 iwl-next 10/11] idpf: add Tx timestamp flows
2025-04-08 21:31 ` [Intel-wired-lan] " Jacob Keller
@ 2025-04-10 13:28 ` Olech, Milena
0 siblings, 0 replies; 35+ messages in thread
From: Olech, Milena @ 2025-04-10 13:28 UTC (permalink / raw)
To: Keller, Jacob E, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Nguyen, Anthony L, Kitszel, Przemyslaw,
Hay, Joshua A, Salin, Samuel
On 4/8/2025 11:31 PM, Jacob Keller wrote:
>On 4/8/2025 3:31 AM, Milena Olech wrote:
>> +/**
>> + * idpf_ptp_tstamp_extend_32b_to_64b - Convert a 32b nanoseconds Tx or Rx
>> + * timestamp value to 64b.
>> + * @cached_phc_time: recently cached copy of PHC time
>> + * @in_timestamp: Ingress/egress 32b nanoseconds timestamp value
>> + *
>> + * Hardware captures timestamps which contain only 32 bits of nominal
>> + * nanoseconds, as opposed to the 64bit timestamps that the stack expects.
>> + *
>> + * Return: Tx timestamp value extended to 64 bits based on cached PHC time.
>> + */
>> +u64 idpf_ptp_tstamp_extend_32b_to_64b(u64 cached_phc_time, u32 in_timestamp)
>> +{
>> + s64 delta;
>> +
>> + delta = in_timestamp - lower_32_bits(cached_phc_time);
>> +
>> + return cached_phc_time + delta;
>> +}
>
>
>This logic looks quite different from what we did in ice and iavf, which
>was based on the math from timecounters. It looks like you do check if
>the value is too old which is good to verify. Perhaps I'm just
>misunderstanding the math.
>
>For clarity, here's what we have in ice:
>
>> static u64 ice_ptp_extend_32b_ts(u64 cached_phc_time, u32 in_tstamp)
>> {
>> u32 delta, phc_time_lo;
>> u64 ns;
>>
>> /* Extract the lower 32 bits of the PHC time */
>> phc_time_lo = (u32)cached_phc_time;
>>
>> /* Calculate the delta between the lower 32bits of the cached PHC
>> * time and the in_tstamp value
>> */
>> delta = (in_tstamp - phc_time_lo);
>>
>> /* Do not assume that the in_tstamp is always more recent than the
>> * cached PHC time. If the delta is large, it indicates that the
>> * in_tstamp was taken in the past, and should be converted
>> * forward.
>> */
>> if (delta > (U32_MAX / 2)) {
>> /* reverse the delta calculation here */
>> delta = (phc_time_lo - in_tstamp);
>> ns = cached_phc_time - delta;
>> } else {
>> ns = cached_phc_time + delta;
>> }
>>
>> return ns;
>> }
>
>
>In particular, this ensures that we correctly handle the case where a
>timestamp is captured just before an update to the cached PHC time.
>Without that check, you can't guarantee that the timestamp is updated
>correctly with lockess PHC updating.
>
>With these checks, as long as the timestamp is recent, we can extend it
>safely without worrying about whether the cached PHC time we are using
>is slightly old or not. (As long as its no more than 2 seconds old).
>
>Could you explain why this was changed for idpf?
>
>Bonus points if we extracted this method into libie/libeth and shared it
>across ice, idpf, and iavf, which I believe recently gained support for
>timestamping as well.
>
TBH I've started using the same algorithm at the beginning, but then I
tried to introduce some simplifications, and this is the final shape.
But after checking it again for both positive and negative delta I see
some discrepancies in timestamps values. So in the next version I'll
switch to well-known method from ice/iavf.
Milena
^ permalink raw reply [flat|nested] 35+ messages in thread
* RE: [Intel-wired-lan] [PATCH v10 iwl-next 09/11] idpf: add Tx timestamp capabilities negotiation
2025-04-09 18:08 ` Jacob Keller
@ 2025-04-10 14:11 ` Olech, Milena
2025-04-10 15:36 ` Keller, Jacob E
0 siblings, 1 reply; 35+ messages in thread
From: Olech, Milena @ 2025-04-10 14:11 UTC (permalink / raw)
To: Keller, Jacob E, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Nguyen, Anthony L, Kitszel, Przemyslaw,
Lobakin, Aleksander, Tantilov, Emil S, Linga, Pavan Kumar,
Salin, Samuel
On 4/9/2025 8:09 PM, Jacob Keller wrote:
>On 4/9/2025 7:04 AM, Olech, Milena wrote:
>> On 4/8/2025 11:23 PM, Jacob Keller wrote:
>>
>>> On 4/8/2025 3:31 AM, Milena Olech wrote:
>>>> +static void idpf_ptp_release_vport_tstamp(struct idpf_vport *vport)
>>>> +{
>>>> + struct idpf_ptp_tx_tstamp *ptp_tx_tstamp, *tmp;
>>>> + struct list_head *head;
>>>> +
>>>> + /* Remove list with free latches */
>>>> + spin_lock(&vport->tx_tstamp_caps->lock_free);
>>>> +
>>>> + head = &vport->tx_tstamp_caps->latches_free;
>>>> + list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
>>>> + list_del(&ptp_tx_tstamp->list_member);
>>>> + kfree(ptp_tx_tstamp);
>>>> + }
>>>> +
>>>> + spin_unlock(&vport->tx_tstamp_caps->lock_free);
>>>> +
>>>> + /* Remove list with latches in use */
>>>> + spin_lock(&vport->tx_tstamp_caps->lock_in_use);
>>>> +
>>>> + head = &vport->tx_tstamp_caps->latches_in_use;
>>>> + list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
>>>> + list_del(&ptp_tx_tstamp->list_member);
>>>> + kfree(ptp_tx_tstamp);
>>>> + }
>>>> +
>>>> + spin_unlock(&vport->tx_tstamp_caps->lock_in_use);
>>>> +
>>>> + kfree(vport->tx_tstamp_caps);
>>>> + vport->tx_tstamp_caps = NULL;
>>>> +}
>>> Could you provide a summary and overview of the locking scheme used
>>> here? I see you have multiple spin locks for both the free bits and the
>>> in-use bits, and its a bit hard to grasp the reasoning behind this. We
>>> had a lot of issues getting locking for Tx timestamps correct in ice,
>>> though most of that had to do with quirks in the hardware.
>>>
>>
>> Ofc :) So the main idea is to have a list of free latches (indexes) and a
>> list of latches that are being used - by used I mean that the timestamp
>> for this index is requested and being processed.
>>
>> So at the beginning, the driver negotiates the list of latches with the CP
>> and adds them to the free list. When the timestamp is requested, driver
>> takes the first item of the free latches and moves it to 'in-use' list.
>> Similarly, when the timestamp is read, driver moves the index from
>> 'in use' to 'free'.
>>
>
>Ok. Is there a reason these need separate locks instead of just sharing
>the same lock?
>
That's a very good question. In fact in most places I need to move item
from the first to the second list, so I could use the same spinlock for
both.
The only place where only one is used is sending virtchnl message to get
the Tx timestamp value, where I search for items on 'in use' list.
But it does not mean that we cannot share lock, because when 'in use'
is processed, it is not possible to request the new index (because we need
the lock to move from 'free' to 'in use').
So to summarize, at the end of the day I don't see any specific reason
of having two.
Let me know what are your thoughts, but I guess it is safe to remove one
lock.
Milena
>> Regards,
>> Milena
>>
>>> Thanks,
>>> Jake
>>>
>
>
^ permalink raw reply [flat|nested] 35+ messages in thread
* RE: [Intel-wired-lan] [PATCH v10 iwl-next 09/11] idpf: add Tx timestamp capabilities negotiation
2025-04-10 14:11 ` Olech, Milena
@ 2025-04-10 15:36 ` Keller, Jacob E
0 siblings, 0 replies; 35+ messages in thread
From: Keller, Jacob E @ 2025-04-10 15:36 UTC (permalink / raw)
To: Olech, Milena, intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org, Nguyen, Anthony L, Kitszel, Przemyslaw,
Lobakin, Aleksander, Tantilov, Emil S, Linga, Pavan Kumar,
Salin, Samuel
> -----Original Message-----
> From: Olech, Milena <milena.olech@intel.com>
> Sent: Thursday, April 10, 2025 7:12 AM
> To: Keller, Jacob E <jacob.e.keller@intel.com>; intel-wired-lan@lists.osuosl.org
> Cc: netdev@vger.kernel.org; Nguyen, Anthony L <anthony.l.nguyen@intel.com>;
> Kitszel, Przemyslaw <przemyslaw.kitszel@intel.com>; Lobakin, Aleksander
> <aleksander.lobakin@intel.com>; Tantilov, Emil S <emil.s.tantilov@intel.com>;
> Linga, Pavan Kumar <pavan.kumar.linga@intel.com>; Salin, Samuel
> <samuel.salin@intel.com>
> Subject: RE: [Intel-wired-lan] [PATCH v10 iwl-next 09/11] idpf: add Tx timestamp
> capabilities negotiation
>
> On 4/9/2025 8:09 PM, Jacob Keller wrote:
>
> >On 4/9/2025 7:04 AM, Olech, Milena wrote:
> >> On 4/8/2025 11:23 PM, Jacob Keller wrote:
> >>
> >>> On 4/8/2025 3:31 AM, Milena Olech wrote:
> >>>> +static void idpf_ptp_release_vport_tstamp(struct idpf_vport *vport)
> >>>> +{
> >>>> + struct idpf_ptp_tx_tstamp *ptp_tx_tstamp, *tmp;
> >>>> + struct list_head *head;
> >>>> +
> >>>> + /* Remove list with free latches */
> >>>> + spin_lock(&vport->tx_tstamp_caps->lock_free);
> >>>> +
> >>>> + head = &vport->tx_tstamp_caps->latches_free;
> >>>> + list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
> >>>> + list_del(&ptp_tx_tstamp->list_member);
> >>>> + kfree(ptp_tx_tstamp);
> >>>> + }
> >>>> +
> >>>> + spin_unlock(&vport->tx_tstamp_caps->lock_free);
> >>>> +
> >>>> + /* Remove list with latches in use */
> >>>> + spin_lock(&vport->tx_tstamp_caps->lock_in_use);
> >>>> +
> >>>> + head = &vport->tx_tstamp_caps->latches_in_use;
> >>>> + list_for_each_entry_safe(ptp_tx_tstamp, tmp, head, list_member) {
> >>>> + list_del(&ptp_tx_tstamp->list_member);
> >>>> + kfree(ptp_tx_tstamp);
> >>>> + }
> >>>> +
> >>>> + spin_unlock(&vport->tx_tstamp_caps->lock_in_use);
> >>>> +
> >>>> + kfree(vport->tx_tstamp_caps);
> >>>> + vport->tx_tstamp_caps = NULL;
> >>>> +}
> >>> Could you provide a summary and overview of the locking scheme used
> >>> here? I see you have multiple spin locks for both the free bits and the
> >>> in-use bits, and its a bit hard to grasp the reasoning behind this. We
> >>> had a lot of issues getting locking for Tx timestamps correct in ice,
> >>> though most of that had to do with quirks in the hardware.
> >>>
> >>
> >> Ofc :) So the main idea is to have a list of free latches (indexes) and a
> >> list of latches that are being used - by used I mean that the timestamp
> >> for this index is requested and being processed.
> >>
> >> So at the beginning, the driver negotiates the list of latches with the CP
> >> and adds them to the free list. When the timestamp is requested, driver
> >> takes the first item of the free latches and moves it to 'in-use' list.
> >> Similarly, when the timestamp is read, driver moves the index from
> >> 'in use' to 'free'.
> >>
> >
> >Ok. Is there a reason these need separate locks instead of just sharing
> >the same lock?
> >
>
> That's a very good question. In fact in most places I need to move item
> from the first to the second list, so I could use the same spinlock for
> both.
>
> The only place where only one is used is sending virtchnl message to get
> the Tx timestamp value, where I search for items on 'in use' list.
>
> But it does not mean that we cannot share lock, because when 'in use'
> is processed, it is not possible to request the new index (because we need
> the lock to move from 'free' to 'in use').
>
> So to summarize, at the end of the day I don't see any specific reason
> of having two.
>
> Let me know what are your thoughts, but I guess it is safe to remove one
> lock.
>
> Milena
I would feel better about only one lock, as it reduces the number of locks we need to think about, and removes the risk of circular locking issues.
Thanks,
Jake
>
> >> Regards,
> >> Milena
> >>
> >>> Thanks,
> >>> Jake
> >>>
> >
> >
^ permalink raw reply [flat|nested] 35+ messages in thread
end of thread, other threads:[~2025-04-10 15:36 UTC | newest]
Thread overview: 35+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-08 10:30 [PATCH v10 iwl-next 00/11] idpf: add initial PTP support Milena Olech
2025-04-08 10:30 ` [PATCH v10 iwl-next 01/11] idpf: change the method for mailbox workqueue allocation Milena Olech
2025-04-08 21:01 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:30 ` [PATCH v10 iwl-next 02/11] idpf: add initial PTP support Milena Olech
2025-04-08 21:02 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:30 ` [PATCH v10 iwl-next 03/11] virtchnl: add PTP virtchnl definitions Milena Olech
2025-04-08 21:12 ` [Intel-wired-lan] " Jacob Keller
2025-04-09 11:51 ` Olech, Milena
2025-04-09 18:11 ` Jacob Keller
2025-04-10 13:18 ` Olech, Milena
2025-04-08 10:30 ` [PATCH v10 iwl-next 04/11] idpf: move virtchnl structures to the header file Milena Olech
2025-04-08 21:12 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:30 ` [PATCH v10 iwl-next 05/11] idpf: negotiate PTP capabilities and get PTP clock Milena Olech
2025-04-08 21:15 ` [Intel-wired-lan] " Jacob Keller
2025-04-09 12:55 ` Olech, Milena
2025-04-09 18:14 ` Jacob Keller
2025-04-08 10:30 ` [PATCH v10 iwl-next 06/11] idpf: add mailbox access to read PTP clock time Milena Olech
2025-04-08 21:16 ` [Intel-wired-lan] " Jacob Keller
2025-04-09 13:01 ` Olech, Milena
2025-04-08 21:17 ` Jacob Keller
2025-04-08 10:31 ` [PATCH v10 iwl-next 07/11] idpf: add cross timestamping Milena Olech
2025-04-08 21:18 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:31 ` [PATCH v10 iwl-next 08/11] idpf: add PTP clock configuration Milena Olech
2025-04-08 21:20 ` [Intel-wired-lan] " Jacob Keller
2025-04-08 10:31 ` [PATCH v10 iwl-next 09/11] idpf: add Tx timestamp capabilities negotiation Milena Olech
2025-04-08 21:23 ` [Intel-wired-lan] " Jacob Keller
2025-04-09 14:04 ` Olech, Milena
2025-04-09 18:08 ` Jacob Keller
2025-04-10 14:11 ` Olech, Milena
2025-04-10 15:36 ` Keller, Jacob E
2025-04-08 10:31 ` [PATCH v10 iwl-next 10/11] idpf: add Tx timestamp flows Milena Olech
2025-04-08 21:31 ` [Intel-wired-lan] " Jacob Keller
2025-04-10 13:28 ` Olech, Milena
2025-04-08 10:31 ` [PATCH v10 iwl-next 11/11] idpf: add support for Rx timestamping Milena Olech
2025-04-08 21:31 ` [Intel-wired-lan] " Jacob Keller
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).