* [PATCH v1 0/4] Enable PTP feature for MEV
@ 2025-10-24 12:08 Soumyadeep Hore
2025-10-24 12:08 ` [PATCH v1 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
` (3 more replies)
0 siblings, 4 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-10-24 12:08 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Enabling basic PTP feature in IDPF PMD using virtchnl messages.
Milena Olech (1):
net/idpf: add a new API for PTP support
Soumyadeep Hore (3):
net/idpf: add PTP virtchnl2 support
net/intel: add support for Precision Time Protocol
doc: add PTP IDPF documentation
doc/guides/nics/idpf.rst | 16 +
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/base/virtchnl2.h | 324 ++++++---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++--
drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
drivers/net/intel/idpf/idpf_ethdev.c | 275 ++++++++
drivers/net/intel/idpf/idpf_ptp.c | 646 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 227 ++++++
drivers/net/intel/idpf/meson.build | 1 +
11 files changed, 1562 insertions(+), 162 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
--
2.34.1
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH v1 1/4] net/idpf: add a new API for PTP support
2025-10-24 12:08 [PATCH v1 0/4] Enable PTP feature for MEV Soumyadeep Hore
@ 2025-10-24 12:08 ` Soumyadeep Hore
2025-10-24 12:08 ` [PATCH v1 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
` (2 subsequent siblings)
3 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-10-24 12:08 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao,
Milena Olech
From: Milena Olech <milena.olech@intel.com>
Add PTP capabilities to determine which PTP features are enabled -
get/set the time, cross timestamp, adjust the clock and timestamping.
Create appropriate structures and opcodes to support features introduced by
capabilities.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
---
drivers/net/intel/idpf/base/virtchnl2.h | 324 +++++++++++++++++-------
1 file changed, 235 insertions(+), 89 deletions(-)
diff --git a/drivers/net/intel/idpf/base/virtchnl2.h b/drivers/net/intel/idpf/base/virtchnl2.h
index 6cfb4f56fa..0b01daee30 100644
--- a/drivers/net/intel/idpf/base/virtchnl2.h
+++ b/drivers/net/intel/idpf/base/virtchnl2.h
@@ -118,8 +118,14 @@ enum virtchnl2_op {
VIRTCHNL2_OP_DEL_QUEUE_GROUPS = 539,
VIRTCHNL2_OP_GET_PORT_STATS = 540,
/* TimeSync opcodes */
- VIRTCHNL2_OP_GET_PTP_CAPS = 541,
- VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES = 542,
+ 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,
};
#define VIRTCHNL2_RDMA_INVALID_QUEUE_IDX 0xFFFF
@@ -1813,123 +1819,180 @@ VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_promisc_info);
* enum virtchnl2_ptp_caps - PTP capabilities
*/
enum virtchnl2_ptp_caps {
- VIRTCHNL2_PTP_CAP_LEGACY_CROSS_TIME = BIT(0),
- VIRTCHNL2_PTP_CAP_PTM = BIT(1),
- VIRTCHNL2_PTP_CAP_DEVICE_CLOCK_CONTROL = BIT(2),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_DIRECT = BIT(3),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_VIRTCHNL = BIT(4),
-};
-
-/**
- * struct virtchnl2_ptp_legacy_cross_time_reg - Legacy cross time registers
- * offsets.
+ 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_legacy_cross_time_reg {
- __le32 shadow_time_0;
- __le32 shadow_time_l;
- __le32 shadow_time_h;
- __le32 cmd_sync;
+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(16, virtchnl2_ptp_legacy_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_clk_reg_offsets);
/**
- * struct virtchnl2_ptp_ptm_cross_time_reg - PTM cross time registers 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_ptm_cross_time_reg {
- __le32 art_l;
- __le32 art_h;
- __le32 cmd_sync;
+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_ptm_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_cross_time_reg_offsets);
/**
- * struct virtchnl2_ptp_device_clock_control - Registers needed to control the
- * main clock.
+ * 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_device_clock_control {
- __le32 cmd;
- __le32 incval_l;
- __le32 incval_h;
- __le32 shadj_l;
- __le32 shadj_h;
- u8 pad[4];
+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(24, virtchnl2_ptp_device_clock_control);
+VIRTCHNL2_CHECK_STRUCT_LEN(40, virtchnl2_ptp_clk_adj_reg_offsets);
/**
- * struct virtchnl2_ptp_tx_tstamp_entry - PTP TX timestamp entry
- * @tx_latch_register_base: TX latch register base
- * @tx_latch_register_offset: TX latch register offset
- * @index: Index
- * @pad: Padding
+ * 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_entry {
- __le32 tx_latch_register_base;
- __le32 tx_latch_register_offset;
+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_entry);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch_caps);
/**
- * struct virtchnl2_ptp_tx_tstamp - Structure that defines tx tstamp entries
+ * struct virtchnl2_ptp_get_vport_tx_tstamp_caps - Structure that defines Tx
+ * tstamp entries
+ * @vport_id: Vport number
* @num_latches: Total number of latches
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
- * @ptp_tx_tstamp_entries: Aarray of TX timestamp entries
+ * @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_tx_tstamp {
+struct virtchnl2_ptp_get_vport_tx_tstamp_caps {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
- struct virtchnl2_ptp_tx_tstamp_entry ptp_tx_tstamp_entries[STRUCT_VAR_LEN];
+ u8 tstamp_ns_lo_bit;
+ u8 tstamp_ns_hi_bit;
+ u8 pad[8];
+
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp,
- ptp_tx_tstamp_entries);
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_caps,
+ tstamp_latches);
/**
- * struct virtchnl2_get_ptp_caps - Get PTP capabilities
- * @ptp_caps: PTP capability bitmap. See enum virtchnl2_ptp_caps.
- * @pad: Padding
- * @legacy_cross_time_reg: Legacy cross time register
- * @ptm_cross_time_reg: PTM cross time register
- * @device_clock_control: Device clock control
- * @tx_tstamp: TX timestamp
+ * 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
*
- * PV/VF sends this message to negotiate PTP capabilities. CP updates bitmap
+ * PF/VF sends this message to negotiate PTP capabilities. CP updates bitmap
* with supported features and fulfills appropriate structures.
- *
- * Associated with VIRTCHNL2_OP_GET_PTP_CAPS.
- */
-struct virtchnl2_get_ptp_caps {
- __le32 ptp_caps;
+ * 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_legacy_cross_time_reg legacy_cross_time_reg;
- struct virtchnl2_ptp_ptm_cross_time_reg ptm_cross_time_reg;
- struct virtchnl2_ptp_device_clock_control device_clock_control;
- struct virtchnl2_ptp_tx_tstamp tx_tstamp;
+ 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_VAR_LEN(88, virtchnl2_get_ptp_caps,
- tx_tstamp.ptp_tx_tstamp_entries);
+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_h: Timestamp high
- * @tstamp_l: Timestamp low
+ * @tstamp: Timestamp value
* @index: Index
* @valid: Timestamp validity
- * @pad: Padding
+ * @pad: Padding for future extensions
*/
struct virtchnl2_ptp_tx_tstamp_latch {
- __le32 tstamp_h;
- __le32 tstamp_l;
+ __le64 tstamp;
u8 index;
u8 valid;
u8 pad[6];
@@ -1938,26 +2001,97 @@ struct virtchnl2_ptp_tx_tstamp_latch {
VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch);
/**
- * struct virtchnl2_ptp_tx_tstamp_latches - PTP TX timestamp latches
+ * 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
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
+ * @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_GET_PTP_TX_TSTAMP_LATCHES.
+ * Associated with VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP.
*/
-struct virtchnl2_ptp_tx_tstamp_latches {
+struct virtchnl2_ptp_get_vport_tx_tstamp_latches {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
+ u8 get_devtime_with_txtstmp;
+ u8 pad[1];
+ u64 device_time;
struct virtchnl2_ptp_tx_tstamp_latch tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp_latches,
+
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_latches,
tstamp_latches);
+/* VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME
+ * @dev_time_ns: Device clock time value in nanoseconds
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the time from the main timer
+ */
+struct virtchnl2_ptp_get_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_dev_clk_time);
+
+/* 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
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the cross time
+ */
+struct virtchnl2_ptp_get_cross_time {
+ __le64 sys_time_ns;
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_get_cross_time);
+
+/* VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME
+ * @dev_time_ns: Device time value expressed in nanoseconds to set
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to set the time of the main timer
+ */
+struct virtchnl2_ptp_set_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_set_dev_clk_time);
+
+/* 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);
+
+/* 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);
+
static inline const char *virtchnl2_op_str(__le32 v_opcode)
{
switch (v_opcode) {
@@ -2025,10 +2159,22 @@ static inline const char *virtchnl2_op_str(__le32 v_opcode)
return "VIRTCHNL2_OP_DEL_QUEUE_GROUPS";
case VIRTCHNL2_OP_GET_PORT_STATS:
return "VIRTCHNL2_OP_GET_PORT_STATS";
- case VIRTCHNL2_OP_GET_PTP_CAPS:
- return "VIRTCHNL2_OP_GET_PTP_CAPS";
- case VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES:
- return "VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES";
+ case VIRTCHNL2_OP_PTP_GET_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_CAPS";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP";
+ case VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_CROSS_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_CROSS_TIME";
+ case VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS";
default:
return "Unsupported (update virtchnl2.h)";
}
--
2.34.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v1 2/4] net/idpf: add PTP virtchnl2 support
2025-10-24 12:08 [PATCH v1 0/4] Enable PTP feature for MEV Soumyadeep Hore
2025-10-24 12:08 ` [PATCH v1 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
@ 2025-10-24 12:08 ` Soumyadeep Hore
2025-10-24 12:08 ` [PATCH v1 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
2025-10-24 12:08 ` [PATCH v1 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
3 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-10-24 12:08 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Add a new file - idpf_ptp - to handle PTP virtchnl messages.
Keep the registers addresses in the PTP struct.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
drivers/net/intel/idpf/idpf_ptp.c | 646 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 227 ++++++
drivers/net/intel/idpf/meson.build | 1 +
5 files changed, 910 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
diff --git a/drivers/net/intel/idpf/idpf_common_device.h b/drivers/net/intel/idpf/idpf_common_device.h
index c32dcfbb12..3c84b96225 100644
--- a/drivers/net/intel/idpf/idpf_common_device.h
+++ b/drivers/net/intel/idpf/idpf_common_device.h
@@ -90,6 +90,7 @@ struct idpf_adapter {
/* For timestamp */
uint64_t time_hw;
+ struct idpf_ptp *ptp;
enum idpf_rx_func_type rx_func_type;
};
@@ -161,6 +162,9 @@ struct idpf_vport {
/* Event from ipf */
bool link_up;
uint32_t link_speed;
+
+ /* For PTP */
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
};
/* Message type read in virtual channel from PF */
diff --git a/drivers/net/intel/idpf/idpf_common_virtchnl.c b/drivers/net/intel/idpf/idpf_common_virtchnl.c
index e927d7415a..a28bc9bee6 100644
--- a/drivers/net/intel/idpf/idpf_common_virtchnl.c
+++ b/drivers/net/intel/idpf/idpf_common_virtchnl.c
@@ -4,6 +4,7 @@
#include "idpf_common_virtchnl.h"
#include "idpf_common_logs.h"
+#include "idpf_ptp.h"
#include <eal_export.h>
@@ -38,6 +39,28 @@ idpf_vc_clean(struct idpf_adapter *adapter)
return 0;
}
+/**
+ * idpf_ptp_is_mb_msg - Check if the message is PTP-related
+ * @op: virtchnl opcode
+ *
+ * Returns true if msg is PTP-related, false otherwise
+ */
+static inline bool idpf_ptp_is_mb_msg(uint32_t op)
+{
+ switch (op) {
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ 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:
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int
idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
uint16_t msg_size, uint8_t *msg)
@@ -71,8 +94,15 @@ idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
memcpy(dma_mem->va, msg, msg_size);
- ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
- ctlq_msg->func_id = 0;
+ if (idpf_ptp_is_mb_msg(op) && adapter->ptp->secondary_mbx.valid) {
+ 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 {
+ ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
+ ctlq_msg->func_id = 0;
+ }
+
ctlq_msg->data_len = msg_size;
ctlq_msg->cookie.mbx.chnl_opcode = op;
ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
diff --git a/drivers/net/intel/idpf/idpf_ptp.c b/drivers/net/intel/idpf/idpf_ptp.c
new file mode 100644
index 0000000000..3a364adc93
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.c
@@ -0,0 +1,646 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2001-2025 Intel Corporation
+ */
+
+#include "idpf_ptp.h"
+#include <base/virtchnl2.h>
+#include "idpf_common_virtchnl.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.
+ */
+static 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);
+
+ /* 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);
+
+ /* Set the device clock time */
+ direct = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME;
+ mailbox = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB;
+ 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);
+
+ /* 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);
+}
+
+/**
+ * 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_cross_time_reg_offsets cross_tstamp_offsets;
+ struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
+ struct virtchnl2_ptp_get_caps send_ptp_caps_msg = { };
+ struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
+ struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
+ struct idpf_cmd_info args = { };
+ struct idpf_ptp_secondary_mbx *scnd_mbx;
+ struct idpf_ptp *ptp = adapter->ptp;
+ struct idpf_hw *hw = &adapter->hw;
+ enum idpf_ptp_access access_type;
+ int err = 0;
+ u32 temp_offset;
+
+ 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 |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CAPS;
+ args.in_args = (uint8_t *)&send_ptp_caps_msg;
+ args.in_args_size = sizeof(send_ptp_caps_msg);
+ recv_ptp_caps_msg = rte_zmalloc(NULL,
+ sizeof(struct virtchnl2_ptp_get_caps), 0);
+ if (!recv_ptp_caps_msg)
+ return -ENOMEM;
+
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(*recv_ptp_caps_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ goto free_mem;
+
+ recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
+ 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);
+
+ /* 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);
+
+ access_type = ptp->get_dev_clk_time_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ goto cross_tstamp;
+
+ 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_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.dev_clk_ns_h);
+ ptp->dev_clk_regs.dev_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_l);
+ ptp->dev_clk_regs.phy_clk_ns_l = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_h);
+ ptp->dev_clk_regs.phy_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+cross_tstamp:
+ access_type = ptp->get_cross_tstamp_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ goto discipline_clock;
+
+ 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_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.sys_time_ns_h);
+ ptp->dev_clk_regs.sys_time_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+discipline_clock:
+ access_type = ptp->adj_dev_clk_time_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ return err;
+
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_l);
+ ptp->dev_clk_regs.incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_h);
+ ptp->dev_clk_regs.incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_l);
+ ptp->dev_clk_regs.shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_h);
+ ptp->dev_clk_regs.shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+ /* PHY clock offsets */
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_cmd_type);
+ ptp->dev_clk_regs.phy_cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_l);
+ ptp->dev_clk_regs.phy_incval_l = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_h);
+ ptp->dev_clk_regs.phy_incval_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_l);
+ ptp->dev_clk_regs.phy_shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_h);
+ ptp->dev_clk_regs.phy_shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+free_mem:
+ rte_free(recv_ptp_caps_msg);
+
+ return err;
+}
+
+/**
+ * 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)
+{
+ uint32_t 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 */
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, shtime_enable);
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, exec_cmd | shtime_enable);
+}
+
+/**
+ * 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_cmd_info args = { };
+ int err = 0;
+ u64 dev_time;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&get_dev_clk_time_msg;
+ args.in_args_size = sizeof(get_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(get_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ get_dev_clk_time_msg = *(struct virtchnl2_ptp_get_dev_clk_time *)args.out_buffer;
+ dev_time = LE64_TO_CPU(get_dev_clk_time_msg.dev_time_ns);
+ dev_clk_time->dev_clk_time_ns = dev_time;
+
+ return err;
+}
+
+/**
+ * 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_cmd_info args = { };
+ int err = 0;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CROSS_TIME;
+ args.in_args = (uint8_t *)&cross_time_msg;
+ args.in_args_size = sizeof(cross_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(cross_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ cross_time_msg = *(struct virtchnl2_ptp_get_cross_time *)args.out_buffer;
+ 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 err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err = 0;
+
+ set_dev_clk_time_msg.dev_time_ns = CPU_TO_LE64(time);
+
+ args.ops = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&set_dev_clk_time_msg;
+ args.in_args_size = sizeof(set_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(set_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err = 0;
+
+ adj_dev_clk_time_msg.delta = CPU_TO_LE64(delta);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&adj_dev_clk_time_msg;
+ args.in_args_size = sizeof(adj_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err;
+
+ adj_dev_clk_fine_msg.incval = CPU_TO_LE64(incval);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE;
+ args.in_args = (uint8_t *)&adj_dev_clk_fine_msg;
+ args.in_args_size = sizeof(adj_dev_clk_fine_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_fine_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
+ enum idpf_ptp_access tstamp_access, get_dev_clk_access;
+ struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
+ struct idpf_ptp *ptp = vport->adapter->ptp;
+ struct idpf_cmd_info args = { };
+ int err = 0;
+ u16 num_latches, i;
+ 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;
+
+ send_tx_tstamp_caps.vport_id = CPU_TO_LE32(vport->vport_id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS;
+ args.in_args = (uint8_t *)&send_tx_tstamp_caps;
+ args.in_args_size = sizeof(send_tx_tstamp_caps);
+ args.out_size = IDPF_CTLQ_MAX_BUF_LEN;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ if (err < 0)
+ return err;
+
+ rcv_tx_tstamp_caps = (struct virtchnl2_ptp_get_vport_tx_tstamp_caps *)
+ args.out_buffer;
+ num_latches = LE16_TO_CPU(rcv_tx_tstamp_caps->num_latches);
+ size = sizeof(struct idpf_ptp_vport_tx_tstamp_caps) +
+ sizeof(struct idpf_ptp_tx_tstamp) * num_latches;
+ tstamp_caps = rte_zmalloc(NULL, size, 0);
+ if (!tstamp_caps)
+ return -ENOMEM;
+
+ tstamp_caps->access = true;
+ tstamp_caps->num_entries = num_latches;
+
+ tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ for (i = 0; i < tstamp_caps->num_entries; i++) {
+ __le32 offset_l, offset_h;
+
+ tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i];
+
+ if (tstamp_access == IDPF_PTP_DIRECT) {
+ offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
+ offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_l = LE32_TO_CPU(offset_l);
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_h = LE32_TO_CPU(offset_h);
+ }
+ tstamp_caps->tx_tstamp[i].idx = tx_tstamp_latch_caps.index;
+ }
+
+ tstamp_caps->latched_idx = -1;
+ vport->tx_tstamp_caps = tstamp_caps;
+
+ return err;
+}
+
+/**
+ * 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
+ * @tx_tstamp: Tx timestamp structure to be fulfilled with the timestamp value
+ *
+ * Read the value of the Tx timestamp for a given latch received from the
+ * Control Plane.
+ *
+ * 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 *tx_tstamp)
+{
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ u8 tstamp_ns_lo_bit;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ tx_tstamp->tstamp = LE64_TO_CPU(tstamp_latch->tstamp);
+ tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
+
+ return 0;
+}
+
+/**
+ * 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 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 *ptp_tx_tstamp;
+ struct idpf_cmd_info args = { };
+ int size, msg_size;
+ u32 vport_id;
+ u16 num_latches, id;
+ int err = 0;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+
+ size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) *
+ tx_tstamp_caps->num_entries;
+ send_tx_tstamp_msg = rte_zmalloc(NULL, size, 0);
+ if (!send_tx_tstamp_msg)
+ return -ENOMEM;
+
+ for (id = 0; id < tx_tstamp_caps->num_entries; id++,
+ ptp_tx_tstamp++)
+ send_tx_tstamp_msg->tstamp_latches[id].index =
+ ptp_tx_tstamp->idx;
+ send_tx_tstamp_msg->get_devtime_with_txtstmp = 1;
+
+ msg_size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) * id;
+ send_tx_tstamp_msg->vport_id = CPU_TO_LE32(vport->vport_id);
+ send_tx_tstamp_msg->num_latches = CPU_TO_LE16(id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP;
+ args.in_args = (uint8_t *)send_tx_tstamp_msg;
+ args.in_args_size = msg_size;
+ args.out_size = msg_size;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ rte_free(send_tx_tstamp_msg);
+ if (err < 0)
+ return err;
+
+ recv_tx_tstamp_msg = (struct virtchnl2_ptp_get_vport_tx_tstamp_latches *)
+ args.out_buffer;
+ vport_id = LE32_TO_CPU(recv_tx_tstamp_msg->vport_id);
+ if (vport->vport_id != vport_id)
+ return -EINVAL;
+
+ num_latches = LE16_TO_CPU(recv_tx_tstamp_msg->num_latches);
+
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+ for (id = 0; id < num_latches; id++, ptp_tx_tstamp++) {
+ tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[id];
+
+ if (!tstamp_latch.valid)
+ continue;
+
+ err = idpf_ptp_get_tstamp_value(vport, &tstamp_latch,
+ ptp_tx_tstamp);
+ if (!err) {
+ tx_tstamp_caps->latched_idx = id;
+ vport->adapter->time_hw = recv_tx_tstamp_msg->device_time;
+ }
+ break;
+ }
+ return err;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer value
+ * @adapter: Driver specific private structure
+ *
+ * Return: the device clock time.
+ */
+static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+ u32 hi, lo;
+
+ idpf_ptp_enable_shtime(adapter);
+
+ lo = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_l);
+ hi = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_h);
+
+ return ((u64)hi << 32) | lo;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value through mailbox
+ * @adapter: Driver specific private structure
+ * @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,
+ u64 *src_clk)
+{
+ struct idpf_ptp_dev_timers clk_time;
+ int err = 0;
+
+ err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
+ if (err)
+ return err;
+
+ *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
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: the device clock time on success, -errno otherwise.
+ */
+int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk)
+{
+ if (!adapter->ptp)
+ return 0;
+ 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, src_clk);
+ case IDPF_PTP_DIRECT:
+ *src_clk = idpf_ptp_read_src_clk_reg_direct(adapter);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/intel/idpf/idpf_ptp.h b/drivers/net/intel/idpf/idpf_ptp.h
new file mode 100644
index 0000000000..3fc000a5a6
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.h
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2001-2025 Intel Corporation
+ */
+
+#ifndef _IDPF_PTP_H_
+#define _IDPF_PTP_H_
+
+#include "idpf_osdep.h"
+#include "rte_time.h"
+#include "idpf_common_device.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
+ * @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
+ */
+struct idpf_ptp_dev_clk_regs {
+ /* Main clock */
+ volatile uint32_t *dev_clk_ns_l;
+ volatile uint32_t *dev_clk_ns_h;
+
+ /* PHY timer */
+ volatile uint32_t *phy_clk_ns_l;
+ volatile uint32_t *phy_clk_ns_h;
+
+ /* System time */
+ volatile uint32_t *sys_time_ns_l;
+ volatile uint32_t *sys_time_ns_h;
+
+ /* Main timer adjustments */
+ volatile uint32_t *incval_l;
+ volatile uint32_t *incval_h;
+ volatile uint32_t *shadj_l;
+ volatile uint32_t *shadj_h;
+
+ /* PHY timer adjustments */
+ volatile uint32_t *phy_incval_l;
+ volatile uint32_t *phy_incval_h;
+ volatile uint32_t *phy_shadj_l;
+ volatile uint32_t *phy_shadj_h;
+
+ /* Command */
+ volatile uint32_t *cmd;
+ volatile uint32_t *phy_cmd;
+ volatile uint32_t *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_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;
+};
+
+/**
+ * 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 - 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
+ * @tstamp: the Tx tstamp value
+ * @idx: the index of the Tx tstamp
+ */
+struct idpf_ptp_tx_tstamp {
+ u32 tx_latch_reg_offset_l;
+ u32 tx_latch_reg_offset_h;
+ 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
+ * @access: indicates an access to Tx timestamp
+ * @latches_index: the index of the latched Tx timestamps
+ * @tx_tstamp: array of Tx timestamp parameters
+ */
+struct idpf_ptp_vport_tx_tstamp_caps {
+ u32 vport_id;
+ u16 num_entries;
+ u16 tstamp_ns_lo_bit;
+ bool access:1;
+ u16 latched_idx;
+ struct idpf_ptp_tx_tstamp tx_tstamp[];
+};
+
+/**
+ * struct idpf_ptp - PTP parameters
+ * @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
+ * @tx_tstamp_access: access type for the Tx timestamp value read
+ * @rsv: Reserved fields
+ * @secondary_mbx: parameters for using dedicated PTP mailbox
+ */
+struct idpf_ptp {
+ 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;
+ enum idpf_ptp_access set_dev_clk_time_access:2;
+ enum idpf_ptp_access adj_dev_clk_time_access:2;
+ enum idpf_ptp_access tx_tstamp_access:2;
+ u8 rsv:6;
+ struct idpf_ptp_secondary_mbx secondary_mbx;
+};
+
+/**
+ * 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;
+};
+
+int idpf_ptp_get_caps(struct idpf_adapter *adapter);
+int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk);
+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);
+int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport);
+int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
+
+/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
+static inline uint64_t
+idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
+ bool is_rx, uint32_t in_timestamp)
+{
+ const uint64_t mask = 0xFFFFFFFF;
+ uint32_t phc_time_lo, delta;
+ uint64_t ns;
+
+ if (flag != 0)
+ idpf_ptp_read_src_clk_reg(ad, &ad->time_hw);
+
+ /* Extract the lower 32 bits of the PHC time */
+ phc_time_lo = (uint32_t)(ad->time_hw);
+
+ /* Calculate the delta between the lower 32bits of the cached PHC
+ * time and the in_timestamp value.
+ */
+ delta = in_timestamp - phc_time_lo;
+
+ if (delta > mask / 2) {
+ /* Reverse the delta calculation here */
+ delta = phc_time_lo - in_timestamp;
+ ns = ad->time_hw - delta;
+ } else {
+ if (is_rx)
+ ns = ad->time_hw - delta;
+ else
+ ns = ad->time_hw + delta;
+ }
+
+ return ns;
+}
+#endif /* _IDPF_PTP_H_ */
diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
index a805d02ea2..5a4a3c2259 100644
--- a/drivers/net/intel/idpf/meson.build
+++ b/drivers/net/intel/idpf/meson.build
@@ -18,6 +18,7 @@ sources += files(
'idpf_ethdev.c',
'idpf_rxtx.c',
+ 'idpf_ptp.c',
)
if arch_subdir == 'x86' and dpdk_conf.get('RTE_IOVA_IN_MBUF') == 1
--
2.34.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v1 3/4] net/intel: add support for Precision Time Protocol
2025-10-24 12:08 [PATCH v1 0/4] Enable PTP feature for MEV Soumyadeep Hore
2025-10-24 12:08 ` [PATCH v1 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
2025-10-24 12:08 ` [PATCH v1 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
@ 2025-10-24 12:08 ` Soumyadeep Hore
2025-10-24 12:08 ` [PATCH v1 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
3 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-10-24 12:08 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Add support for PTP that contains negotiation with CP to receive a
set of supported functionalities through capabilities,
get/set/adjust the time of the main timer and Tx timestamping
through secondary mailbox dedicated to PTP.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++++++++------
drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
drivers/net/intel/idpf/idpf_ethdev.c | 275 ++++++++++++++++++++++
4 files changed, 401 insertions(+), 71 deletions(-)
diff --git a/drivers/net/intel/common/tx.h b/drivers/net/intel/common/tx.h
index 5af64a4cfe..fe175f7d65 100644
--- a/drivers/net/intel/common/tx.h
+++ b/drivers/net/intel/common/tx.h
@@ -113,6 +113,7 @@ struct ci_tx_queue {
uint16_t sw_tail;
uint16_t rs_compl_count;
uint8_t expected_gen_id;
+ uint32_t latch_idx; /* Tx timestamp latch index */
};
};
};
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.c b/drivers/net/intel/idpf/idpf_common_rxtx.c
index a5d0795057..da41b02af4 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.c
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.c
@@ -9,6 +9,7 @@
#include "idpf_common_rxtx.h"
#include "idpf_common_device.h"
#include "../common/rx.h"
+#include "idpf_ptp.h"
int idpf_timestamp_dynfield_offset = -1;
uint64_t idpf_timestamp_dynflag;
@@ -435,58 +436,6 @@ idpf_qc_split_rxq_mbufs_alloc(struct idpf_rx_queue *rxq)
return 0;
}
-#define IDPF_TIMESYNC_REG_WRAP_GUARD_BAND 10000
-/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
-static inline uint64_t
-idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
- uint32_t in_timestamp)
-{
-#ifdef RTE_ARCH_X86_64
- struct idpf_hw *hw = &ad->hw;
- const uint64_t mask = 0xFFFFFFFF;
- uint32_t hi, lo, lo2, delta;
- uint64_t ns;
-
- if (flag != 0) {
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_EXEC_CMD_M |
- PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- /*
- * On typical system, the delta between lo and lo2 is ~1000ns,
- * so 10000 seems a large-enough but not overly-big guard band.
- */
- if (lo > (UINT32_MAX - IDPF_TIMESYNC_REG_WRAP_GUARD_BAND))
- lo2 = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- else
- lo2 = lo;
-
- if (lo2 < lo) {
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- }
-
- ad->time_hw = ((uint64_t)hi << 32) | lo;
- }
-
- delta = (in_timestamp - (uint32_t)(ad->time_hw & mask));
- if (delta > (mask / 2)) {
- delta = ((uint32_t)(ad->time_hw & mask) - in_timestamp);
- ns = ad->time_hw - delta;
- } else {
- ns = ad->time_hw + delta;
- }
-
- return ns;
-#else /* !RTE_ARCH_X86_64 */
- RTE_SET_USED(ad);
- RTE_SET_USED(flag);
- RTE_SET_USED(in_timestamp);
- return 0;
-#endif /* RTE_ARCH_X86_64 */
-}
-
#define IDPF_RX_FLEX_DESC_ADV_STATUS0_XSUM_S \
(RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S) | \
RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S) | \
@@ -655,8 +604,12 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rx_desc_ring = rxq->rx_ring;
ptype_tbl = rxq->adapter->ptype_tbl;
- if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
- rxq->hw_register_set = 1;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
while (nb_rx < nb_pkts) {
rx_desc = &rx_desc_ring[rx_id];
@@ -732,20 +685,38 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(rte_le_to_cpu_16(rx_desc->ptype_err_fflags0) &
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M) >>
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_S];
+
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
status_err0_qw1 = rte_le_to_cpu_16(rx_desc->status_err0_qw1);
pkt_flags = idpf_splitq_rx_csum_offload(status_err0_qw1);
pkt_flags |= idpf_splitq_rx_rss_offload(rxm, rx_desc);
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP)) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rx_desc->ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rx_desc->ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ if (rx_desc->ts_low & VIRTCHNL2_RX_FLEX_TSTAMP_VALID)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -839,10 +810,34 @@ idpf_split_tx_free(struct ci_tx_queue *cq)
static inline uint16_t
idpf_calc_context_desc(uint64_t flags)
{
- if ((flags & RTE_MBUF_F_TX_TCP_SEG) != 0)
- return 1;
+ static uint64_t mask = RTE_MBUF_F_TX_TCP_SEG |
+ RTE_MBUF_F_TX_IEEE1588_TMST;
- return 0;
+ return (flags & mask) ? 1 : 0;
+}
+
+/**
+ * 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 inline void
+idpf_tx_set_tstamp_desc(volatile union idpf_flex_tx_ctx_desc *ctx_desc,
+ uint32_t idx)
+{
+ ctx_desc->tsyn.qw1.cmd_dtype =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_CMD_M,
+ IDPF_TX_CTX_DESC_TSYN));
+ ctx_desc->tsyn.qw1.cmd_dtype |=
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_DTYPE_M,
+ IDPF_TX_DESC_DTYPE_CTX));
+ ctx_desc->tsyn.qw1.tsyn_reg_l =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_L_M,
+ idx));
+ ctx_desc->tsyn.qw1.tsyn_reg_h =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_H_M,
+ idx >> 2));
}
/* set TSO context descriptor
@@ -948,6 +943,9 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_desc);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_desc, txq->latch_idx);
+
tx_id++;
if (tx_id == txq->nb_tx_desc)
tx_id = 0;
@@ -1104,8 +1102,12 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rx_ring = rxq->rx_ring;
ptype_tbl = rxq->adapter->ptype_tbl;
- if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
- rxq->hw_register_set = 1;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
@@ -1167,17 +1169,33 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rxm->ol_flags |= pkt_flags;
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
rxm->ol_flags |= idpf_timestamp_dynflag;
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
rx_pkts[nb_rx++] = rxm;
@@ -1218,6 +1236,13 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
if (unlikely(!rxq) || unlikely(!rxq->q_started))
return nb_rx;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
+
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
rx_status0 = rte_le_to_cpu_16(rxdp->flex_nic_wb.status_error0);
@@ -1298,17 +1323,33 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(uint8_t)(rte_cpu_to_le_16(rxd.flex_nic_wb.ptype_flex_flags0) &
VIRTCHNL2_RX_FLEX_DESC_PTYPE_M)];
+ if ((first_seg->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -1474,6 +1515,9 @@ idpf_dp_singleq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_txd);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_txd, txq->latch_idx);
+
txe->last_id = tx_last;
tx_id = txe->next_id;
txe = txn;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.h b/drivers/net/intel/idpf/idpf_common_rxtx.h
index 3bc3323af4..f0812befc5 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.h
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.h
@@ -13,6 +13,12 @@
#include "../common/tx.h"
#include "../common/rx.h"
+#define FIELD_PREP(_mask, _val) \
+ (__extension__ ({ \
+ typeof(_mask) _x = (_mask); \
+ ((typeof(_x))(_val) << rte_bsf32(_x)) & (_x); \
+ }))
+
#define IDPF_RX_MAX_BURST 32
#define IDPF_RX_OFFLOAD_IPV4_CKSUM RTE_BIT64(1)
@@ -156,6 +162,10 @@ struct idpf_rx_queue {
uint64_t offloads;
uint32_t hw_register_set;
+ uint32_t time_high; /* high 32 bits of hardware timestamp register */
+ uint32_t hw_time_high; /* high 32 bits of timestamp */
+ uint32_t hw_time_low; /* low 32 bits of timestamp */
+ uint64_t hw_time_update; /* Last time HW timestamp was updated */
};
/* Offload features */
diff --git a/drivers/net/intel/idpf/idpf_ethdev.c b/drivers/net/intel/idpf/idpf_ethdev.c
index 5d786fbba6..22b7a1093c 100644
--- a/drivers/net/intel/idpf/idpf_ethdev.c
+++ b/drivers/net/intel/idpf/idpf_ethdev.c
@@ -2,6 +2,8 @@
* Copyright(c) 2022 Intel Corporation
*/
+#include <math.h>
+
#include <rte_atomic.h>
#include <rte_eal.h>
#include <rte_ether.h>
@@ -14,6 +16,7 @@
#include "idpf_ethdev.h"
#include "idpf_rxtx.h"
#include "../common/tx.h"
+#include "idpf_ptp.h"
#define IDPF_TX_SINGLE_Q "tx_single"
#define IDPF_RX_SINGLE_Q "rx_single"
@@ -841,6 +844,270 @@ idpf_dev_close(struct rte_eth_dev *dev)
return 0;
}
+static int
+idpf_timesync_enable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct timespec sys_ts;
+ uint64_t ns;
+ int ret = 0, q_id = 0;
+
+ if (dev->data->dev_started && !(dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP)) {
+ PMD_DRV_LOG(ERR, "Rx timestamp offload not configured");
+ return -1;
+ }
+
+ adapter->ptp = rte_zmalloc(NULL, sizeof(struct idpf_ptp), 0);
+ if (adapter->ptp == NULL) {
+ PMD_DRV_LOG(ERR, "Failed to allocate memory for PTP");
+ return -ENOMEM;
+ }
+
+ ret = idpf_ptp_get_caps(adapter);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get PTP capabilities, err=%d", ret);
+ goto err_ptp;
+ }
+ /* Write the default increment time value if the clock adjustments
+ * are enabled.
+ */
+ if (adapter->ptp->adj_dev_clk_time_access != IDPF_PTP_NONE) {
+ ret = idpf_ptp_adj_dev_clk_fine(adapter,
+ adapter->ptp->base_incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set incval failed, err=%d", ret);
+ goto err_ptp;
+ }
+ }
+
+ /* Do not initialize the PTP if the device clock time cannot be read. */
+ if (adapter->ptp->get_dev_clk_time_access == IDPF_PTP_NONE) {
+ PMD_DRV_LOG(ERR, "Getting device clock time is not supported");
+ ret = -EIO;
+ goto err_ptp;
+ }
+
+ /* Set the device clock time to system time. */
+ if (adapter->ptp->set_dev_clk_time_access != IDPF_PTP_NONE) {
+ clock_gettime(CLOCK_REALTIME, &sys_ts);
+ ns = rte_timespec_to_ns(&sys_ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err=%d", ret);
+ goto err_ptp;
+ }
+ }
+
+ ret = idpf_ptp_get_vport_tstamps_caps(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get vport timestamp capabilities, err=%d", ret);
+ goto err_ptp;
+ }
+
+ for (q_id = 0; q_id < dev->data->nb_tx_queues; q_id++) {
+ struct ci_tx_queue *txq = dev->data->tx_queues[q_id];
+ txq->latch_idx = vport->tx_tstamp_caps->tx_tstamp[q_id].idx;
+ }
+
+ 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;
+
+err_ptp:
+ if (ret != 0) {
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ }
+ return ret;
+}
+
+static int
+idpf_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp,
+ uint32_t flags)
+{
+ struct idpf_rx_queue *rxq;
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ts_ns;
+
+ rxq = dev->data->rx_queues[flags];
+
+ ts_ns = idpf_tstamp_convert_32b_64b(adapter, 1, true, rxq->time_high);
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ return 0;
+}
+
+static int
+idpf_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ uint16_t latch_idx;
+ uint64_t ts_ns, tstamp;
+ int ret = 0;
+
+ ret = idpf_ptp_get_tx_tstamp(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get TX timestamp");
+ return ret;
+ }
+
+ latch_idx = vport->tx_tstamp_caps->latched_idx;
+ tstamp = vport->tx_tstamp_caps->tx_tstamp[latch_idx].tstamp;
+ ts_ns = idpf_tstamp_convert_32b_64b(vport->adapter, 0, false, tstamp);
+
+ /* Convert to timespec */
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ vport->tx_tstamp_caps->latched_idx = -1;
+
+ return 0;
+}
+
+static int
+idpf_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint64_t time, ns;
+ int ret = 0;
+
+ if (ptp->adj_dev_clk_time_access != IDPF_PTP_MAILBOX) {
+ PMD_DRV_LOG(ERR, "Adjusting device clock time is not supported");
+ return -ENOTSUP;
+ }
+
+ if (delta > INT32_MAX || delta < INT32_MIN) {
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP read clock time failed, err %d", ret);
+ return ret;
+ }
+
+ ns = time + delta;
+
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err %d", ret);
+
+ return ret;
+ }
+
+ ret = idpf_ptp_adj_dev_clk_time(adapter, delta);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP adjusting clock failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ int64_t incval, diff = 0;
+ bool negative = false;
+ uint64_t div, rem;
+ uint64_t divisor = 1000000ULL << 16;
+ int shift;
+ int ret = 0;
+
+ incval = ptp->base_incval;
+
+ if (ppm < 0) {
+ negative = true;
+ ppm = -ppm;
+ }
+
+ /* can incval * ppm overflow ? */
+ if (log2(incval) + log2(ppm) > 62) {
+ rem = ppm % divisor;
+ div = ppm / divisor;
+ diff = div * incval;
+ ppm = rem;
+
+ shift = log2(incval) + log2(ppm) - 62;
+ if (shift > 0) {
+ /* drop precision */
+ ppm >>= shift;
+ divisor >>= shift;
+ }
+ }
+
+ if (divisor)
+ diff = diff + incval * ppm / divisor;
+
+ if (negative)
+ incval -= diff;
+ else
+ incval += diff;
+
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int
+idpf_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ns;
+ int ret = 0;
+
+ ns = rte_timespec_to_ns(ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP write time failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t time;
+ int ret = 0;
+
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP read time failed, err %d", ret);
+ else
+ *ts = rte_ns_to_timespec(time);
+
+ return ret;
+}
+
+static int
+idpf_timesync_disable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+
+ if (vport->tx_tstamp_caps) {
+ rte_free(vport->tx_tstamp_caps);
+ vport->tx_tstamp_caps = NULL;
+ }
+
+ if (adapter->ptp) {
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ }
+
+ return 0;
+}
+
+
static const struct eth_dev_ops idpf_eth_dev_ops = {
.dev_configure = idpf_dev_configure,
.dev_close = idpf_dev_close,
@@ -867,6 +1134,14 @@ static const struct eth_dev_ops idpf_eth_dev_ops = {
.xstats_get = idpf_dev_xstats_get,
.xstats_get_names = idpf_dev_xstats_get_names,
.xstats_reset = idpf_dev_xstats_reset,
+ .timesync_enable = idpf_timesync_enable,
+ .timesync_read_rx_timestamp = idpf_timesync_read_rx_timestamp,
+ .timesync_read_tx_timestamp = idpf_timesync_read_tx_timestamp,
+ .timesync_adjust_time = idpf_timesync_adjust_time,
+ .timesync_adjust_freq = idpf_timesync_adjust_freq,
+ .timesync_read_time = idpf_timesync_read_time,
+ .timesync_write_time = idpf_timesync_write_time,
+ .timesync_disable = idpf_timesync_disable,
};
static int
--
2.34.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v1 4/4] doc: add PTP IDPF documentation
2025-10-24 12:08 [PATCH v1 0/4] Enable PTP feature for MEV Soumyadeep Hore
` (2 preceding siblings ...)
2025-10-24 12:08 ` [PATCH v1 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
@ 2025-10-24 12:08 ` Soumyadeep Hore
2025-10-24 16:28 ` Stephen Hemminger
2025-10-28 6:07 ` [PATCH v2 0/4] Enable PTP feature for MEV Soumyadeep Hore
3 siblings, 2 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-10-24 12:08 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Updated the IDPF Documentation with PTP feature.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
doc/guides/nics/idpf.rst | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/doc/guides/nics/idpf.rst b/doc/guides/nics/idpf.rst
index b99cc18188..e76632904b 100644
--- a/doc/guides/nics/idpf.rst
+++ b/doc/guides/nics/idpf.rst
@@ -109,3 +109,19 @@ The paths are chosen based on 2 conditions:
A value "P" means the offload feature is not supported by vector path.
If any not supported features are used, idpf vector PMD is disabled
and the scalar paths are chosen.
+
+Time Synchronisation
+~~~~~~~~~~~~~~~~~~~~
+
+The system operator can run a PTP (Precision Time Protocol) client application
+to synchronise the time on the network card in ACC
+(and optionally the time on the system) to the PTP master.
+
+IDPF PMD supports PTP client applications that use the DPDK IEEE 1588 API
+to communicate with the PTP master clock.
+Note that PTP client application needs to run on PF in ACC
+and add the ``--force-max-simd-bitwidth=64`` startup parameter to disable vector mode.
+
+.. code-block:: console
+
+ examples/dpdk-ptpclient -l 0-3 -n 3 -a 0000:ec:00.1 --force-max-simd-bitwidth=64 -- -T 1 -p 0x1 -c 1
--
2.34.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* Re: [PATCH v1 4/4] doc: add PTP IDPF documentation
2025-10-24 12:08 ` [PATCH v1 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
@ 2025-10-24 16:28 ` Stephen Hemminger
2025-10-28 6:07 ` [PATCH v2 0/4] Enable PTP feature for MEV Soumyadeep Hore
1 sibling, 0 replies; 57+ messages in thread
From: Stephen Hemminger @ 2025-10-24 16:28 UTC (permalink / raw)
To: Soumyadeep Hore
Cc: dev, bruce.richardson, rajesh3.kumar, aman.deep.singh,
manoj.kumar.subbarao
On Fri, 24 Oct 2025 17:38:40 +0530
Soumyadeep Hore <soumyadeep.hore@intel.com> wrote:
> +Time Synchronisation
> +~~~~~~~~~~~~~~~~~~~~
> +
> +The system operator can run a PTP (Precision Time Protocol) client application
> +to synchronise the time on the network card in ACC
> +(and optionally the time on the system) to the PTP master.
The term PTP master is deprecated in current standard.
DPDK has worked to remove references to master/slave.
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH v2 0/4] Enable PTP feature for MEV
2025-10-24 12:08 ` [PATCH v1 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
2025-10-24 16:28 ` Stephen Hemminger
@ 2025-10-28 6:07 ` Soumyadeep Hore
2025-10-28 6:07 ` [PATCH v2 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
` (5 more replies)
1 sibling, 6 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-10-28 6:07 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Enabling basic PTP feature in IDPF PMD using virtchnl messages.
---
v2:
- Fixed essential checkpatch warnings
---
Milena Olech (1):
net/idpf: add a new API for PTP support
Soumyadeep Hore (3):
net/idpf: add PTP virtchnl2 support
net/intel: add support for Precision Time Protocol
doc: add PTP IDPF documentation
doc/guides/nics/idpf.rst | 16 +
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/base/virtchnl2.h | 324 ++++++---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++--
drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
drivers/net/intel/idpf/idpf_ethdev.c | 275 ++++++++
drivers/net/intel/idpf/idpf_ptp.c | 646 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 227 ++++++
drivers/net/intel/idpf/meson.build | 1 +
11 files changed, 1562 insertions(+), 162 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
--
2.47.1
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH v2 1/4] net/idpf: add a new API for PTP support
2025-10-28 6:07 ` [PATCH v2 0/4] Enable PTP feature for MEV Soumyadeep Hore
@ 2025-10-28 6:07 ` Soumyadeep Hore
2025-10-28 6:07 ` [PATCH v2 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
` (4 subsequent siblings)
5 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-10-28 6:07 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao,
Milena Olech
From: Milena Olech <milena.olech@intel.com>
Add PTP capabilities to determine which PTP features are enabled -
get/set the time, cross timestamp, adjust the clock and timestamping.
Create appropriate structures and opcodes to support features introduced by
capabilities.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
---
drivers/net/intel/idpf/base/virtchnl2.h | 324 +++++++++++++++++-------
1 file changed, 235 insertions(+), 89 deletions(-)
diff --git a/drivers/net/intel/idpf/base/virtchnl2.h b/drivers/net/intel/idpf/base/virtchnl2.h
index 6cfb4f56fa..0b01daee30 100644
--- a/drivers/net/intel/idpf/base/virtchnl2.h
+++ b/drivers/net/intel/idpf/base/virtchnl2.h
@@ -118,8 +118,14 @@ enum virtchnl2_op {
VIRTCHNL2_OP_DEL_QUEUE_GROUPS = 539,
VIRTCHNL2_OP_GET_PORT_STATS = 540,
/* TimeSync opcodes */
- VIRTCHNL2_OP_GET_PTP_CAPS = 541,
- VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES = 542,
+ 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,
};
#define VIRTCHNL2_RDMA_INVALID_QUEUE_IDX 0xFFFF
@@ -1813,123 +1819,180 @@ VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_promisc_info);
* enum virtchnl2_ptp_caps - PTP capabilities
*/
enum virtchnl2_ptp_caps {
- VIRTCHNL2_PTP_CAP_LEGACY_CROSS_TIME = BIT(0),
- VIRTCHNL2_PTP_CAP_PTM = BIT(1),
- VIRTCHNL2_PTP_CAP_DEVICE_CLOCK_CONTROL = BIT(2),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_DIRECT = BIT(3),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_VIRTCHNL = BIT(4),
-};
-
-/**
- * struct virtchnl2_ptp_legacy_cross_time_reg - Legacy cross time registers
- * offsets.
+ 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_legacy_cross_time_reg {
- __le32 shadow_time_0;
- __le32 shadow_time_l;
- __le32 shadow_time_h;
- __le32 cmd_sync;
+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(16, virtchnl2_ptp_legacy_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_clk_reg_offsets);
/**
- * struct virtchnl2_ptp_ptm_cross_time_reg - PTM cross time registers 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_ptm_cross_time_reg {
- __le32 art_l;
- __le32 art_h;
- __le32 cmd_sync;
+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_ptm_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_cross_time_reg_offsets);
/**
- * struct virtchnl2_ptp_device_clock_control - Registers needed to control the
- * main clock.
+ * 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_device_clock_control {
- __le32 cmd;
- __le32 incval_l;
- __le32 incval_h;
- __le32 shadj_l;
- __le32 shadj_h;
- u8 pad[4];
+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(24, virtchnl2_ptp_device_clock_control);
+VIRTCHNL2_CHECK_STRUCT_LEN(40, virtchnl2_ptp_clk_adj_reg_offsets);
/**
- * struct virtchnl2_ptp_tx_tstamp_entry - PTP TX timestamp entry
- * @tx_latch_register_base: TX latch register base
- * @tx_latch_register_offset: TX latch register offset
- * @index: Index
- * @pad: Padding
+ * 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_entry {
- __le32 tx_latch_register_base;
- __le32 tx_latch_register_offset;
+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_entry);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch_caps);
/**
- * struct virtchnl2_ptp_tx_tstamp - Structure that defines tx tstamp entries
+ * struct virtchnl2_ptp_get_vport_tx_tstamp_caps - Structure that defines Tx
+ * tstamp entries
+ * @vport_id: Vport number
* @num_latches: Total number of latches
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
- * @ptp_tx_tstamp_entries: Aarray of TX timestamp entries
+ * @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_tx_tstamp {
+struct virtchnl2_ptp_get_vport_tx_tstamp_caps {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
- struct virtchnl2_ptp_tx_tstamp_entry ptp_tx_tstamp_entries[STRUCT_VAR_LEN];
+ u8 tstamp_ns_lo_bit;
+ u8 tstamp_ns_hi_bit;
+ u8 pad[8];
+
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp,
- ptp_tx_tstamp_entries);
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_caps,
+ tstamp_latches);
/**
- * struct virtchnl2_get_ptp_caps - Get PTP capabilities
- * @ptp_caps: PTP capability bitmap. See enum virtchnl2_ptp_caps.
- * @pad: Padding
- * @legacy_cross_time_reg: Legacy cross time register
- * @ptm_cross_time_reg: PTM cross time register
- * @device_clock_control: Device clock control
- * @tx_tstamp: TX timestamp
+ * 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
*
- * PV/VF sends this message to negotiate PTP capabilities. CP updates bitmap
+ * PF/VF sends this message to negotiate PTP capabilities. CP updates bitmap
* with supported features and fulfills appropriate structures.
- *
- * Associated with VIRTCHNL2_OP_GET_PTP_CAPS.
- */
-struct virtchnl2_get_ptp_caps {
- __le32 ptp_caps;
+ * 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_legacy_cross_time_reg legacy_cross_time_reg;
- struct virtchnl2_ptp_ptm_cross_time_reg ptm_cross_time_reg;
- struct virtchnl2_ptp_device_clock_control device_clock_control;
- struct virtchnl2_ptp_tx_tstamp tx_tstamp;
+ 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_VAR_LEN(88, virtchnl2_get_ptp_caps,
- tx_tstamp.ptp_tx_tstamp_entries);
+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_h: Timestamp high
- * @tstamp_l: Timestamp low
+ * @tstamp: Timestamp value
* @index: Index
* @valid: Timestamp validity
- * @pad: Padding
+ * @pad: Padding for future extensions
*/
struct virtchnl2_ptp_tx_tstamp_latch {
- __le32 tstamp_h;
- __le32 tstamp_l;
+ __le64 tstamp;
u8 index;
u8 valid;
u8 pad[6];
@@ -1938,26 +2001,97 @@ struct virtchnl2_ptp_tx_tstamp_latch {
VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch);
/**
- * struct virtchnl2_ptp_tx_tstamp_latches - PTP TX timestamp latches
+ * 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
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
+ * @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_GET_PTP_TX_TSTAMP_LATCHES.
+ * Associated with VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP.
*/
-struct virtchnl2_ptp_tx_tstamp_latches {
+struct virtchnl2_ptp_get_vport_tx_tstamp_latches {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
+ u8 get_devtime_with_txtstmp;
+ u8 pad[1];
+ u64 device_time;
struct virtchnl2_ptp_tx_tstamp_latch tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp_latches,
+
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_latches,
tstamp_latches);
+/* VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME
+ * @dev_time_ns: Device clock time value in nanoseconds
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the time from the main timer
+ */
+struct virtchnl2_ptp_get_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_dev_clk_time);
+
+/* 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
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the cross time
+ */
+struct virtchnl2_ptp_get_cross_time {
+ __le64 sys_time_ns;
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_get_cross_time);
+
+/* VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME
+ * @dev_time_ns: Device time value expressed in nanoseconds to set
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to set the time of the main timer
+ */
+struct virtchnl2_ptp_set_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_set_dev_clk_time);
+
+/* 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);
+
+/* 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);
+
static inline const char *virtchnl2_op_str(__le32 v_opcode)
{
switch (v_opcode) {
@@ -2025,10 +2159,22 @@ static inline const char *virtchnl2_op_str(__le32 v_opcode)
return "VIRTCHNL2_OP_DEL_QUEUE_GROUPS";
case VIRTCHNL2_OP_GET_PORT_STATS:
return "VIRTCHNL2_OP_GET_PORT_STATS";
- case VIRTCHNL2_OP_GET_PTP_CAPS:
- return "VIRTCHNL2_OP_GET_PTP_CAPS";
- case VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES:
- return "VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES";
+ case VIRTCHNL2_OP_PTP_GET_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_CAPS";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP";
+ case VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_CROSS_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_CROSS_TIME";
+ case VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS";
default:
return "Unsupported (update virtchnl2.h)";
}
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v2 2/4] net/idpf: add PTP virtchnl2 support
2025-10-28 6:07 ` [PATCH v2 0/4] Enable PTP feature for MEV Soumyadeep Hore
2025-10-28 6:07 ` [PATCH v2 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
@ 2025-10-28 6:07 ` Soumyadeep Hore
2025-10-28 6:07 ` [PATCH v2 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
` (3 subsequent siblings)
5 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-10-28 6:07 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Add a new file - idpf_ptp - to handle PTP virtchnl messages.
Keep the registers addresses in the PTP struct.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
drivers/net/intel/idpf/idpf_ptp.c | 646 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 227 ++++++
drivers/net/intel/idpf/meson.build | 1 +
5 files changed, 910 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
diff --git a/drivers/net/intel/idpf/idpf_common_device.h b/drivers/net/intel/idpf/idpf_common_device.h
index c32dcfbb12..3c84b96225 100644
--- a/drivers/net/intel/idpf/idpf_common_device.h
+++ b/drivers/net/intel/idpf/idpf_common_device.h
@@ -90,6 +90,7 @@ struct idpf_adapter {
/* For timestamp */
uint64_t time_hw;
+ struct idpf_ptp *ptp;
enum idpf_rx_func_type rx_func_type;
};
@@ -161,6 +162,9 @@ struct idpf_vport {
/* Event from ipf */
bool link_up;
uint32_t link_speed;
+
+ /* For PTP */
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
};
/* Message type read in virtual channel from PF */
diff --git a/drivers/net/intel/idpf/idpf_common_virtchnl.c b/drivers/net/intel/idpf/idpf_common_virtchnl.c
index e927d7415a..a28bc9bee6 100644
--- a/drivers/net/intel/idpf/idpf_common_virtchnl.c
+++ b/drivers/net/intel/idpf/idpf_common_virtchnl.c
@@ -4,6 +4,7 @@
#include "idpf_common_virtchnl.h"
#include "idpf_common_logs.h"
+#include "idpf_ptp.h"
#include <eal_export.h>
@@ -38,6 +39,28 @@ idpf_vc_clean(struct idpf_adapter *adapter)
return 0;
}
+/**
+ * idpf_ptp_is_mb_msg - Check if the message is PTP-related
+ * @op: virtchnl opcode
+ *
+ * Returns true if msg is PTP-related, false otherwise
+ */
+static inline bool idpf_ptp_is_mb_msg(uint32_t op)
+{
+ switch (op) {
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ 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:
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int
idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
uint16_t msg_size, uint8_t *msg)
@@ -71,8 +94,15 @@ idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
memcpy(dma_mem->va, msg, msg_size);
- ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
- ctlq_msg->func_id = 0;
+ if (idpf_ptp_is_mb_msg(op) && adapter->ptp->secondary_mbx.valid) {
+ 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 {
+ ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
+ ctlq_msg->func_id = 0;
+ }
+
ctlq_msg->data_len = msg_size;
ctlq_msg->cookie.mbx.chnl_opcode = op;
ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
diff --git a/drivers/net/intel/idpf/idpf_ptp.c b/drivers/net/intel/idpf/idpf_ptp.c
new file mode 100644
index 0000000000..3a364adc93
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.c
@@ -0,0 +1,646 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2001-2025 Intel Corporation
+ */
+
+#include "idpf_ptp.h"
+#include <base/virtchnl2.h>
+#include "idpf_common_virtchnl.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.
+ */
+static 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);
+
+ /* 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);
+
+ /* Set the device clock time */
+ direct = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME;
+ mailbox = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB;
+ 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);
+
+ /* 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);
+}
+
+/**
+ * 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_cross_time_reg_offsets cross_tstamp_offsets;
+ struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
+ struct virtchnl2_ptp_get_caps send_ptp_caps_msg = { };
+ struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
+ struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
+ struct idpf_cmd_info args = { };
+ struct idpf_ptp_secondary_mbx *scnd_mbx;
+ struct idpf_ptp *ptp = adapter->ptp;
+ struct idpf_hw *hw = &adapter->hw;
+ enum idpf_ptp_access access_type;
+ int err = 0;
+ u32 temp_offset;
+
+ 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 |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CAPS;
+ args.in_args = (uint8_t *)&send_ptp_caps_msg;
+ args.in_args_size = sizeof(send_ptp_caps_msg);
+ recv_ptp_caps_msg = rte_zmalloc(NULL,
+ sizeof(struct virtchnl2_ptp_get_caps), 0);
+ if (!recv_ptp_caps_msg)
+ return -ENOMEM;
+
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(*recv_ptp_caps_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ goto free_mem;
+
+ recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
+ 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);
+
+ /* 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);
+
+ access_type = ptp->get_dev_clk_time_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ goto cross_tstamp;
+
+ 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_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.dev_clk_ns_h);
+ ptp->dev_clk_regs.dev_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_l);
+ ptp->dev_clk_regs.phy_clk_ns_l = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_h);
+ ptp->dev_clk_regs.phy_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+cross_tstamp:
+ access_type = ptp->get_cross_tstamp_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ goto discipline_clock;
+
+ 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_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.sys_time_ns_h);
+ ptp->dev_clk_regs.sys_time_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+discipline_clock:
+ access_type = ptp->adj_dev_clk_time_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ return err;
+
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_l);
+ ptp->dev_clk_regs.incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_h);
+ ptp->dev_clk_regs.incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_l);
+ ptp->dev_clk_regs.shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_h);
+ ptp->dev_clk_regs.shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+ /* PHY clock offsets */
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_cmd_type);
+ ptp->dev_clk_regs.phy_cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_l);
+ ptp->dev_clk_regs.phy_incval_l = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_h);
+ ptp->dev_clk_regs.phy_incval_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_l);
+ ptp->dev_clk_regs.phy_shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_h);
+ ptp->dev_clk_regs.phy_shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+free_mem:
+ rte_free(recv_ptp_caps_msg);
+
+ return err;
+}
+
+/**
+ * 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)
+{
+ uint32_t 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 */
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, shtime_enable);
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, exec_cmd | shtime_enable);
+}
+
+/**
+ * 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_cmd_info args = { };
+ int err = 0;
+ u64 dev_time;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&get_dev_clk_time_msg;
+ args.in_args_size = sizeof(get_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(get_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ get_dev_clk_time_msg = *(struct virtchnl2_ptp_get_dev_clk_time *)args.out_buffer;
+ dev_time = LE64_TO_CPU(get_dev_clk_time_msg.dev_time_ns);
+ dev_clk_time->dev_clk_time_ns = dev_time;
+
+ return err;
+}
+
+/**
+ * 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_cmd_info args = { };
+ int err = 0;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CROSS_TIME;
+ args.in_args = (uint8_t *)&cross_time_msg;
+ args.in_args_size = sizeof(cross_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(cross_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ cross_time_msg = *(struct virtchnl2_ptp_get_cross_time *)args.out_buffer;
+ 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 err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err = 0;
+
+ set_dev_clk_time_msg.dev_time_ns = CPU_TO_LE64(time);
+
+ args.ops = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&set_dev_clk_time_msg;
+ args.in_args_size = sizeof(set_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(set_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err = 0;
+
+ adj_dev_clk_time_msg.delta = CPU_TO_LE64(delta);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&adj_dev_clk_time_msg;
+ args.in_args_size = sizeof(adj_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err;
+
+ adj_dev_clk_fine_msg.incval = CPU_TO_LE64(incval);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE;
+ args.in_args = (uint8_t *)&adj_dev_clk_fine_msg;
+ args.in_args_size = sizeof(adj_dev_clk_fine_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_fine_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
+ enum idpf_ptp_access tstamp_access, get_dev_clk_access;
+ struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
+ struct idpf_ptp *ptp = vport->adapter->ptp;
+ struct idpf_cmd_info args = { };
+ int err = 0;
+ u16 num_latches, i;
+ 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;
+
+ send_tx_tstamp_caps.vport_id = CPU_TO_LE32(vport->vport_id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS;
+ args.in_args = (uint8_t *)&send_tx_tstamp_caps;
+ args.in_args_size = sizeof(send_tx_tstamp_caps);
+ args.out_size = IDPF_CTLQ_MAX_BUF_LEN;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ if (err < 0)
+ return err;
+
+ rcv_tx_tstamp_caps = (struct virtchnl2_ptp_get_vport_tx_tstamp_caps *)
+ args.out_buffer;
+ num_latches = LE16_TO_CPU(rcv_tx_tstamp_caps->num_latches);
+ size = sizeof(struct idpf_ptp_vport_tx_tstamp_caps) +
+ sizeof(struct idpf_ptp_tx_tstamp) * num_latches;
+ tstamp_caps = rte_zmalloc(NULL, size, 0);
+ if (!tstamp_caps)
+ return -ENOMEM;
+
+ tstamp_caps->access = true;
+ tstamp_caps->num_entries = num_latches;
+
+ tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ for (i = 0; i < tstamp_caps->num_entries; i++) {
+ __le32 offset_l, offset_h;
+
+ tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i];
+
+ if (tstamp_access == IDPF_PTP_DIRECT) {
+ offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
+ offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_l = LE32_TO_CPU(offset_l);
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_h = LE32_TO_CPU(offset_h);
+ }
+ tstamp_caps->tx_tstamp[i].idx = tx_tstamp_latch_caps.index;
+ }
+
+ tstamp_caps->latched_idx = -1;
+ vport->tx_tstamp_caps = tstamp_caps;
+
+ return err;
+}
+
+/**
+ * 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
+ * @tx_tstamp: Tx timestamp structure to be fulfilled with the timestamp value
+ *
+ * Read the value of the Tx timestamp for a given latch received from the
+ * Control Plane.
+ *
+ * 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 *tx_tstamp)
+{
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ u8 tstamp_ns_lo_bit;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ tx_tstamp->tstamp = LE64_TO_CPU(tstamp_latch->tstamp);
+ tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
+
+ return 0;
+}
+
+/**
+ * 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 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 *ptp_tx_tstamp;
+ struct idpf_cmd_info args = { };
+ int size, msg_size;
+ u32 vport_id;
+ u16 num_latches, id;
+ int err = 0;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+
+ size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) *
+ tx_tstamp_caps->num_entries;
+ send_tx_tstamp_msg = rte_zmalloc(NULL, size, 0);
+ if (!send_tx_tstamp_msg)
+ return -ENOMEM;
+
+ for (id = 0; id < tx_tstamp_caps->num_entries; id++,
+ ptp_tx_tstamp++)
+ send_tx_tstamp_msg->tstamp_latches[id].index =
+ ptp_tx_tstamp->idx;
+ send_tx_tstamp_msg->get_devtime_with_txtstmp = 1;
+
+ msg_size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) * id;
+ send_tx_tstamp_msg->vport_id = CPU_TO_LE32(vport->vport_id);
+ send_tx_tstamp_msg->num_latches = CPU_TO_LE16(id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP;
+ args.in_args = (uint8_t *)send_tx_tstamp_msg;
+ args.in_args_size = msg_size;
+ args.out_size = msg_size;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ rte_free(send_tx_tstamp_msg);
+ if (err < 0)
+ return err;
+
+ recv_tx_tstamp_msg = (struct virtchnl2_ptp_get_vport_tx_tstamp_latches *)
+ args.out_buffer;
+ vport_id = LE32_TO_CPU(recv_tx_tstamp_msg->vport_id);
+ if (vport->vport_id != vport_id)
+ return -EINVAL;
+
+ num_latches = LE16_TO_CPU(recv_tx_tstamp_msg->num_latches);
+
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+ for (id = 0; id < num_latches; id++, ptp_tx_tstamp++) {
+ tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[id];
+
+ if (!tstamp_latch.valid)
+ continue;
+
+ err = idpf_ptp_get_tstamp_value(vport, &tstamp_latch,
+ ptp_tx_tstamp);
+ if (!err) {
+ tx_tstamp_caps->latched_idx = id;
+ vport->adapter->time_hw = recv_tx_tstamp_msg->device_time;
+ }
+ break;
+ }
+ return err;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer value
+ * @adapter: Driver specific private structure
+ *
+ * Return: the device clock time.
+ */
+static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+ u32 hi, lo;
+
+ idpf_ptp_enable_shtime(adapter);
+
+ lo = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_l);
+ hi = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_h);
+
+ return ((u64)hi << 32) | lo;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value through mailbox
+ * @adapter: Driver specific private structure
+ * @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,
+ u64 *src_clk)
+{
+ struct idpf_ptp_dev_timers clk_time;
+ int err = 0;
+
+ err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
+ if (err)
+ return err;
+
+ *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
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: the device clock time on success, -errno otherwise.
+ */
+int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk)
+{
+ if (!adapter->ptp)
+ return 0;
+ 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, src_clk);
+ case IDPF_PTP_DIRECT:
+ *src_clk = idpf_ptp_read_src_clk_reg_direct(adapter);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/intel/idpf/idpf_ptp.h b/drivers/net/intel/idpf/idpf_ptp.h
new file mode 100644
index 0000000000..3fc000a5a6
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.h
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2001-2025 Intel Corporation
+ */
+
+#ifndef _IDPF_PTP_H_
+#define _IDPF_PTP_H_
+
+#include "idpf_osdep.h"
+#include "rte_time.h"
+#include "idpf_common_device.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
+ * @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
+ */
+struct idpf_ptp_dev_clk_regs {
+ /* Main clock */
+ volatile uint32_t *dev_clk_ns_l;
+ volatile uint32_t *dev_clk_ns_h;
+
+ /* PHY timer */
+ volatile uint32_t *phy_clk_ns_l;
+ volatile uint32_t *phy_clk_ns_h;
+
+ /* System time */
+ volatile uint32_t *sys_time_ns_l;
+ volatile uint32_t *sys_time_ns_h;
+
+ /* Main timer adjustments */
+ volatile uint32_t *incval_l;
+ volatile uint32_t *incval_h;
+ volatile uint32_t *shadj_l;
+ volatile uint32_t *shadj_h;
+
+ /* PHY timer adjustments */
+ volatile uint32_t *phy_incval_l;
+ volatile uint32_t *phy_incval_h;
+ volatile uint32_t *phy_shadj_l;
+ volatile uint32_t *phy_shadj_h;
+
+ /* Command */
+ volatile uint32_t *cmd;
+ volatile uint32_t *phy_cmd;
+ volatile uint32_t *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_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;
+};
+
+/**
+ * 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 - 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
+ * @tstamp: the Tx tstamp value
+ * @idx: the index of the Tx tstamp
+ */
+struct idpf_ptp_tx_tstamp {
+ u32 tx_latch_reg_offset_l;
+ u32 tx_latch_reg_offset_h;
+ 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
+ * @access: indicates an access to Tx timestamp
+ * @latches_index: the index of the latched Tx timestamps
+ * @tx_tstamp: array of Tx timestamp parameters
+ */
+struct idpf_ptp_vport_tx_tstamp_caps {
+ u32 vport_id;
+ u16 num_entries;
+ u16 tstamp_ns_lo_bit;
+ bool access:1;
+ u16 latched_idx;
+ struct idpf_ptp_tx_tstamp tx_tstamp[];
+};
+
+/**
+ * struct idpf_ptp - PTP parameters
+ * @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
+ * @tx_tstamp_access: access type for the Tx timestamp value read
+ * @rsv: Reserved fields
+ * @secondary_mbx: parameters for using dedicated PTP mailbox
+ */
+struct idpf_ptp {
+ 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;
+ enum idpf_ptp_access set_dev_clk_time_access:2;
+ enum idpf_ptp_access adj_dev_clk_time_access:2;
+ enum idpf_ptp_access tx_tstamp_access:2;
+ u8 rsv:6;
+ struct idpf_ptp_secondary_mbx secondary_mbx;
+};
+
+/**
+ * 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;
+};
+
+int idpf_ptp_get_caps(struct idpf_adapter *adapter);
+int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk);
+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);
+int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport);
+int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
+
+/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
+static inline uint64_t
+idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
+ bool is_rx, uint32_t in_timestamp)
+{
+ const uint64_t mask = 0xFFFFFFFF;
+ uint32_t phc_time_lo, delta;
+ uint64_t ns;
+
+ if (flag != 0)
+ idpf_ptp_read_src_clk_reg(ad, &ad->time_hw);
+
+ /* Extract the lower 32 bits of the PHC time */
+ phc_time_lo = (uint32_t)(ad->time_hw);
+
+ /* Calculate the delta between the lower 32bits of the cached PHC
+ * time and the in_timestamp value.
+ */
+ delta = in_timestamp - phc_time_lo;
+
+ if (delta > mask / 2) {
+ /* Reverse the delta calculation here */
+ delta = phc_time_lo - in_timestamp;
+ ns = ad->time_hw - delta;
+ } else {
+ if (is_rx)
+ ns = ad->time_hw - delta;
+ else
+ ns = ad->time_hw + delta;
+ }
+
+ return ns;
+}
+#endif /* _IDPF_PTP_H_ */
diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
index a805d02ea2..5a4a3c2259 100644
--- a/drivers/net/intel/idpf/meson.build
+++ b/drivers/net/intel/idpf/meson.build
@@ -18,6 +18,7 @@ sources += files(
'idpf_ethdev.c',
'idpf_rxtx.c',
+ 'idpf_ptp.c',
)
if arch_subdir == 'x86' and dpdk_conf.get('RTE_IOVA_IN_MBUF') == 1
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v2 3/4] net/intel: add support for Precision Time Protocol
2025-10-28 6:07 ` [PATCH v2 0/4] Enable PTP feature for MEV Soumyadeep Hore
2025-10-28 6:07 ` [PATCH v2 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
2025-10-28 6:07 ` [PATCH v2 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
@ 2025-10-28 6:07 ` Soumyadeep Hore
2025-10-28 6:07 ` [PATCH v2 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
` (2 subsequent siblings)
5 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-10-28 6:07 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Add support for PTP that contains negotiation with CP to receive a
set of supported functionalities through capabilities,
get/set/adjust the time of the main timer and Tx timestamping
through secondary mailbox dedicated to PTP.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++++++++------
drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
drivers/net/intel/idpf/idpf_ethdev.c | 275 ++++++++++++++++++++++
4 files changed, 401 insertions(+), 71 deletions(-)
diff --git a/drivers/net/intel/common/tx.h b/drivers/net/intel/common/tx.h
index 5af64a4cfe..fe175f7d65 100644
--- a/drivers/net/intel/common/tx.h
+++ b/drivers/net/intel/common/tx.h
@@ -113,6 +113,7 @@ struct ci_tx_queue {
uint16_t sw_tail;
uint16_t rs_compl_count;
uint8_t expected_gen_id;
+ uint32_t latch_idx; /* Tx timestamp latch index */
};
};
};
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.c b/drivers/net/intel/idpf/idpf_common_rxtx.c
index a5d0795057..c8cd5b9d5b 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.c
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.c
@@ -9,6 +9,7 @@
#include "idpf_common_rxtx.h"
#include "idpf_common_device.h"
#include "../common/rx.h"
+#include "idpf_ptp.h"
int idpf_timestamp_dynfield_offset = -1;
uint64_t idpf_timestamp_dynflag;
@@ -435,58 +436,6 @@ idpf_qc_split_rxq_mbufs_alloc(struct idpf_rx_queue *rxq)
return 0;
}
-#define IDPF_TIMESYNC_REG_WRAP_GUARD_BAND 10000
-/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
-static inline uint64_t
-idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
- uint32_t in_timestamp)
-{
-#ifdef RTE_ARCH_X86_64
- struct idpf_hw *hw = &ad->hw;
- const uint64_t mask = 0xFFFFFFFF;
- uint32_t hi, lo, lo2, delta;
- uint64_t ns;
-
- if (flag != 0) {
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_EXEC_CMD_M |
- PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- /*
- * On typical system, the delta between lo and lo2 is ~1000ns,
- * so 10000 seems a large-enough but not overly-big guard band.
- */
- if (lo > (UINT32_MAX - IDPF_TIMESYNC_REG_WRAP_GUARD_BAND))
- lo2 = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- else
- lo2 = lo;
-
- if (lo2 < lo) {
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- }
-
- ad->time_hw = ((uint64_t)hi << 32) | lo;
- }
-
- delta = (in_timestamp - (uint32_t)(ad->time_hw & mask));
- if (delta > (mask / 2)) {
- delta = ((uint32_t)(ad->time_hw & mask) - in_timestamp);
- ns = ad->time_hw - delta;
- } else {
- ns = ad->time_hw + delta;
- }
-
- return ns;
-#else /* !RTE_ARCH_X86_64 */
- RTE_SET_USED(ad);
- RTE_SET_USED(flag);
- RTE_SET_USED(in_timestamp);
- return 0;
-#endif /* RTE_ARCH_X86_64 */
-}
-
#define IDPF_RX_FLEX_DESC_ADV_STATUS0_XSUM_S \
(RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S) | \
RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S) | \
@@ -655,8 +604,12 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rx_desc_ring = rxq->rx_ring;
ptype_tbl = rxq->adapter->ptype_tbl;
- if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
- rxq->hw_register_set = 1;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
while (nb_rx < nb_pkts) {
rx_desc = &rx_desc_ring[rx_id];
@@ -732,20 +685,38 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(rte_le_to_cpu_16(rx_desc->ptype_err_fflags0) &
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M) >>
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_S];
+
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
status_err0_qw1 = rte_le_to_cpu_16(rx_desc->status_err0_qw1);
pkt_flags = idpf_splitq_rx_csum_offload(status_err0_qw1);
pkt_flags |= idpf_splitq_rx_rss_offload(rxm, rx_desc);
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP)) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rx_desc->ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rx_desc->ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ if (rx_desc->ts_low & VIRTCHNL2_RX_FLEX_TSTAMP_VALID)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -839,10 +810,34 @@ idpf_split_tx_free(struct ci_tx_queue *cq)
static inline uint16_t
idpf_calc_context_desc(uint64_t flags)
{
- if ((flags & RTE_MBUF_F_TX_TCP_SEG) != 0)
- return 1;
+ static uint64_t mask = RTE_MBUF_F_TX_TCP_SEG |
+ RTE_MBUF_F_TX_IEEE1588_TMST;
- return 0;
+ return (flags & mask) ? 1 : 0;
+}
+
+/**
+ * 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 inline void
+idpf_tx_set_tstamp_desc(volatile union idpf_flex_tx_ctx_desc *ctx_desc,
+ uint32_t idx)
+{
+ ctx_desc->tsyn.qw1.cmd_dtype =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_CMD_M,
+ IDPF_TX_CTX_DESC_TSYN));
+ ctx_desc->tsyn.qw1.cmd_dtype |=
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_DTYPE_M,
+ IDPF_TX_DESC_DTYPE_CTX));
+ ctx_desc->tsyn.qw1.tsyn_reg_l =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_L_M,
+ idx));
+ ctx_desc->tsyn.qw1.tsyn_reg_h =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_H_M,
+ idx >> 2));
}
/* set TSO context descriptor
@@ -948,6 +943,9 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_desc);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_desc, txq->latch_idx);
+
tx_id++;
if (tx_id == txq->nb_tx_desc)
tx_id = 0;
@@ -1104,8 +1102,12 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rx_ring = rxq->rx_ring;
ptype_tbl = rxq->adapter->ptype_tbl;
- if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
- rxq->hw_register_set = 1;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
@@ -1167,17 +1169,33 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rxm->ol_flags |= pkt_flags;
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
rxm->ol_flags |= idpf_timestamp_dynflag;
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
rx_pkts[nb_rx++] = rxm;
@@ -1218,6 +1236,13 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
if (unlikely(!rxq) || unlikely(!rxq->q_started))
return nb_rx;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
+
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
rx_status0 = rte_le_to_cpu_16(rxdp->flex_nic_wb.status_error0);
@@ -1298,17 +1323,33 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(uint8_t)(rte_cpu_to_le_16(rxd.flex_nic_wb.ptype_flex_flags0) &
VIRTCHNL2_RX_FLEX_DESC_PTYPE_M)];
+ if ((first_seg->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -1474,6 +1515,9 @@ idpf_dp_singleq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_txd);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_txd, txq->latch_idx);
+
txe->last_id = tx_last;
tx_id = txe->next_id;
txe = txn;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.h b/drivers/net/intel/idpf/idpf_common_rxtx.h
index 3bc3323af4..f0812befc5 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.h
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.h
@@ -13,6 +13,12 @@
#include "../common/tx.h"
#include "../common/rx.h"
+#define FIELD_PREP(_mask, _val) \
+ (__extension__ ({ \
+ typeof(_mask) _x = (_mask); \
+ ((typeof(_x))(_val) << rte_bsf32(_x)) & (_x); \
+ }))
+
#define IDPF_RX_MAX_BURST 32
#define IDPF_RX_OFFLOAD_IPV4_CKSUM RTE_BIT64(1)
@@ -156,6 +162,10 @@ struct idpf_rx_queue {
uint64_t offloads;
uint32_t hw_register_set;
+ uint32_t time_high; /* high 32 bits of hardware timestamp register */
+ uint32_t hw_time_high; /* high 32 bits of timestamp */
+ uint32_t hw_time_low; /* low 32 bits of timestamp */
+ uint64_t hw_time_update; /* Last time HW timestamp was updated */
};
/* Offload features */
diff --git a/drivers/net/intel/idpf/idpf_ethdev.c b/drivers/net/intel/idpf/idpf_ethdev.c
index 5d786fbba6..22b7a1093c 100644
--- a/drivers/net/intel/idpf/idpf_ethdev.c
+++ b/drivers/net/intel/idpf/idpf_ethdev.c
@@ -2,6 +2,8 @@
* Copyright(c) 2022 Intel Corporation
*/
+#include <math.h>
+
#include <rte_atomic.h>
#include <rte_eal.h>
#include <rte_ether.h>
@@ -14,6 +16,7 @@
#include "idpf_ethdev.h"
#include "idpf_rxtx.h"
#include "../common/tx.h"
+#include "idpf_ptp.h"
#define IDPF_TX_SINGLE_Q "tx_single"
#define IDPF_RX_SINGLE_Q "rx_single"
@@ -841,6 +844,270 @@ idpf_dev_close(struct rte_eth_dev *dev)
return 0;
}
+static int
+idpf_timesync_enable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct timespec sys_ts;
+ uint64_t ns;
+ int ret = 0, q_id = 0;
+
+ if (dev->data->dev_started && !(dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP)) {
+ PMD_DRV_LOG(ERR, "Rx timestamp offload not configured");
+ return -1;
+ }
+
+ adapter->ptp = rte_zmalloc(NULL, sizeof(struct idpf_ptp), 0);
+ if (adapter->ptp == NULL) {
+ PMD_DRV_LOG(ERR, "Failed to allocate memory for PTP");
+ return -ENOMEM;
+ }
+
+ ret = idpf_ptp_get_caps(adapter);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get PTP capabilities, err=%d", ret);
+ goto err_ptp;
+ }
+ /* Write the default increment time value if the clock adjustments
+ * are enabled.
+ */
+ if (adapter->ptp->adj_dev_clk_time_access != IDPF_PTP_NONE) {
+ ret = idpf_ptp_adj_dev_clk_fine(adapter,
+ adapter->ptp->base_incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set incval failed, err=%d", ret);
+ goto err_ptp;
+ }
+ }
+
+ /* Do not initialize the PTP if the device clock time cannot be read. */
+ if (adapter->ptp->get_dev_clk_time_access == IDPF_PTP_NONE) {
+ PMD_DRV_LOG(ERR, "Getting device clock time is not supported");
+ ret = -EIO;
+ goto err_ptp;
+ }
+
+ /* Set the device clock time to system time. */
+ if (adapter->ptp->set_dev_clk_time_access != IDPF_PTP_NONE) {
+ clock_gettime(CLOCK_REALTIME, &sys_ts);
+ ns = rte_timespec_to_ns(&sys_ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err=%d", ret);
+ goto err_ptp;
+ }
+ }
+
+ ret = idpf_ptp_get_vport_tstamps_caps(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get vport timestamp capabilities, err=%d", ret);
+ goto err_ptp;
+ }
+
+ for (q_id = 0; q_id < dev->data->nb_tx_queues; q_id++) {
+ struct ci_tx_queue *txq = dev->data->tx_queues[q_id];
+ txq->latch_idx = vport->tx_tstamp_caps->tx_tstamp[q_id].idx;
+ }
+
+ 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;
+
+err_ptp:
+ if (ret != 0) {
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ }
+ return ret;
+}
+
+static int
+idpf_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp,
+ uint32_t flags)
+{
+ struct idpf_rx_queue *rxq;
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ts_ns;
+
+ rxq = dev->data->rx_queues[flags];
+
+ ts_ns = idpf_tstamp_convert_32b_64b(adapter, 1, true, rxq->time_high);
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ return 0;
+}
+
+static int
+idpf_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ uint16_t latch_idx;
+ uint64_t ts_ns, tstamp;
+ int ret = 0;
+
+ ret = idpf_ptp_get_tx_tstamp(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get TX timestamp");
+ return ret;
+ }
+
+ latch_idx = vport->tx_tstamp_caps->latched_idx;
+ tstamp = vport->tx_tstamp_caps->tx_tstamp[latch_idx].tstamp;
+ ts_ns = idpf_tstamp_convert_32b_64b(vport->adapter, 0, false, tstamp);
+
+ /* Convert to timespec */
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ vport->tx_tstamp_caps->latched_idx = -1;
+
+ return 0;
+}
+
+static int
+idpf_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint64_t time, ns;
+ int ret = 0;
+
+ if (ptp->adj_dev_clk_time_access != IDPF_PTP_MAILBOX) {
+ PMD_DRV_LOG(ERR, "Adjusting device clock time is not supported");
+ return -ENOTSUP;
+ }
+
+ if (delta > INT32_MAX || delta < INT32_MIN) {
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP read clock time failed, err %d", ret);
+ return ret;
+ }
+
+ ns = time + delta;
+
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err %d", ret);
+
+ return ret;
+ }
+
+ ret = idpf_ptp_adj_dev_clk_time(adapter, delta);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP adjusting clock failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ int64_t incval, diff = 0;
+ bool negative = false;
+ uint64_t div, rem;
+ uint64_t divisor = 1000000ULL << 16;
+ int shift;
+ int ret = 0;
+
+ incval = ptp->base_incval;
+
+ if (ppm < 0) {
+ negative = true;
+ ppm = -ppm;
+ }
+
+ /* can incval * ppm overflow ? */
+ if (log2(incval) + log2(ppm) > 62) {
+ rem = ppm % divisor;
+ div = ppm / divisor;
+ diff = div * incval;
+ ppm = rem;
+
+ shift = log2(incval) + log2(ppm) - 62;
+ if (shift > 0) {
+ /* drop precision */
+ ppm >>= shift;
+ divisor >>= shift;
+ }
+ }
+
+ if (divisor)
+ diff = diff + incval * ppm / divisor;
+
+ if (negative)
+ incval -= diff;
+ else
+ incval += diff;
+
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int
+idpf_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ns;
+ int ret = 0;
+
+ ns = rte_timespec_to_ns(ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP write time failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t time;
+ int ret = 0;
+
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP read time failed, err %d", ret);
+ else
+ *ts = rte_ns_to_timespec(time);
+
+ return ret;
+}
+
+static int
+idpf_timesync_disable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+
+ if (vport->tx_tstamp_caps) {
+ rte_free(vport->tx_tstamp_caps);
+ vport->tx_tstamp_caps = NULL;
+ }
+
+ if (adapter->ptp) {
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ }
+
+ return 0;
+}
+
+
static const struct eth_dev_ops idpf_eth_dev_ops = {
.dev_configure = idpf_dev_configure,
.dev_close = idpf_dev_close,
@@ -867,6 +1134,14 @@ static const struct eth_dev_ops idpf_eth_dev_ops = {
.xstats_get = idpf_dev_xstats_get,
.xstats_get_names = idpf_dev_xstats_get_names,
.xstats_reset = idpf_dev_xstats_reset,
+ .timesync_enable = idpf_timesync_enable,
+ .timesync_read_rx_timestamp = idpf_timesync_read_rx_timestamp,
+ .timesync_read_tx_timestamp = idpf_timesync_read_tx_timestamp,
+ .timesync_adjust_time = idpf_timesync_adjust_time,
+ .timesync_adjust_freq = idpf_timesync_adjust_freq,
+ .timesync_read_time = idpf_timesync_read_time,
+ .timesync_write_time = idpf_timesync_write_time,
+ .timesync_disable = idpf_timesync_disable,
};
static int
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v2 4/4] doc: add PTP IDPF documentation
2025-10-28 6:07 ` [PATCH v2 0/4] Enable PTP feature for MEV Soumyadeep Hore
` (2 preceding siblings ...)
2025-10-28 6:07 ` [PATCH v2 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
@ 2025-10-28 6:07 ` Soumyadeep Hore
2025-10-30 9:41 ` Bruce Richardson
2026-01-20 14:34 ` Stephen Hemminger
2025-11-03 16:06 ` [PATCH v3 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-01-20 14:56 ` [PATCH v2 0/4] Enable PTP feature for MEV Stephen Hemminger
5 siblings, 2 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-10-28 6:07 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Updated the IDPF Documentation with PTP feature.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
doc/guides/nics/idpf.rst | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/doc/guides/nics/idpf.rst b/doc/guides/nics/idpf.rst
index b99cc18188..e76632904b 100644
--- a/doc/guides/nics/idpf.rst
+++ b/doc/guides/nics/idpf.rst
@@ -109,3 +109,19 @@ The paths are chosen based on 2 conditions:
A value "P" means the offload feature is not supported by vector path.
If any not supported features are used, idpf vector PMD is disabled
and the scalar paths are chosen.
+
+Time Synchronisation
+~~~~~~~~~~~~~~~~~~~~
+
+The system operator can run a PTP (Precision Time Protocol) client application
+to synchronise the time on the network card in ACC
+(and optionally the time on the system) to the PTP master.
+
+IDPF PMD supports PTP client applications that use the DPDK IEEE 1588 API
+to communicate with the PTP master clock.
+Note that PTP client application needs to run on PF in ACC
+and add the ``--force-max-simd-bitwidth=64`` startup parameter to disable vector mode.
+
+.. code-block:: console
+
+ examples/dpdk-ptpclient -l 0-3 -n 3 -a 0000:ec:00.1 --force-max-simd-bitwidth=64 -- -T 1 -p 0x1 -c 1
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* Re: [PATCH v2 4/4] doc: add PTP IDPF documentation
2025-10-28 6:07 ` [PATCH v2 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
@ 2025-10-30 9:41 ` Bruce Richardson
2026-01-20 14:34 ` Stephen Hemminger
1 sibling, 0 replies; 57+ messages in thread
From: Bruce Richardson @ 2025-10-30 9:41 UTC (permalink / raw)
To: Soumyadeep Hore; +Cc: dev, rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
On Tue, Oct 28, 2025 at 02:07:58AM -0400, Soumyadeep Hore wrote:
> Updated the IDPF Documentation with PTP feature.
>
> Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
> ---
> doc/guides/nics/idpf.rst | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
>
> diff --git a/doc/guides/nics/idpf.rst b/doc/guides/nics/idpf.rst
> index b99cc18188..e76632904b 100644
> --- a/doc/guides/nics/idpf.rst
> +++ b/doc/guides/nics/idpf.rst
> @@ -109,3 +109,19 @@ The paths are chosen based on 2 conditions:
> A value "P" means the offload feature is not supported by vector path.
> If any not supported features are used, idpf vector PMD is disabled
> and the scalar paths are chosen.
> +
> +Time Synchronisation
> +~~~~~~~~~~~~~~~~~~~~
> +
> +The system operator can run a PTP (Precision Time Protocol) client application
> +to synchronise the time on the network card in ACC
> +(and optionally the time on the system) to the PTP master.
> +
> +IDPF PMD supports PTP client applications that use the DPDK IEEE 1588 API
> +to communicate with the PTP master clock.
> +Note that PTP client application needs to run on PF in ACC
> +and add the ``--force-max-simd-bitwidth=64`` startup parameter to disable vector mode.
> +
> +.. code-block:: console
> +
> + examples/dpdk-ptpclient -l 0-3 -n 3 -a 0000:ec:00.1 --force-max-simd-bitwidth=64 -- -T 1 -p 0x1 -c 1
Why is force-max-simd-bitwidth necessary? When using a feature not
supported by the vector driver path, the driver itself must be the one
which selects a scalar path. That task cannot fall to the user, or require
the user to know that he needs to select a scalar path.
/Bruce
> --
> 2.47.1
>
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH v3 0/4] Enable PTP feature for MEV
2025-10-28 6:07 ` [PATCH v2 0/4] Enable PTP feature for MEV Soumyadeep Hore
` (3 preceding siblings ...)
2025-10-28 6:07 ` [PATCH v2 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
@ 2025-11-03 16:06 ` Soumyadeep Hore
2025-11-03 16:06 ` [PATCH v3 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
` (4 more replies)
2026-01-20 14:56 ` [PATCH v2 0/4] Enable PTP feature for MEV Stephen Hemminger
5 siblings, 5 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-11-03 16:06 UTC (permalink / raw)
To: dev, bruce.richardson, aman.deep.singh
Cc: rajesh3.kumar, manoj.kumar.subbarao
Enabling basic PTP feature in IDPF PMD using virtchnl messages.
---
v3:
- Fixed checkpatch warnings
- Addressed Bruce's comments
---
v2:
- Fixed essential checkpatch warnings
---
Milena Olech (1):
net/idpf: add a new API for PTP support
Soumyadeep Hore (3):
net/idpf: add PTP virtchnl2 support
net/intel: add support for Precision Time Protocol
doc: add PTP IDPF documentation
doc/guides/nics/idpf.rst | 15 +
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/base/virtchnl2.h | 324 ++++++---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++--
drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
drivers/net/intel/idpf/idpf_ethdev.c | 275 ++++++++
drivers/net/intel/idpf/idpf_ptp.c | 646 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 227 ++++++
drivers/net/intel/idpf/meson.build | 1 +
11 files changed, 1561 insertions(+), 162 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
--
2.47.1
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH v3 1/4] net/idpf: add a new API for PTP support
2025-11-03 16:06 ` [PATCH v3 0/4] Enable PTP feature for MEV Soumyadeep Hore
@ 2025-11-03 16:06 ` Soumyadeep Hore
2025-11-03 16:06 ` [PATCH v3 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
` (3 subsequent siblings)
4 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-11-03 16:06 UTC (permalink / raw)
To: dev, bruce.richardson, aman.deep.singh
Cc: rajesh3.kumar, manoj.kumar.subbarao, Milena Olech
From: Milena Olech <milena.olech@intel.com>
Add PTP capabilities to determine which PTP features are enabled -
get/set the time, cross timestamp, adjust the clock and timestamping.
Create appropriate structures and opcodes to support features introduced by
capabilities.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
---
drivers/net/intel/idpf/base/virtchnl2.h | 324 +++++++++++++++++-------
1 file changed, 235 insertions(+), 89 deletions(-)
diff --git a/drivers/net/intel/idpf/base/virtchnl2.h b/drivers/net/intel/idpf/base/virtchnl2.h
index 6cfb4f56fa..0b01daee30 100644
--- a/drivers/net/intel/idpf/base/virtchnl2.h
+++ b/drivers/net/intel/idpf/base/virtchnl2.h
@@ -118,8 +118,14 @@ enum virtchnl2_op {
VIRTCHNL2_OP_DEL_QUEUE_GROUPS = 539,
VIRTCHNL2_OP_GET_PORT_STATS = 540,
/* TimeSync opcodes */
- VIRTCHNL2_OP_GET_PTP_CAPS = 541,
- VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES = 542,
+ 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,
};
#define VIRTCHNL2_RDMA_INVALID_QUEUE_IDX 0xFFFF
@@ -1813,123 +1819,180 @@ VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_promisc_info);
* enum virtchnl2_ptp_caps - PTP capabilities
*/
enum virtchnl2_ptp_caps {
- VIRTCHNL2_PTP_CAP_LEGACY_CROSS_TIME = BIT(0),
- VIRTCHNL2_PTP_CAP_PTM = BIT(1),
- VIRTCHNL2_PTP_CAP_DEVICE_CLOCK_CONTROL = BIT(2),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_DIRECT = BIT(3),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_VIRTCHNL = BIT(4),
-};
-
-/**
- * struct virtchnl2_ptp_legacy_cross_time_reg - Legacy cross time registers
- * offsets.
+ 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_legacy_cross_time_reg {
- __le32 shadow_time_0;
- __le32 shadow_time_l;
- __le32 shadow_time_h;
- __le32 cmd_sync;
+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(16, virtchnl2_ptp_legacy_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_clk_reg_offsets);
/**
- * struct virtchnl2_ptp_ptm_cross_time_reg - PTM cross time registers 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_ptm_cross_time_reg {
- __le32 art_l;
- __le32 art_h;
- __le32 cmd_sync;
+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_ptm_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_cross_time_reg_offsets);
/**
- * struct virtchnl2_ptp_device_clock_control - Registers needed to control the
- * main clock.
+ * 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_device_clock_control {
- __le32 cmd;
- __le32 incval_l;
- __le32 incval_h;
- __le32 shadj_l;
- __le32 shadj_h;
- u8 pad[4];
+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(24, virtchnl2_ptp_device_clock_control);
+VIRTCHNL2_CHECK_STRUCT_LEN(40, virtchnl2_ptp_clk_adj_reg_offsets);
/**
- * struct virtchnl2_ptp_tx_tstamp_entry - PTP TX timestamp entry
- * @tx_latch_register_base: TX latch register base
- * @tx_latch_register_offset: TX latch register offset
- * @index: Index
- * @pad: Padding
+ * 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_entry {
- __le32 tx_latch_register_base;
- __le32 tx_latch_register_offset;
+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_entry);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch_caps);
/**
- * struct virtchnl2_ptp_tx_tstamp - Structure that defines tx tstamp entries
+ * struct virtchnl2_ptp_get_vport_tx_tstamp_caps - Structure that defines Tx
+ * tstamp entries
+ * @vport_id: Vport number
* @num_latches: Total number of latches
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
- * @ptp_tx_tstamp_entries: Aarray of TX timestamp entries
+ * @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_tx_tstamp {
+struct virtchnl2_ptp_get_vport_tx_tstamp_caps {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
- struct virtchnl2_ptp_tx_tstamp_entry ptp_tx_tstamp_entries[STRUCT_VAR_LEN];
+ u8 tstamp_ns_lo_bit;
+ u8 tstamp_ns_hi_bit;
+ u8 pad[8];
+
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp,
- ptp_tx_tstamp_entries);
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_caps,
+ tstamp_latches);
/**
- * struct virtchnl2_get_ptp_caps - Get PTP capabilities
- * @ptp_caps: PTP capability bitmap. See enum virtchnl2_ptp_caps.
- * @pad: Padding
- * @legacy_cross_time_reg: Legacy cross time register
- * @ptm_cross_time_reg: PTM cross time register
- * @device_clock_control: Device clock control
- * @tx_tstamp: TX timestamp
+ * 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
*
- * PV/VF sends this message to negotiate PTP capabilities. CP updates bitmap
+ * PF/VF sends this message to negotiate PTP capabilities. CP updates bitmap
* with supported features and fulfills appropriate structures.
- *
- * Associated with VIRTCHNL2_OP_GET_PTP_CAPS.
- */
-struct virtchnl2_get_ptp_caps {
- __le32 ptp_caps;
+ * 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_legacy_cross_time_reg legacy_cross_time_reg;
- struct virtchnl2_ptp_ptm_cross_time_reg ptm_cross_time_reg;
- struct virtchnl2_ptp_device_clock_control device_clock_control;
- struct virtchnl2_ptp_tx_tstamp tx_tstamp;
+ 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_VAR_LEN(88, virtchnl2_get_ptp_caps,
- tx_tstamp.ptp_tx_tstamp_entries);
+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_h: Timestamp high
- * @tstamp_l: Timestamp low
+ * @tstamp: Timestamp value
* @index: Index
* @valid: Timestamp validity
- * @pad: Padding
+ * @pad: Padding for future extensions
*/
struct virtchnl2_ptp_tx_tstamp_latch {
- __le32 tstamp_h;
- __le32 tstamp_l;
+ __le64 tstamp;
u8 index;
u8 valid;
u8 pad[6];
@@ -1938,26 +2001,97 @@ struct virtchnl2_ptp_tx_tstamp_latch {
VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch);
/**
- * struct virtchnl2_ptp_tx_tstamp_latches - PTP TX timestamp latches
+ * 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
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
+ * @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_GET_PTP_TX_TSTAMP_LATCHES.
+ * Associated with VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP.
*/
-struct virtchnl2_ptp_tx_tstamp_latches {
+struct virtchnl2_ptp_get_vport_tx_tstamp_latches {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
+ u8 get_devtime_with_txtstmp;
+ u8 pad[1];
+ u64 device_time;
struct virtchnl2_ptp_tx_tstamp_latch tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp_latches,
+
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_latches,
tstamp_latches);
+/* VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME
+ * @dev_time_ns: Device clock time value in nanoseconds
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the time from the main timer
+ */
+struct virtchnl2_ptp_get_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_dev_clk_time);
+
+/* 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
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the cross time
+ */
+struct virtchnl2_ptp_get_cross_time {
+ __le64 sys_time_ns;
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_get_cross_time);
+
+/* VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME
+ * @dev_time_ns: Device time value expressed in nanoseconds to set
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to set the time of the main timer
+ */
+struct virtchnl2_ptp_set_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_set_dev_clk_time);
+
+/* 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);
+
+/* 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);
+
static inline const char *virtchnl2_op_str(__le32 v_opcode)
{
switch (v_opcode) {
@@ -2025,10 +2159,22 @@ static inline const char *virtchnl2_op_str(__le32 v_opcode)
return "VIRTCHNL2_OP_DEL_QUEUE_GROUPS";
case VIRTCHNL2_OP_GET_PORT_STATS:
return "VIRTCHNL2_OP_GET_PORT_STATS";
- case VIRTCHNL2_OP_GET_PTP_CAPS:
- return "VIRTCHNL2_OP_GET_PTP_CAPS";
- case VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES:
- return "VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES";
+ case VIRTCHNL2_OP_PTP_GET_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_CAPS";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP";
+ case VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_CROSS_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_CROSS_TIME";
+ case VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS";
default:
return "Unsupported (update virtchnl2.h)";
}
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v3 2/4] net/idpf: add PTP virtchnl2 support
2025-11-03 16:06 ` [PATCH v3 0/4] Enable PTP feature for MEV Soumyadeep Hore
2025-11-03 16:06 ` [PATCH v3 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
@ 2025-11-03 16:06 ` Soumyadeep Hore
2025-11-03 16:06 ` [PATCH v3 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
` (2 subsequent siblings)
4 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-11-03 16:06 UTC (permalink / raw)
To: dev, bruce.richardson, aman.deep.singh
Cc: rajesh3.kumar, manoj.kumar.subbarao
Add a new file - idpf_ptp - to handle PTP virtchnl messages.
Keep the registers addresses in the PTP struct.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
drivers/net/intel/idpf/idpf_ptp.c | 646 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 227 ++++++
drivers/net/intel/idpf/meson.build | 1 +
5 files changed, 910 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
diff --git a/drivers/net/intel/idpf/idpf_common_device.h b/drivers/net/intel/idpf/idpf_common_device.h
index c32dcfbb12..3c84b96225 100644
--- a/drivers/net/intel/idpf/idpf_common_device.h
+++ b/drivers/net/intel/idpf/idpf_common_device.h
@@ -90,6 +90,7 @@ struct idpf_adapter {
/* For timestamp */
uint64_t time_hw;
+ struct idpf_ptp *ptp;
enum idpf_rx_func_type rx_func_type;
};
@@ -161,6 +162,9 @@ struct idpf_vport {
/* Event from ipf */
bool link_up;
uint32_t link_speed;
+
+ /* For PTP */
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
};
/* Message type read in virtual channel from PF */
diff --git a/drivers/net/intel/idpf/idpf_common_virtchnl.c b/drivers/net/intel/idpf/idpf_common_virtchnl.c
index e927d7415a..a28bc9bee6 100644
--- a/drivers/net/intel/idpf/idpf_common_virtchnl.c
+++ b/drivers/net/intel/idpf/idpf_common_virtchnl.c
@@ -4,6 +4,7 @@
#include "idpf_common_virtchnl.h"
#include "idpf_common_logs.h"
+#include "idpf_ptp.h"
#include <eal_export.h>
@@ -38,6 +39,28 @@ idpf_vc_clean(struct idpf_adapter *adapter)
return 0;
}
+/**
+ * idpf_ptp_is_mb_msg - Check if the message is PTP-related
+ * @op: virtchnl opcode
+ *
+ * Returns true if msg is PTP-related, false otherwise
+ */
+static inline bool idpf_ptp_is_mb_msg(uint32_t op)
+{
+ switch (op) {
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ 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:
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int
idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
uint16_t msg_size, uint8_t *msg)
@@ -71,8 +94,15 @@ idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
memcpy(dma_mem->va, msg, msg_size);
- ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
- ctlq_msg->func_id = 0;
+ if (idpf_ptp_is_mb_msg(op) && adapter->ptp->secondary_mbx.valid) {
+ 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 {
+ ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
+ ctlq_msg->func_id = 0;
+ }
+
ctlq_msg->data_len = msg_size;
ctlq_msg->cookie.mbx.chnl_opcode = op;
ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
diff --git a/drivers/net/intel/idpf/idpf_ptp.c b/drivers/net/intel/idpf/idpf_ptp.c
new file mode 100644
index 0000000000..3a364adc93
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.c
@@ -0,0 +1,646 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2001-2025 Intel Corporation
+ */
+
+#include "idpf_ptp.h"
+#include <base/virtchnl2.h>
+#include "idpf_common_virtchnl.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.
+ */
+static 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);
+
+ /* 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);
+
+ /* Set the device clock time */
+ direct = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME;
+ mailbox = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB;
+ 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);
+
+ /* 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);
+}
+
+/**
+ * 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_cross_time_reg_offsets cross_tstamp_offsets;
+ struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
+ struct virtchnl2_ptp_get_caps send_ptp_caps_msg = { };
+ struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
+ struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
+ struct idpf_cmd_info args = { };
+ struct idpf_ptp_secondary_mbx *scnd_mbx;
+ struct idpf_ptp *ptp = adapter->ptp;
+ struct idpf_hw *hw = &adapter->hw;
+ enum idpf_ptp_access access_type;
+ int err = 0;
+ u32 temp_offset;
+
+ 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 |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CAPS;
+ args.in_args = (uint8_t *)&send_ptp_caps_msg;
+ args.in_args_size = sizeof(send_ptp_caps_msg);
+ recv_ptp_caps_msg = rte_zmalloc(NULL,
+ sizeof(struct virtchnl2_ptp_get_caps), 0);
+ if (!recv_ptp_caps_msg)
+ return -ENOMEM;
+
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(*recv_ptp_caps_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ goto free_mem;
+
+ recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
+ 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);
+
+ /* 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);
+
+ access_type = ptp->get_dev_clk_time_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ goto cross_tstamp;
+
+ 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_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.dev_clk_ns_h);
+ ptp->dev_clk_regs.dev_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_l);
+ ptp->dev_clk_regs.phy_clk_ns_l = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_h);
+ ptp->dev_clk_regs.phy_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+cross_tstamp:
+ access_type = ptp->get_cross_tstamp_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ goto discipline_clock;
+
+ 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_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.sys_time_ns_h);
+ ptp->dev_clk_regs.sys_time_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+discipline_clock:
+ access_type = ptp->adj_dev_clk_time_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ return err;
+
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_l);
+ ptp->dev_clk_regs.incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_h);
+ ptp->dev_clk_regs.incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_l);
+ ptp->dev_clk_regs.shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_h);
+ ptp->dev_clk_regs.shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+ /* PHY clock offsets */
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_cmd_type);
+ ptp->dev_clk_regs.phy_cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_l);
+ ptp->dev_clk_regs.phy_incval_l = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_h);
+ ptp->dev_clk_regs.phy_incval_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_l);
+ ptp->dev_clk_regs.phy_shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_h);
+ ptp->dev_clk_regs.phy_shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+free_mem:
+ rte_free(recv_ptp_caps_msg);
+
+ return err;
+}
+
+/**
+ * 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)
+{
+ uint32_t 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 */
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, shtime_enable);
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, exec_cmd | shtime_enable);
+}
+
+/**
+ * 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_cmd_info args = { };
+ int err = 0;
+ u64 dev_time;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&get_dev_clk_time_msg;
+ args.in_args_size = sizeof(get_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(get_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ get_dev_clk_time_msg = *(struct virtchnl2_ptp_get_dev_clk_time *)args.out_buffer;
+ dev_time = LE64_TO_CPU(get_dev_clk_time_msg.dev_time_ns);
+ dev_clk_time->dev_clk_time_ns = dev_time;
+
+ return err;
+}
+
+/**
+ * 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_cmd_info args = { };
+ int err = 0;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CROSS_TIME;
+ args.in_args = (uint8_t *)&cross_time_msg;
+ args.in_args_size = sizeof(cross_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(cross_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ cross_time_msg = *(struct virtchnl2_ptp_get_cross_time *)args.out_buffer;
+ 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 err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err = 0;
+
+ set_dev_clk_time_msg.dev_time_ns = CPU_TO_LE64(time);
+
+ args.ops = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&set_dev_clk_time_msg;
+ args.in_args_size = sizeof(set_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(set_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err = 0;
+
+ adj_dev_clk_time_msg.delta = CPU_TO_LE64(delta);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&adj_dev_clk_time_msg;
+ args.in_args_size = sizeof(adj_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err;
+
+ adj_dev_clk_fine_msg.incval = CPU_TO_LE64(incval);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE;
+ args.in_args = (uint8_t *)&adj_dev_clk_fine_msg;
+ args.in_args_size = sizeof(adj_dev_clk_fine_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_fine_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
+ enum idpf_ptp_access tstamp_access, get_dev_clk_access;
+ struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
+ struct idpf_ptp *ptp = vport->adapter->ptp;
+ struct idpf_cmd_info args = { };
+ int err = 0;
+ u16 num_latches, i;
+ 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;
+
+ send_tx_tstamp_caps.vport_id = CPU_TO_LE32(vport->vport_id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS;
+ args.in_args = (uint8_t *)&send_tx_tstamp_caps;
+ args.in_args_size = sizeof(send_tx_tstamp_caps);
+ args.out_size = IDPF_CTLQ_MAX_BUF_LEN;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ if (err < 0)
+ return err;
+
+ rcv_tx_tstamp_caps = (struct virtchnl2_ptp_get_vport_tx_tstamp_caps *)
+ args.out_buffer;
+ num_latches = LE16_TO_CPU(rcv_tx_tstamp_caps->num_latches);
+ size = sizeof(struct idpf_ptp_vport_tx_tstamp_caps) +
+ sizeof(struct idpf_ptp_tx_tstamp) * num_latches;
+ tstamp_caps = rte_zmalloc(NULL, size, 0);
+ if (!tstamp_caps)
+ return -ENOMEM;
+
+ tstamp_caps->access = true;
+ tstamp_caps->num_entries = num_latches;
+
+ tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ for (i = 0; i < tstamp_caps->num_entries; i++) {
+ __le32 offset_l, offset_h;
+
+ tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i];
+
+ if (tstamp_access == IDPF_PTP_DIRECT) {
+ offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
+ offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_l = LE32_TO_CPU(offset_l);
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_h = LE32_TO_CPU(offset_h);
+ }
+ tstamp_caps->tx_tstamp[i].idx = tx_tstamp_latch_caps.index;
+ }
+
+ tstamp_caps->latched_idx = -1;
+ vport->tx_tstamp_caps = tstamp_caps;
+
+ return err;
+}
+
+/**
+ * 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
+ * @tx_tstamp: Tx timestamp structure to be fulfilled with the timestamp value
+ *
+ * Read the value of the Tx timestamp for a given latch received from the
+ * Control Plane.
+ *
+ * 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 *tx_tstamp)
+{
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ u8 tstamp_ns_lo_bit;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ tx_tstamp->tstamp = LE64_TO_CPU(tstamp_latch->tstamp);
+ tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
+
+ return 0;
+}
+
+/**
+ * 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 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 *ptp_tx_tstamp;
+ struct idpf_cmd_info args = { };
+ int size, msg_size;
+ u32 vport_id;
+ u16 num_latches, id;
+ int err = 0;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+
+ size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) *
+ tx_tstamp_caps->num_entries;
+ send_tx_tstamp_msg = rte_zmalloc(NULL, size, 0);
+ if (!send_tx_tstamp_msg)
+ return -ENOMEM;
+
+ for (id = 0; id < tx_tstamp_caps->num_entries; id++,
+ ptp_tx_tstamp++)
+ send_tx_tstamp_msg->tstamp_latches[id].index =
+ ptp_tx_tstamp->idx;
+ send_tx_tstamp_msg->get_devtime_with_txtstmp = 1;
+
+ msg_size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) * id;
+ send_tx_tstamp_msg->vport_id = CPU_TO_LE32(vport->vport_id);
+ send_tx_tstamp_msg->num_latches = CPU_TO_LE16(id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP;
+ args.in_args = (uint8_t *)send_tx_tstamp_msg;
+ args.in_args_size = msg_size;
+ args.out_size = msg_size;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ rte_free(send_tx_tstamp_msg);
+ if (err < 0)
+ return err;
+
+ recv_tx_tstamp_msg = (struct virtchnl2_ptp_get_vport_tx_tstamp_latches *)
+ args.out_buffer;
+ vport_id = LE32_TO_CPU(recv_tx_tstamp_msg->vport_id);
+ if (vport->vport_id != vport_id)
+ return -EINVAL;
+
+ num_latches = LE16_TO_CPU(recv_tx_tstamp_msg->num_latches);
+
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+ for (id = 0; id < num_latches; id++, ptp_tx_tstamp++) {
+ tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[id];
+
+ if (!tstamp_latch.valid)
+ continue;
+
+ err = idpf_ptp_get_tstamp_value(vport, &tstamp_latch,
+ ptp_tx_tstamp);
+ if (!err) {
+ tx_tstamp_caps->latched_idx = id;
+ vport->adapter->time_hw = recv_tx_tstamp_msg->device_time;
+ }
+ break;
+ }
+ return err;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer value
+ * @adapter: Driver specific private structure
+ *
+ * Return: the device clock time.
+ */
+static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+ u32 hi, lo;
+
+ idpf_ptp_enable_shtime(adapter);
+
+ lo = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_l);
+ hi = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_h);
+
+ return ((u64)hi << 32) | lo;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value through mailbox
+ * @adapter: Driver specific private structure
+ * @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,
+ u64 *src_clk)
+{
+ struct idpf_ptp_dev_timers clk_time;
+ int err = 0;
+
+ err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
+ if (err)
+ return err;
+
+ *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
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: the device clock time on success, -errno otherwise.
+ */
+int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk)
+{
+ if (!adapter->ptp)
+ return 0;
+ 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, src_clk);
+ case IDPF_PTP_DIRECT:
+ *src_clk = idpf_ptp_read_src_clk_reg_direct(adapter);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/intel/idpf/idpf_ptp.h b/drivers/net/intel/idpf/idpf_ptp.h
new file mode 100644
index 0000000000..3fc000a5a6
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.h
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2001-2025 Intel Corporation
+ */
+
+#ifndef _IDPF_PTP_H_
+#define _IDPF_PTP_H_
+
+#include "idpf_osdep.h"
+#include "rte_time.h"
+#include "idpf_common_device.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
+ * @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
+ */
+struct idpf_ptp_dev_clk_regs {
+ /* Main clock */
+ volatile uint32_t *dev_clk_ns_l;
+ volatile uint32_t *dev_clk_ns_h;
+
+ /* PHY timer */
+ volatile uint32_t *phy_clk_ns_l;
+ volatile uint32_t *phy_clk_ns_h;
+
+ /* System time */
+ volatile uint32_t *sys_time_ns_l;
+ volatile uint32_t *sys_time_ns_h;
+
+ /* Main timer adjustments */
+ volatile uint32_t *incval_l;
+ volatile uint32_t *incval_h;
+ volatile uint32_t *shadj_l;
+ volatile uint32_t *shadj_h;
+
+ /* PHY timer adjustments */
+ volatile uint32_t *phy_incval_l;
+ volatile uint32_t *phy_incval_h;
+ volatile uint32_t *phy_shadj_l;
+ volatile uint32_t *phy_shadj_h;
+
+ /* Command */
+ volatile uint32_t *cmd;
+ volatile uint32_t *phy_cmd;
+ volatile uint32_t *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_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;
+};
+
+/**
+ * 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 - 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
+ * @tstamp: the Tx tstamp value
+ * @idx: the index of the Tx tstamp
+ */
+struct idpf_ptp_tx_tstamp {
+ u32 tx_latch_reg_offset_l;
+ u32 tx_latch_reg_offset_h;
+ 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
+ * @access: indicates an access to Tx timestamp
+ * @latches_index: the index of the latched Tx timestamps
+ * @tx_tstamp: array of Tx timestamp parameters
+ */
+struct idpf_ptp_vport_tx_tstamp_caps {
+ u32 vport_id;
+ u16 num_entries;
+ u16 tstamp_ns_lo_bit;
+ bool access:1;
+ u16 latched_idx;
+ struct idpf_ptp_tx_tstamp tx_tstamp[];
+};
+
+/**
+ * struct idpf_ptp - PTP parameters
+ * @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
+ * @tx_tstamp_access: access type for the Tx timestamp value read
+ * @rsv: Reserved fields
+ * @secondary_mbx: parameters for using dedicated PTP mailbox
+ */
+struct idpf_ptp {
+ 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;
+ enum idpf_ptp_access set_dev_clk_time_access:2;
+ enum idpf_ptp_access adj_dev_clk_time_access:2;
+ enum idpf_ptp_access tx_tstamp_access:2;
+ u8 rsv:6;
+ struct idpf_ptp_secondary_mbx secondary_mbx;
+};
+
+/**
+ * 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;
+};
+
+int idpf_ptp_get_caps(struct idpf_adapter *adapter);
+int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk);
+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);
+int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport);
+int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
+
+/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
+static inline uint64_t
+idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
+ bool is_rx, uint32_t in_timestamp)
+{
+ const uint64_t mask = 0xFFFFFFFF;
+ uint32_t phc_time_lo, delta;
+ uint64_t ns;
+
+ if (flag != 0)
+ idpf_ptp_read_src_clk_reg(ad, &ad->time_hw);
+
+ /* Extract the lower 32 bits of the PHC time */
+ phc_time_lo = (uint32_t)(ad->time_hw);
+
+ /* Calculate the delta between the lower 32bits of the cached PHC
+ * time and the in_timestamp value.
+ */
+ delta = in_timestamp - phc_time_lo;
+
+ if (delta > mask / 2) {
+ /* Reverse the delta calculation here */
+ delta = phc_time_lo - in_timestamp;
+ ns = ad->time_hw - delta;
+ } else {
+ if (is_rx)
+ ns = ad->time_hw - delta;
+ else
+ ns = ad->time_hw + delta;
+ }
+
+ return ns;
+}
+#endif /* _IDPF_PTP_H_ */
diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
index a805d02ea2..5a4a3c2259 100644
--- a/drivers/net/intel/idpf/meson.build
+++ b/drivers/net/intel/idpf/meson.build
@@ -18,6 +18,7 @@ sources += files(
'idpf_ethdev.c',
'idpf_rxtx.c',
+ 'idpf_ptp.c',
)
if arch_subdir == 'x86' and dpdk_conf.get('RTE_IOVA_IN_MBUF') == 1
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v3 3/4] net/intel: add support for Precision Time Protocol
2025-11-03 16:06 ` [PATCH v3 0/4] Enable PTP feature for MEV Soumyadeep Hore
2025-11-03 16:06 ` [PATCH v3 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
2025-11-03 16:06 ` [PATCH v3 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
@ 2025-11-03 16:06 ` Soumyadeep Hore
2025-12-16 12:43 ` Bruce Richardson
2025-11-03 16:06 ` [PATCH v3 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 0/4] Enable PTP feature for MEV Soumyadeep Hore
4 siblings, 1 reply; 57+ messages in thread
From: Soumyadeep Hore @ 2025-11-03 16:06 UTC (permalink / raw)
To: dev, bruce.richardson, aman.deep.singh
Cc: rajesh3.kumar, manoj.kumar.subbarao
Add support for PTP that contains negotiation with CP to receive a
set of supported functionalities through capabilities,
get/set/adjust the time of the main timer and Tx timestamping
through secondary mailbox dedicated to PTP.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++++++++------
drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
drivers/net/intel/idpf/idpf_ethdev.c | 275 ++++++++++++++++++++++
4 files changed, 401 insertions(+), 71 deletions(-)
diff --git a/drivers/net/intel/common/tx.h b/drivers/net/intel/common/tx.h
index 5af64a4cfe..fe175f7d65 100644
--- a/drivers/net/intel/common/tx.h
+++ b/drivers/net/intel/common/tx.h
@@ -113,6 +113,7 @@ struct ci_tx_queue {
uint16_t sw_tail;
uint16_t rs_compl_count;
uint8_t expected_gen_id;
+ uint32_t latch_idx; /* Tx timestamp latch index */
};
};
};
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.c b/drivers/net/intel/idpf/idpf_common_rxtx.c
index a5d0795057..c8cd5b9d5b 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.c
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.c
@@ -9,6 +9,7 @@
#include "idpf_common_rxtx.h"
#include "idpf_common_device.h"
#include "../common/rx.h"
+#include "idpf_ptp.h"
int idpf_timestamp_dynfield_offset = -1;
uint64_t idpf_timestamp_dynflag;
@@ -435,58 +436,6 @@ idpf_qc_split_rxq_mbufs_alloc(struct idpf_rx_queue *rxq)
return 0;
}
-#define IDPF_TIMESYNC_REG_WRAP_GUARD_BAND 10000
-/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
-static inline uint64_t
-idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
- uint32_t in_timestamp)
-{
-#ifdef RTE_ARCH_X86_64
- struct idpf_hw *hw = &ad->hw;
- const uint64_t mask = 0xFFFFFFFF;
- uint32_t hi, lo, lo2, delta;
- uint64_t ns;
-
- if (flag != 0) {
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_EXEC_CMD_M |
- PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- /*
- * On typical system, the delta between lo and lo2 is ~1000ns,
- * so 10000 seems a large-enough but not overly-big guard band.
- */
- if (lo > (UINT32_MAX - IDPF_TIMESYNC_REG_WRAP_GUARD_BAND))
- lo2 = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- else
- lo2 = lo;
-
- if (lo2 < lo) {
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- }
-
- ad->time_hw = ((uint64_t)hi << 32) | lo;
- }
-
- delta = (in_timestamp - (uint32_t)(ad->time_hw & mask));
- if (delta > (mask / 2)) {
- delta = ((uint32_t)(ad->time_hw & mask) - in_timestamp);
- ns = ad->time_hw - delta;
- } else {
- ns = ad->time_hw + delta;
- }
-
- return ns;
-#else /* !RTE_ARCH_X86_64 */
- RTE_SET_USED(ad);
- RTE_SET_USED(flag);
- RTE_SET_USED(in_timestamp);
- return 0;
-#endif /* RTE_ARCH_X86_64 */
-}
-
#define IDPF_RX_FLEX_DESC_ADV_STATUS0_XSUM_S \
(RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S) | \
RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S) | \
@@ -655,8 +604,12 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rx_desc_ring = rxq->rx_ring;
ptype_tbl = rxq->adapter->ptype_tbl;
- if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
- rxq->hw_register_set = 1;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
while (nb_rx < nb_pkts) {
rx_desc = &rx_desc_ring[rx_id];
@@ -732,20 +685,38 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(rte_le_to_cpu_16(rx_desc->ptype_err_fflags0) &
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M) >>
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_S];
+
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
status_err0_qw1 = rte_le_to_cpu_16(rx_desc->status_err0_qw1);
pkt_flags = idpf_splitq_rx_csum_offload(status_err0_qw1);
pkt_flags |= idpf_splitq_rx_rss_offload(rxm, rx_desc);
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP)) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rx_desc->ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rx_desc->ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ if (rx_desc->ts_low & VIRTCHNL2_RX_FLEX_TSTAMP_VALID)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -839,10 +810,34 @@ idpf_split_tx_free(struct ci_tx_queue *cq)
static inline uint16_t
idpf_calc_context_desc(uint64_t flags)
{
- if ((flags & RTE_MBUF_F_TX_TCP_SEG) != 0)
- return 1;
+ static uint64_t mask = RTE_MBUF_F_TX_TCP_SEG |
+ RTE_MBUF_F_TX_IEEE1588_TMST;
- return 0;
+ return (flags & mask) ? 1 : 0;
+}
+
+/**
+ * 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 inline void
+idpf_tx_set_tstamp_desc(volatile union idpf_flex_tx_ctx_desc *ctx_desc,
+ uint32_t idx)
+{
+ ctx_desc->tsyn.qw1.cmd_dtype =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_CMD_M,
+ IDPF_TX_CTX_DESC_TSYN));
+ ctx_desc->tsyn.qw1.cmd_dtype |=
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_DTYPE_M,
+ IDPF_TX_DESC_DTYPE_CTX));
+ ctx_desc->tsyn.qw1.tsyn_reg_l =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_L_M,
+ idx));
+ ctx_desc->tsyn.qw1.tsyn_reg_h =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_H_M,
+ idx >> 2));
}
/* set TSO context descriptor
@@ -948,6 +943,9 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_desc);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_desc, txq->latch_idx);
+
tx_id++;
if (tx_id == txq->nb_tx_desc)
tx_id = 0;
@@ -1104,8 +1102,12 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rx_ring = rxq->rx_ring;
ptype_tbl = rxq->adapter->ptype_tbl;
- if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
- rxq->hw_register_set = 1;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
@@ -1167,17 +1169,33 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rxm->ol_flags |= pkt_flags;
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
rxm->ol_flags |= idpf_timestamp_dynflag;
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
rx_pkts[nb_rx++] = rxm;
@@ -1218,6 +1236,13 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
if (unlikely(!rxq) || unlikely(!rxq->q_started))
return nb_rx;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
+
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
rx_status0 = rte_le_to_cpu_16(rxdp->flex_nic_wb.status_error0);
@@ -1298,17 +1323,33 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(uint8_t)(rte_cpu_to_le_16(rxd.flex_nic_wb.ptype_flex_flags0) &
VIRTCHNL2_RX_FLEX_DESC_PTYPE_M)];
+ if ((first_seg->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -1474,6 +1515,9 @@ idpf_dp_singleq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_txd);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_txd, txq->latch_idx);
+
txe->last_id = tx_last;
tx_id = txe->next_id;
txe = txn;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.h b/drivers/net/intel/idpf/idpf_common_rxtx.h
index 3bc3323af4..f0812befc5 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.h
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.h
@@ -13,6 +13,12 @@
#include "../common/tx.h"
#include "../common/rx.h"
+#define FIELD_PREP(_mask, _val) \
+ (__extension__ ({ \
+ typeof(_mask) _x = (_mask); \
+ ((typeof(_x))(_val) << rte_bsf32(_x)) & (_x); \
+ }))
+
#define IDPF_RX_MAX_BURST 32
#define IDPF_RX_OFFLOAD_IPV4_CKSUM RTE_BIT64(1)
@@ -156,6 +162,10 @@ struct idpf_rx_queue {
uint64_t offloads;
uint32_t hw_register_set;
+ uint32_t time_high; /* high 32 bits of hardware timestamp register */
+ uint32_t hw_time_high; /* high 32 bits of timestamp */
+ uint32_t hw_time_low; /* low 32 bits of timestamp */
+ uint64_t hw_time_update; /* Last time HW timestamp was updated */
};
/* Offload features */
diff --git a/drivers/net/intel/idpf/idpf_ethdev.c b/drivers/net/intel/idpf/idpf_ethdev.c
index 5d786fbba6..22b7a1093c 100644
--- a/drivers/net/intel/idpf/idpf_ethdev.c
+++ b/drivers/net/intel/idpf/idpf_ethdev.c
@@ -2,6 +2,8 @@
* Copyright(c) 2022 Intel Corporation
*/
+#include <math.h>
+
#include <rte_atomic.h>
#include <rte_eal.h>
#include <rte_ether.h>
@@ -14,6 +16,7 @@
#include "idpf_ethdev.h"
#include "idpf_rxtx.h"
#include "../common/tx.h"
+#include "idpf_ptp.h"
#define IDPF_TX_SINGLE_Q "tx_single"
#define IDPF_RX_SINGLE_Q "rx_single"
@@ -841,6 +844,270 @@ idpf_dev_close(struct rte_eth_dev *dev)
return 0;
}
+static int
+idpf_timesync_enable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct timespec sys_ts;
+ uint64_t ns;
+ int ret = 0, q_id = 0;
+
+ if (dev->data->dev_started && !(dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP)) {
+ PMD_DRV_LOG(ERR, "Rx timestamp offload not configured");
+ return -1;
+ }
+
+ adapter->ptp = rte_zmalloc(NULL, sizeof(struct idpf_ptp), 0);
+ if (adapter->ptp == NULL) {
+ PMD_DRV_LOG(ERR, "Failed to allocate memory for PTP");
+ return -ENOMEM;
+ }
+
+ ret = idpf_ptp_get_caps(adapter);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get PTP capabilities, err=%d", ret);
+ goto err_ptp;
+ }
+ /* Write the default increment time value if the clock adjustments
+ * are enabled.
+ */
+ if (adapter->ptp->adj_dev_clk_time_access != IDPF_PTP_NONE) {
+ ret = idpf_ptp_adj_dev_clk_fine(adapter,
+ adapter->ptp->base_incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set incval failed, err=%d", ret);
+ goto err_ptp;
+ }
+ }
+
+ /* Do not initialize the PTP if the device clock time cannot be read. */
+ if (adapter->ptp->get_dev_clk_time_access == IDPF_PTP_NONE) {
+ PMD_DRV_LOG(ERR, "Getting device clock time is not supported");
+ ret = -EIO;
+ goto err_ptp;
+ }
+
+ /* Set the device clock time to system time. */
+ if (adapter->ptp->set_dev_clk_time_access != IDPF_PTP_NONE) {
+ clock_gettime(CLOCK_REALTIME, &sys_ts);
+ ns = rte_timespec_to_ns(&sys_ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err=%d", ret);
+ goto err_ptp;
+ }
+ }
+
+ ret = idpf_ptp_get_vport_tstamps_caps(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get vport timestamp capabilities, err=%d", ret);
+ goto err_ptp;
+ }
+
+ for (q_id = 0; q_id < dev->data->nb_tx_queues; q_id++) {
+ struct ci_tx_queue *txq = dev->data->tx_queues[q_id];
+ txq->latch_idx = vport->tx_tstamp_caps->tx_tstamp[q_id].idx;
+ }
+
+ 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;
+
+err_ptp:
+ if (ret != 0) {
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ }
+ return ret;
+}
+
+static int
+idpf_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp,
+ uint32_t flags)
+{
+ struct idpf_rx_queue *rxq;
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ts_ns;
+
+ rxq = dev->data->rx_queues[flags];
+
+ ts_ns = idpf_tstamp_convert_32b_64b(adapter, 1, true, rxq->time_high);
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ return 0;
+}
+
+static int
+idpf_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ uint16_t latch_idx;
+ uint64_t ts_ns, tstamp;
+ int ret = 0;
+
+ ret = idpf_ptp_get_tx_tstamp(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get TX timestamp");
+ return ret;
+ }
+
+ latch_idx = vport->tx_tstamp_caps->latched_idx;
+ tstamp = vport->tx_tstamp_caps->tx_tstamp[latch_idx].tstamp;
+ ts_ns = idpf_tstamp_convert_32b_64b(vport->adapter, 0, false, tstamp);
+
+ /* Convert to timespec */
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ vport->tx_tstamp_caps->latched_idx = -1;
+
+ return 0;
+}
+
+static int
+idpf_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint64_t time, ns;
+ int ret = 0;
+
+ if (ptp->adj_dev_clk_time_access != IDPF_PTP_MAILBOX) {
+ PMD_DRV_LOG(ERR, "Adjusting device clock time is not supported");
+ return -ENOTSUP;
+ }
+
+ if (delta > INT32_MAX || delta < INT32_MIN) {
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP read clock time failed, err %d", ret);
+ return ret;
+ }
+
+ ns = time + delta;
+
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err %d", ret);
+
+ return ret;
+ }
+
+ ret = idpf_ptp_adj_dev_clk_time(adapter, delta);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP adjusting clock failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ int64_t incval, diff = 0;
+ bool negative = false;
+ uint64_t div, rem;
+ uint64_t divisor = 1000000ULL << 16;
+ int shift;
+ int ret = 0;
+
+ incval = ptp->base_incval;
+
+ if (ppm < 0) {
+ negative = true;
+ ppm = -ppm;
+ }
+
+ /* can incval * ppm overflow ? */
+ if (log2(incval) + log2(ppm) > 62) {
+ rem = ppm % divisor;
+ div = ppm / divisor;
+ diff = div * incval;
+ ppm = rem;
+
+ shift = log2(incval) + log2(ppm) - 62;
+ if (shift > 0) {
+ /* drop precision */
+ ppm >>= shift;
+ divisor >>= shift;
+ }
+ }
+
+ if (divisor)
+ diff = diff + incval * ppm / divisor;
+
+ if (negative)
+ incval -= diff;
+ else
+ incval += diff;
+
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int
+idpf_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ns;
+ int ret = 0;
+
+ ns = rte_timespec_to_ns(ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP write time failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t time;
+ int ret = 0;
+
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP read time failed, err %d", ret);
+ else
+ *ts = rte_ns_to_timespec(time);
+
+ return ret;
+}
+
+static int
+idpf_timesync_disable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+
+ if (vport->tx_tstamp_caps) {
+ rte_free(vport->tx_tstamp_caps);
+ vport->tx_tstamp_caps = NULL;
+ }
+
+ if (adapter->ptp) {
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ }
+
+ return 0;
+}
+
+
static const struct eth_dev_ops idpf_eth_dev_ops = {
.dev_configure = idpf_dev_configure,
.dev_close = idpf_dev_close,
@@ -867,6 +1134,14 @@ static const struct eth_dev_ops idpf_eth_dev_ops = {
.xstats_get = idpf_dev_xstats_get,
.xstats_get_names = idpf_dev_xstats_get_names,
.xstats_reset = idpf_dev_xstats_reset,
+ .timesync_enable = idpf_timesync_enable,
+ .timesync_read_rx_timestamp = idpf_timesync_read_rx_timestamp,
+ .timesync_read_tx_timestamp = idpf_timesync_read_tx_timestamp,
+ .timesync_adjust_time = idpf_timesync_adjust_time,
+ .timesync_adjust_freq = idpf_timesync_adjust_freq,
+ .timesync_read_time = idpf_timesync_read_time,
+ .timesync_write_time = idpf_timesync_write_time,
+ .timesync_disable = idpf_timesync_disable,
};
static int
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v3 4/4] doc: add PTP IDPF documentation
2025-11-03 16:06 ` [PATCH v3 0/4] Enable PTP feature for MEV Soumyadeep Hore
` (2 preceding siblings ...)
2025-11-03 16:06 ` [PATCH v3 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
@ 2025-11-03 16:06 ` Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 0/4] Enable PTP feature for MEV Soumyadeep Hore
4 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2025-11-03 16:06 UTC (permalink / raw)
To: dev, bruce.richardson, aman.deep.singh
Cc: rajesh3.kumar, manoj.kumar.subbarao
Updated the IDPF Documentation with PTP feature.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
doc/guides/nics/idpf.rst | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/doc/guides/nics/idpf.rst b/doc/guides/nics/idpf.rst
index b99cc18188..6e02aa3dbb 100644
--- a/doc/guides/nics/idpf.rst
+++ b/doc/guides/nics/idpf.rst
@@ -109,3 +109,18 @@ The paths are chosen based on 2 conditions:
A value "P" means the offload feature is not supported by vector path.
If any not supported features are used, idpf vector PMD is disabled
and the scalar paths are chosen.
+
+Time Synchronisation
+~~~~~~~~~~~~~~~~~~~~
+
+The system operator can run a PTP (Precision Time Protocol) client application
+to synchronise the time on the network card in ACC
+(and optionally the time on the system) to the PTP primary.
+
+IDPF PMD supports PTP client applications that use the DPDK IEEE 1588 API
+to communicate with the PTP primary clock.
+Note that PTP client application needs to run on PF in ACC.
+
+.. code-block:: console
+
+ examples/dpdk-ptpclient -l 0-3 -n 3 -a 0000:ec:00.1 -- -T 1 -p 0x1
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* Re: [PATCH v3 3/4] net/intel: add support for Precision Time Protocol
2025-11-03 16:06 ` [PATCH v3 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
@ 2025-12-16 12:43 ` Bruce Richardson
0 siblings, 0 replies; 57+ messages in thread
From: Bruce Richardson @ 2025-12-16 12:43 UTC (permalink / raw)
To: Soumyadeep Hore; +Cc: dev, aman.deep.singh, rajesh3.kumar, manoj.kumar.subbarao
On Mon, Nov 03, 2025 at 11:06:44AM -0500, Soumyadeep Hore wrote:
> Add support for PTP that contains negotiation with CP to receive a
> set of supported functionalities through capabilities,
> get/set/adjust the time of the main timer and Tx timestamping
> through secondary mailbox dedicated to PTP.
>
> Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
> ---
> drivers/net/intel/common/tx.h | 1 +
> drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++++++++------
> drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
> drivers/net/intel/idpf/idpf_ethdev.c | 275 ++++++++++++++++++++++
> 4 files changed, 401 insertions(+), 71 deletions(-)
>
> diff --git a/drivers/net/intel/common/tx.h b/drivers/net/intel/common/tx.h
> index 5af64a4cfe..fe175f7d65 100644
> --- a/drivers/net/intel/common/tx.h
> +++ b/drivers/net/intel/common/tx.h
> @@ -113,6 +113,7 @@ struct ci_tx_queue {
> uint16_t sw_tail;
> uint16_t rs_compl_count;
> uint8_t expected_gen_id;
> + uint32_t latch_idx; /* Tx timestamp latch index */
This creates padding in the structure. Unless there is a good reason why
not, generally structure variables should be kept in order of size, from
biggest to smallest to avoid padding.
/Bruce
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH v2 4/4] doc: add PTP IDPF documentation
2025-10-28 6:07 ` [PATCH v2 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
2025-10-30 9:41 ` Bruce Richardson
@ 2026-01-20 14:34 ` Stephen Hemminger
1 sibling, 0 replies; 57+ messages in thread
From: Stephen Hemminger @ 2026-01-20 14:34 UTC (permalink / raw)
To: Soumyadeep Hore
Cc: dev, bruce.richardson, rajesh3.kumar, aman.deep.singh,
manoj.kumar.subbarao
On Tue, 28 Oct 2025 02:07:58 -0400
Soumyadeep Hore <soumyadeep.hore@intel.com> wrote:
> Updated the IDPF Documentation with PTP feature.
>
> Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
> ---
> doc/guides/nics/idpf.rst | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
>
> diff --git a/doc/guides/nics/idpf.rst b/doc/guides/nics/idpf.rst
> index b99cc18188..e76632904b 100644
> --- a/doc/guides/nics/idpf.rst
> +++ b/doc/guides/nics/idpf.rst
> @@ -109,3 +109,19 @@ The paths are chosen based on 2 conditions:
> A value "P" means the offload feature is not supported by vector path.
> If any not supported features are used, idpf vector PMD is disabled
> and the scalar paths are chosen.
> +
> +Time Synchronisation
> +~~~~~~~~~~~~~~~~~~~~
> +
> +The system operator can run a PTP (Precision Time Protocol) client application
> +to synchronise the time on the network card in ACC
> +(and optionally the time on the system) to the PTP master.
> +
NAK
Don't use master. DPDK has switched over to the later terminolgy
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH v2 0/4] Enable PTP feature for MEV
2025-10-28 6:07 ` [PATCH v2 0/4] Enable PTP feature for MEV Soumyadeep Hore
` (4 preceding siblings ...)
2025-11-03 16:06 ` [PATCH v3 0/4] Enable PTP feature for MEV Soumyadeep Hore
@ 2026-01-20 14:56 ` Stephen Hemminger
5 siblings, 0 replies; 57+ messages in thread
From: Stephen Hemminger @ 2026-01-20 14:56 UTC (permalink / raw)
To: Soumyadeep Hore
Cc: dev, bruce.richardson, rajesh3.kumar, aman.deep.singh,
manoj.kumar.subbarao
On Tue, 28 Oct 2025 02:07:54 -0400
Soumyadeep Hore <soumyadeep.hore@intel.com> wrote:
> Enabling basic PTP feature in IDPF PMD using virtchnl messages.
>
> ---
> v2:
> - Fixed essential checkpatch warnings
> ---
> Milena Olech (1):
> net/idpf: add a new API for PTP support
>
> Soumyadeep Hore (3):
> net/idpf: add PTP virtchnl2 support
> net/intel: add support for Precision Time Protocol
> doc: add PTP IDPF documentation
>
> doc/guides/nics/idpf.rst | 16 +
> drivers/net/intel/common/tx.h | 1 +
> drivers/net/intel/idpf/base/virtchnl2.h | 324 ++++++---
> drivers/net/intel/idpf/idpf_common_device.h | 4 +
> drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++--
> drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
> drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
> drivers/net/intel/idpf/idpf_ethdev.c | 275 ++++++++
> drivers/net/intel/idpf/idpf_ptp.c | 646 ++++++++++++++++++
> drivers/net/intel/idpf/idpf_ptp.h | 227 ++++++
> drivers/net/intel/idpf/meson.build | 1 +
> 11 files changed, 1562 insertions(+), 162 deletions(-)
> create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
> create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
>
Lots more issues found by automated patch review
## DPDK Patch Review: PTP Support for IDPF Driver (v4)
**Series:** [PATCH v4 1-4/4] PTP support for IDPF
**Submitter:** Soumyadeep Hore
---
### Summary
This 4-patch series adds Precision Time Protocol (PTP) support to the IDPF driver, including virtchnl2 API definitions, mailbox message handling, timesync ethdev operations, and documentation.
---
### Patch 1/4: `net/idpf: add a new API for PTP support`
**Commit Message:**
- ✅ Subject line 39 chars (≤60)
- ✅ Lowercase after colon
- ✅ Correct prefix `net/idpf:`
- ✅ No trailing period
- ✅ Has two Signed-off-by tags in correct order
**Code Issues:**
- ✅ This is base code in `drivers/net/intel/idpf/base/` which may use different conventions per AGENTS.md exception for base directories
---
### Patch 2/4: `net/idpf: add PTP virtchnl2 support`
**Commit Message:**
- ✅ Subject line 37 chars (≤60)
- ✅ Imperative mood, lowercase, no period
- ✅ Has Signed-off-by
**License (SPDX):**
- ✅ `idpf_ptp.c` and `idpf_ptp.h` have proper SPDX-License-Identifier: BSD-3-Clause
- ⚠️ **Warning:** Copyright dates `2001-2025` seem incorrect—IDPF is relatively recent technology
**Code Style Issues:**
| Severity | Location | Issue |
|----------|----------|-------|
| Warning | `idpf_ptp.c:762` | Unnecessary initialization: `int err = 0;` is assigned before use |
| Warning | `idpf_ptp.c:916,983,1013,1044` | Multiple unnecessary `int err = 0;` initializations |
| Warning | `idpf_ptp.h:1327` | `#include "rte_time.h"` should be `<rte_time.h>` for DPDK system headers |
| Warning | `idpf_ptp.h:1340-1388` | Mixed comment styles: some use `/* struct` (missing `@`), others use proper `/**` Doxygen |
| Warning | `idpf_ptp.h:1481-1486` | Bitfield syntax `:2` and `:6` on enum—may have portability concerns |
| Warning | Multiple | Implicit NULL comparisons (`if (!ptr)`) instead of explicit `if (ptr == NULL)` |
**Potential Bug:**
```c
// idpf_ptp.c:779-791
recv_ptp_caps_msg = rte_zmalloc(NULL, sizeof(struct virtchnl2_ptp_get_caps), 0);
...
recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
```
⚠️ **Memory leak:** The originally allocated `recv_ptp_caps_msg` at line 779 is overwritten at line 791, leaking memory. The `free_mem` label at line 879 frees `args.out_buffer`, not the original allocation.
---
### Patch 3/4: `net/intel: add support for Precision Time Protocol`
**Commit Message:**
- ⚠️ **Warning:** Subject uses "Precision Time Protocol" — should be lowercase `precision time protocol` or abbreviated as `PTP` per case sensitivity rules
- ⚠️ **Warning:** Prefix `net/intel:` is generic—the changes are specifically in `idpf`, not all Intel drivers
- ✅ Has Signed-off-by
**Code Style Issues:**
| Severity | Location | Issue |
|----------|----------|-------|
| Error | `idpf_ethdev.c:2983` | `#include <math.h>` and use of `log2()` function—requires libm linkage, non-standard for DPDK |
| Error | `idpf_ethdev.c:2181` | `log2(incval) + log2(ppm)` — should use DPDK's `rte_log2_u64()` or similar |
| Warning | `idpf_ptp.h:1524` | Indentation uses spaces instead of tabs (hard tabs required per AGENTS.md) |
| Warning | `idpf_common_rxtx.c:1201-1202` | Poor alignment and excessive whitespace in assignment |
| Warning | `idpf_ethdev.c:2007,2217,2233` | Unnecessary `int ret = 0;` initializations |
| Warning | `idpf_ethdev.c:2066-2075` | Control flow issue: success path falls through to `err_ptp:` label |
| Warning | `idpf_ethdev.c:2097-2098` | Inconsistent parameter alignment in function signature |
| Info | `idpf_common_rxtx.h:1954-1958` | `FIELD_PREP` macro—consider if this should be shared or already exists in DPDK |
**Functional Concerns:**
1. **Scope:** Patch modifies `drivers/net/intel/common/tx.h` adding `latch_idx` to `ci_tx_queue`—this affects the common Intel driver structure, not just IDPF.
2. **Error handling in `idpf_timesync_enable()`:**
```c
err_ptp:
if (ret != 0) {
rte_free(adapter->ptp);
adapter->ptp = NULL;
}
return ret;
```
On success, control still reaches this label but doesn't clean up partial state if later operations fail.
3. **Division in hot path:** Lines 1733, 1839, 1889 compute `rte_get_timer_cycles() / (rte_get_timer_hz() / 1000)` in the receive path—this division could be precomputed.
---
### Patch 4/4: `doc: add PTP IDPF documentation`
**Commit Message:**
- ⚠️ **Error:** Body starts with "Updated" (past tense)—should use imperative mood: "Update"
- ⚠️ **Warning:** Prefix `doc:` is too generic—should be `doc/guides/nics:` or similar
- ✅ Subject line 32 chars (≤60)
- ✅ Has Signed-off-by
**Documentation Completeness:**
- ❌ **Missing:** Release notes entry in `doc/guides/rel_notes/` (required per AGENTS.md for new features)
- ❌ **Missing:** Update to `doc/guides/nics/features/idpf.ini` feature matrix (required per AGENTS.md)
**Content:**
- ✅ Documentation matches the implementation
- ⚠️ "Synchronisation" uses British spelling—DPDK typically uses American English
---
### Overall Assessment
**Errors (must fix):**
1. Memory leak in `idpf_ptp_get_caps()`
2. Use of `<math.h>` and `log2()` function—should use DPDK equivalents
3. Commit message body starts with past tense "Updated" (patch 4)
4. Missing release notes for new feature
5. Missing feature matrix update in `doc/guides/nics/features/`
**Warnings (should fix):**
1. Multiple unnecessary variable initializations (`int err = 0;`)
2. Implicit NULL/zero comparisons instead of explicit
3. Inconsistent include style (`"rte_time.h"` vs `<rte_time.h>`)
4. Copyright year range `2001-2025` seems incorrect
5. Mixed indentation (spaces vs tabs) in idpf_ptp.h
6. Subject case for "Precision Time Protocol"
7. Generic commit prefixes (`net/intel:`, `doc:`)
8. Division operation in Rx hot path could be optimized
**Info:**
1. `FIELD_PREP` macro added—verify this doesn't duplicate existing DPDK functionality
2. Patch 3 modifies common Intel tx.h—ensure other Intel drivers aren't affected
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH v4 0/4] Enable PTP feature for MEV
2025-11-03 16:06 ` [PATCH v3 0/4] Enable PTP feature for MEV Soumyadeep Hore
` (3 preceding siblings ...)
2025-11-03 16:06 ` [PATCH v3 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
@ 2026-01-20 19:11 ` Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
` (3 more replies)
4 siblings, 4 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-01-20 19:11 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Enabling basic PTP feature in IDPF PMD using virtchnl messages.
---
v4:
- Addressed Bruce's comments
---
v3:
- Fixed checkpatch warnings
- Addressed Bruce's comments
---
v2:
- Fixed essential checkpatch warnings
---
Milena Olech (1):
net/idpf: add a new API for PTP support
Soumyadeep Hore (3):
net/idpf: add PTP virtchnl2 support
net/intel: add support for Precision Time Protocol
doc: add PTP IDPF documentation
doc/guides/nics/idpf.rst | 15 +
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/base/virtchnl2.h | 324 ++++++---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++--
drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
drivers/net/intel/idpf/idpf_ethdev.c | 275 ++++++++
drivers/net/intel/idpf/idpf_ptp.c | 646 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 227 ++++++
drivers/net/intel/idpf/meson.build | 1 +
11 files changed, 1561 insertions(+), 162 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
--
2.47.1
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH v4 1/4] net/idpf: add a new API for PTP support
2026-01-20 19:11 ` [PATCH v4 0/4] Enable PTP feature for MEV Soumyadeep Hore
@ 2026-01-20 19:11 ` Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
` (2 subsequent siblings)
3 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-01-20 19:11 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao,
Milena Olech
From: Milena Olech <milena.olech@intel.com>
Add PTP capabilities to determine which PTP features are enabled -
get/set the time, cross timestamp, adjust the clock and timestamping.
Create appropriate structures and opcodes to support features introduced by
capabilities.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
---
drivers/net/intel/idpf/base/virtchnl2.h | 324 +++++++++++++++++-------
1 file changed, 235 insertions(+), 89 deletions(-)
diff --git a/drivers/net/intel/idpf/base/virtchnl2.h b/drivers/net/intel/idpf/base/virtchnl2.h
index 6cfb4f56fa..0b01daee30 100644
--- a/drivers/net/intel/idpf/base/virtchnl2.h
+++ b/drivers/net/intel/idpf/base/virtchnl2.h
@@ -118,8 +118,14 @@ enum virtchnl2_op {
VIRTCHNL2_OP_DEL_QUEUE_GROUPS = 539,
VIRTCHNL2_OP_GET_PORT_STATS = 540,
/* TimeSync opcodes */
- VIRTCHNL2_OP_GET_PTP_CAPS = 541,
- VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES = 542,
+ 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,
};
#define VIRTCHNL2_RDMA_INVALID_QUEUE_IDX 0xFFFF
@@ -1813,123 +1819,180 @@ VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_promisc_info);
* enum virtchnl2_ptp_caps - PTP capabilities
*/
enum virtchnl2_ptp_caps {
- VIRTCHNL2_PTP_CAP_LEGACY_CROSS_TIME = BIT(0),
- VIRTCHNL2_PTP_CAP_PTM = BIT(1),
- VIRTCHNL2_PTP_CAP_DEVICE_CLOCK_CONTROL = BIT(2),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_DIRECT = BIT(3),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_VIRTCHNL = BIT(4),
-};
-
-/**
- * struct virtchnl2_ptp_legacy_cross_time_reg - Legacy cross time registers
- * offsets.
+ 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_legacy_cross_time_reg {
- __le32 shadow_time_0;
- __le32 shadow_time_l;
- __le32 shadow_time_h;
- __le32 cmd_sync;
+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(16, virtchnl2_ptp_legacy_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_clk_reg_offsets);
/**
- * struct virtchnl2_ptp_ptm_cross_time_reg - PTM cross time registers 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_ptm_cross_time_reg {
- __le32 art_l;
- __le32 art_h;
- __le32 cmd_sync;
+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_ptm_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_cross_time_reg_offsets);
/**
- * struct virtchnl2_ptp_device_clock_control - Registers needed to control the
- * main clock.
+ * 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_device_clock_control {
- __le32 cmd;
- __le32 incval_l;
- __le32 incval_h;
- __le32 shadj_l;
- __le32 shadj_h;
- u8 pad[4];
+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(24, virtchnl2_ptp_device_clock_control);
+VIRTCHNL2_CHECK_STRUCT_LEN(40, virtchnl2_ptp_clk_adj_reg_offsets);
/**
- * struct virtchnl2_ptp_tx_tstamp_entry - PTP TX timestamp entry
- * @tx_latch_register_base: TX latch register base
- * @tx_latch_register_offset: TX latch register offset
- * @index: Index
- * @pad: Padding
+ * 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_entry {
- __le32 tx_latch_register_base;
- __le32 tx_latch_register_offset;
+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_entry);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch_caps);
/**
- * struct virtchnl2_ptp_tx_tstamp - Structure that defines tx tstamp entries
+ * struct virtchnl2_ptp_get_vport_tx_tstamp_caps - Structure that defines Tx
+ * tstamp entries
+ * @vport_id: Vport number
* @num_latches: Total number of latches
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
- * @ptp_tx_tstamp_entries: Aarray of TX timestamp entries
+ * @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_tx_tstamp {
+struct virtchnl2_ptp_get_vport_tx_tstamp_caps {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
- struct virtchnl2_ptp_tx_tstamp_entry ptp_tx_tstamp_entries[STRUCT_VAR_LEN];
+ u8 tstamp_ns_lo_bit;
+ u8 tstamp_ns_hi_bit;
+ u8 pad[8];
+
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp,
- ptp_tx_tstamp_entries);
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_caps,
+ tstamp_latches);
/**
- * struct virtchnl2_get_ptp_caps - Get PTP capabilities
- * @ptp_caps: PTP capability bitmap. See enum virtchnl2_ptp_caps.
- * @pad: Padding
- * @legacy_cross_time_reg: Legacy cross time register
- * @ptm_cross_time_reg: PTM cross time register
- * @device_clock_control: Device clock control
- * @tx_tstamp: TX timestamp
+ * 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
*
- * PV/VF sends this message to negotiate PTP capabilities. CP updates bitmap
+ * PF/VF sends this message to negotiate PTP capabilities. CP updates bitmap
* with supported features and fulfills appropriate structures.
- *
- * Associated with VIRTCHNL2_OP_GET_PTP_CAPS.
- */
-struct virtchnl2_get_ptp_caps {
- __le32 ptp_caps;
+ * 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_legacy_cross_time_reg legacy_cross_time_reg;
- struct virtchnl2_ptp_ptm_cross_time_reg ptm_cross_time_reg;
- struct virtchnl2_ptp_device_clock_control device_clock_control;
- struct virtchnl2_ptp_tx_tstamp tx_tstamp;
+ 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_VAR_LEN(88, virtchnl2_get_ptp_caps,
- tx_tstamp.ptp_tx_tstamp_entries);
+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_h: Timestamp high
- * @tstamp_l: Timestamp low
+ * @tstamp: Timestamp value
* @index: Index
* @valid: Timestamp validity
- * @pad: Padding
+ * @pad: Padding for future extensions
*/
struct virtchnl2_ptp_tx_tstamp_latch {
- __le32 tstamp_h;
- __le32 tstamp_l;
+ __le64 tstamp;
u8 index;
u8 valid;
u8 pad[6];
@@ -1938,26 +2001,97 @@ struct virtchnl2_ptp_tx_tstamp_latch {
VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch);
/**
- * struct virtchnl2_ptp_tx_tstamp_latches - PTP TX timestamp latches
+ * 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
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
+ * @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_GET_PTP_TX_TSTAMP_LATCHES.
+ * Associated with VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP.
*/
-struct virtchnl2_ptp_tx_tstamp_latches {
+struct virtchnl2_ptp_get_vport_tx_tstamp_latches {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
+ u8 get_devtime_with_txtstmp;
+ u8 pad[1];
+ u64 device_time;
struct virtchnl2_ptp_tx_tstamp_latch tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp_latches,
+
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_latches,
tstamp_latches);
+/* VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME
+ * @dev_time_ns: Device clock time value in nanoseconds
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the time from the main timer
+ */
+struct virtchnl2_ptp_get_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_dev_clk_time);
+
+/* 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
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the cross time
+ */
+struct virtchnl2_ptp_get_cross_time {
+ __le64 sys_time_ns;
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_get_cross_time);
+
+/* VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME
+ * @dev_time_ns: Device time value expressed in nanoseconds to set
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to set the time of the main timer
+ */
+struct virtchnl2_ptp_set_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_set_dev_clk_time);
+
+/* 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);
+
+/* 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);
+
static inline const char *virtchnl2_op_str(__le32 v_opcode)
{
switch (v_opcode) {
@@ -2025,10 +2159,22 @@ static inline const char *virtchnl2_op_str(__le32 v_opcode)
return "VIRTCHNL2_OP_DEL_QUEUE_GROUPS";
case VIRTCHNL2_OP_GET_PORT_STATS:
return "VIRTCHNL2_OP_GET_PORT_STATS";
- case VIRTCHNL2_OP_GET_PTP_CAPS:
- return "VIRTCHNL2_OP_GET_PTP_CAPS";
- case VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES:
- return "VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES";
+ case VIRTCHNL2_OP_PTP_GET_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_CAPS";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP";
+ case VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_CROSS_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_CROSS_TIME";
+ case VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS";
default:
return "Unsupported (update virtchnl2.h)";
}
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v4 2/4] net/idpf: add PTP virtchnl2 support
2026-01-20 19:11 ` [PATCH v4 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
@ 2026-01-20 19:11 ` Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 " Soumyadeep Hore
3 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-01-20 19:11 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Add a new file - idpf_ptp - to handle PTP virtchnl messages.
Keep the registers addresses in the PTP struct.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
drivers/net/intel/idpf/idpf_ptp.c | 646 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 227 ++++++
drivers/net/intel/idpf/meson.build | 1 +
5 files changed, 910 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
diff --git a/drivers/net/intel/idpf/idpf_common_device.h b/drivers/net/intel/idpf/idpf_common_device.h
index c32dcfbb12..3c84b96225 100644
--- a/drivers/net/intel/idpf/idpf_common_device.h
+++ b/drivers/net/intel/idpf/idpf_common_device.h
@@ -90,6 +90,7 @@ struct idpf_adapter {
/* For timestamp */
uint64_t time_hw;
+ struct idpf_ptp *ptp;
enum idpf_rx_func_type rx_func_type;
};
@@ -161,6 +162,9 @@ struct idpf_vport {
/* Event from ipf */
bool link_up;
uint32_t link_speed;
+
+ /* For PTP */
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
};
/* Message type read in virtual channel from PF */
diff --git a/drivers/net/intel/idpf/idpf_common_virtchnl.c b/drivers/net/intel/idpf/idpf_common_virtchnl.c
index e927d7415a..a28bc9bee6 100644
--- a/drivers/net/intel/idpf/idpf_common_virtchnl.c
+++ b/drivers/net/intel/idpf/idpf_common_virtchnl.c
@@ -4,6 +4,7 @@
#include "idpf_common_virtchnl.h"
#include "idpf_common_logs.h"
+#include "idpf_ptp.h"
#include <eal_export.h>
@@ -38,6 +39,28 @@ idpf_vc_clean(struct idpf_adapter *adapter)
return 0;
}
+/**
+ * idpf_ptp_is_mb_msg - Check if the message is PTP-related
+ * @op: virtchnl opcode
+ *
+ * Returns true if msg is PTP-related, false otherwise
+ */
+static inline bool idpf_ptp_is_mb_msg(uint32_t op)
+{
+ switch (op) {
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ 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:
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int
idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
uint16_t msg_size, uint8_t *msg)
@@ -71,8 +94,15 @@ idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
memcpy(dma_mem->va, msg, msg_size);
- ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
- ctlq_msg->func_id = 0;
+ if (idpf_ptp_is_mb_msg(op) && adapter->ptp->secondary_mbx.valid) {
+ 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 {
+ ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
+ ctlq_msg->func_id = 0;
+ }
+
ctlq_msg->data_len = msg_size;
ctlq_msg->cookie.mbx.chnl_opcode = op;
ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
diff --git a/drivers/net/intel/idpf/idpf_ptp.c b/drivers/net/intel/idpf/idpf_ptp.c
new file mode 100644
index 0000000000..3a364adc93
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.c
@@ -0,0 +1,646 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2001-2025 Intel Corporation
+ */
+
+#include "idpf_ptp.h"
+#include <base/virtchnl2.h>
+#include "idpf_common_virtchnl.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.
+ */
+static 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);
+
+ /* 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);
+
+ /* Set the device clock time */
+ direct = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME;
+ mailbox = VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB;
+ 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);
+
+ /* 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);
+}
+
+/**
+ * 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_cross_time_reg_offsets cross_tstamp_offsets;
+ struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
+ struct virtchnl2_ptp_get_caps send_ptp_caps_msg = { };
+ struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
+ struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
+ struct idpf_cmd_info args = { };
+ struct idpf_ptp_secondary_mbx *scnd_mbx;
+ struct idpf_ptp *ptp = adapter->ptp;
+ struct idpf_hw *hw = &adapter->hw;
+ enum idpf_ptp_access access_type;
+ int err = 0;
+ u32 temp_offset;
+
+ 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 |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CAPS;
+ args.in_args = (uint8_t *)&send_ptp_caps_msg;
+ args.in_args_size = sizeof(send_ptp_caps_msg);
+ recv_ptp_caps_msg = rte_zmalloc(NULL,
+ sizeof(struct virtchnl2_ptp_get_caps), 0);
+ if (!recv_ptp_caps_msg)
+ return -ENOMEM;
+
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(*recv_ptp_caps_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ goto free_mem;
+
+ recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
+ 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);
+
+ /* 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);
+
+ access_type = ptp->get_dev_clk_time_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ goto cross_tstamp;
+
+ 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_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.dev_clk_ns_h);
+ ptp->dev_clk_regs.dev_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_l);
+ ptp->dev_clk_regs.phy_clk_ns_l = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_h);
+ ptp->dev_clk_regs.phy_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+cross_tstamp:
+ access_type = ptp->get_cross_tstamp_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ goto discipline_clock;
+
+ 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_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.sys_time_ns_h);
+ ptp->dev_clk_regs.sys_time_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+discipline_clock:
+ access_type = ptp->adj_dev_clk_time_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ return err;
+
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_l);
+ ptp->dev_clk_regs.incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_h);
+ ptp->dev_clk_regs.incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_l);
+ ptp->dev_clk_regs.shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_h);
+ ptp->dev_clk_regs.shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+ /* PHY clock offsets */
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_cmd_type);
+ ptp->dev_clk_regs.phy_cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_l);
+ ptp->dev_clk_regs.phy_incval_l = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_h);
+ ptp->dev_clk_regs.phy_incval_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_l);
+ ptp->dev_clk_regs.phy_shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_h);
+ ptp->dev_clk_regs.phy_shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+free_mem:
+ rte_free(recv_ptp_caps_msg);
+
+ return err;
+}
+
+/**
+ * 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)
+{
+ uint32_t 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 */
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, shtime_enable);
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, exec_cmd | shtime_enable);
+}
+
+/**
+ * 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_cmd_info args = { };
+ int err = 0;
+ u64 dev_time;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&get_dev_clk_time_msg;
+ args.in_args_size = sizeof(get_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(get_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ get_dev_clk_time_msg = *(struct virtchnl2_ptp_get_dev_clk_time *)args.out_buffer;
+ dev_time = LE64_TO_CPU(get_dev_clk_time_msg.dev_time_ns);
+ dev_clk_time->dev_clk_time_ns = dev_time;
+
+ return err;
+}
+
+/**
+ * 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_cmd_info args = { };
+ int err = 0;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CROSS_TIME;
+ args.in_args = (uint8_t *)&cross_time_msg;
+ args.in_args_size = sizeof(cross_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(cross_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ cross_time_msg = *(struct virtchnl2_ptp_get_cross_time *)args.out_buffer;
+ 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 err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err = 0;
+
+ set_dev_clk_time_msg.dev_time_ns = CPU_TO_LE64(time);
+
+ args.ops = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&set_dev_clk_time_msg;
+ args.in_args_size = sizeof(set_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(set_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err = 0;
+
+ adj_dev_clk_time_msg.delta = CPU_TO_LE64(delta);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&adj_dev_clk_time_msg;
+ args.in_args_size = sizeof(adj_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err;
+
+ adj_dev_clk_fine_msg.incval = CPU_TO_LE64(incval);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE;
+ args.in_args = (uint8_t *)&adj_dev_clk_fine_msg;
+ args.in_args_size = sizeof(adj_dev_clk_fine_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_fine_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
+ enum idpf_ptp_access tstamp_access, get_dev_clk_access;
+ struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
+ struct idpf_ptp *ptp = vport->adapter->ptp;
+ struct idpf_cmd_info args = { };
+ int err = 0;
+ u16 num_latches, i;
+ 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;
+
+ send_tx_tstamp_caps.vport_id = CPU_TO_LE32(vport->vport_id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS;
+ args.in_args = (uint8_t *)&send_tx_tstamp_caps;
+ args.in_args_size = sizeof(send_tx_tstamp_caps);
+ args.out_size = IDPF_CTLQ_MAX_BUF_LEN;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ if (err < 0)
+ return err;
+
+ rcv_tx_tstamp_caps = (struct virtchnl2_ptp_get_vport_tx_tstamp_caps *)
+ args.out_buffer;
+ num_latches = LE16_TO_CPU(rcv_tx_tstamp_caps->num_latches);
+ size = sizeof(struct idpf_ptp_vport_tx_tstamp_caps) +
+ sizeof(struct idpf_ptp_tx_tstamp) * num_latches;
+ tstamp_caps = rte_zmalloc(NULL, size, 0);
+ if (!tstamp_caps)
+ return -ENOMEM;
+
+ tstamp_caps->access = true;
+ tstamp_caps->num_entries = num_latches;
+
+ tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ for (i = 0; i < tstamp_caps->num_entries; i++) {
+ __le32 offset_l, offset_h;
+
+ tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i];
+
+ if (tstamp_access == IDPF_PTP_DIRECT) {
+ offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
+ offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_l = LE32_TO_CPU(offset_l);
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_h = LE32_TO_CPU(offset_h);
+ }
+ tstamp_caps->tx_tstamp[i].idx = tx_tstamp_latch_caps.index;
+ }
+
+ tstamp_caps->latched_idx = -1;
+ vport->tx_tstamp_caps = tstamp_caps;
+
+ return err;
+}
+
+/**
+ * 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
+ * @tx_tstamp: Tx timestamp structure to be fulfilled with the timestamp value
+ *
+ * Read the value of the Tx timestamp for a given latch received from the
+ * Control Plane.
+ *
+ * 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 *tx_tstamp)
+{
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ u8 tstamp_ns_lo_bit;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ tx_tstamp->tstamp = LE64_TO_CPU(tstamp_latch->tstamp);
+ tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
+
+ return 0;
+}
+
+/**
+ * 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 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 *ptp_tx_tstamp;
+ struct idpf_cmd_info args = { };
+ int size, msg_size;
+ u32 vport_id;
+ u16 num_latches, id;
+ int err = 0;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+
+ size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) *
+ tx_tstamp_caps->num_entries;
+ send_tx_tstamp_msg = rte_zmalloc(NULL, size, 0);
+ if (!send_tx_tstamp_msg)
+ return -ENOMEM;
+
+ for (id = 0; id < tx_tstamp_caps->num_entries; id++,
+ ptp_tx_tstamp++)
+ send_tx_tstamp_msg->tstamp_latches[id].index =
+ ptp_tx_tstamp->idx;
+ send_tx_tstamp_msg->get_devtime_with_txtstmp = 1;
+
+ msg_size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) * id;
+ send_tx_tstamp_msg->vport_id = CPU_TO_LE32(vport->vport_id);
+ send_tx_tstamp_msg->num_latches = CPU_TO_LE16(id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP;
+ args.in_args = (uint8_t *)send_tx_tstamp_msg;
+ args.in_args_size = msg_size;
+ args.out_size = msg_size;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ rte_free(send_tx_tstamp_msg);
+ if (err < 0)
+ return err;
+
+ recv_tx_tstamp_msg = (struct virtchnl2_ptp_get_vport_tx_tstamp_latches *)
+ args.out_buffer;
+ vport_id = LE32_TO_CPU(recv_tx_tstamp_msg->vport_id);
+ if (vport->vport_id != vport_id)
+ return -EINVAL;
+
+ num_latches = LE16_TO_CPU(recv_tx_tstamp_msg->num_latches);
+
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+ for (id = 0; id < num_latches; id++, ptp_tx_tstamp++) {
+ tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[id];
+
+ if (!tstamp_latch.valid)
+ continue;
+
+ err = idpf_ptp_get_tstamp_value(vport, &tstamp_latch,
+ ptp_tx_tstamp);
+ if (!err) {
+ tx_tstamp_caps->latched_idx = id;
+ vport->adapter->time_hw = recv_tx_tstamp_msg->device_time;
+ }
+ break;
+ }
+ return err;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer value
+ * @adapter: Driver specific private structure
+ *
+ * Return: the device clock time.
+ */
+static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+ u32 hi, lo;
+
+ idpf_ptp_enable_shtime(adapter);
+
+ lo = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_l);
+ hi = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_h);
+
+ return ((u64)hi << 32) | lo;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value through mailbox
+ * @adapter: Driver specific private structure
+ * @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,
+ u64 *src_clk)
+{
+ struct idpf_ptp_dev_timers clk_time;
+ int err = 0;
+
+ err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
+ if (err)
+ return err;
+
+ *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
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: the device clock time on success, -errno otherwise.
+ */
+int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk)
+{
+ if (!adapter->ptp)
+ return 0;
+ 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, src_clk);
+ case IDPF_PTP_DIRECT:
+ *src_clk = idpf_ptp_read_src_clk_reg_direct(adapter);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/intel/idpf/idpf_ptp.h b/drivers/net/intel/idpf/idpf_ptp.h
new file mode 100644
index 0000000000..696bf615fb
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.h
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2001-2025 Intel Corporation
+ */
+
+#ifndef _IDPF_PTP_H_
+#define _IDPF_PTP_H_
+
+#include "idpf_osdep.h"
+#include "rte_time.h"
+#include "idpf_common_device.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
+ * @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
+ */
+struct idpf_ptp_dev_clk_regs {
+ /* Main clock */
+ volatile uint32_t *dev_clk_ns_l;
+ volatile uint32_t *dev_clk_ns_h;
+
+ /* PHY timer */
+ volatile uint32_t *phy_clk_ns_l;
+ volatile uint32_t *phy_clk_ns_h;
+
+ /* System time */
+ volatile uint32_t *sys_time_ns_l;
+ volatile uint32_t *sys_time_ns_h;
+
+ /* Main timer adjustments */
+ volatile uint32_t *incval_l;
+ volatile uint32_t *incval_h;
+ volatile uint32_t *shadj_l;
+ volatile uint32_t *shadj_h;
+
+ /* PHY timer adjustments */
+ volatile uint32_t *phy_incval_l;
+ volatile uint32_t *phy_incval_h;
+ volatile uint32_t *phy_shadj_l;
+ volatile uint32_t *phy_shadj_h;
+
+ /* Command */
+ volatile uint32_t *cmd;
+ volatile uint32_t *phy_cmd;
+ volatile uint32_t *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_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;
+};
+
+/**
+ * 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 - 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
+ * @tstamp: the Tx tstamp value
+ * @idx: the index of the Tx tstamp
+ */
+struct idpf_ptp_tx_tstamp {
+ u64 tstamp;
+ u32 tx_latch_reg_offset_l;
+ u32 tx_latch_reg_offset_h;
+ 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
+ * @access: indicates an access to Tx timestamp
+ * @latches_index: the index of the latched Tx timestamps
+ * @tx_tstamp: array of Tx timestamp parameters
+ */
+struct idpf_ptp_vport_tx_tstamp_caps {
+ u32 vport_id;
+ u16 num_entries;
+ u16 tstamp_ns_lo_bit;
+ u16 latched_idx;
+ bool access:1;
+ struct idpf_ptp_tx_tstamp tx_tstamp[];
+};
+
+/**
+ * struct idpf_ptp - PTP parameters
+ * @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
+ * @tx_tstamp_access: access type for the Tx timestamp value read
+ * @rsv: Reserved fields
+ * @secondary_mbx: parameters for using dedicated PTP mailbox
+ */
+struct idpf_ptp {
+ 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;
+ enum idpf_ptp_access set_dev_clk_time_access:2;
+ enum idpf_ptp_access adj_dev_clk_time_access:2;
+ enum idpf_ptp_access tx_tstamp_access:2;
+ u8 rsv:6;
+ struct idpf_ptp_secondary_mbx secondary_mbx;
+};
+
+/**
+ * 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;
+};
+
+int idpf_ptp_get_caps(struct idpf_adapter *adapter);
+int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk);
+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);
+int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport);
+int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
+
+/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
+static inline uint64_t
+idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
+ bool is_rx, uint32_t in_timestamp)
+{
+ const uint64_t mask = 0xFFFFFFFF;
+ uint32_t phc_time_lo, delta;
+ uint64_t ns;
+
+ if (flag != 0)
+ idpf_ptp_read_src_clk_reg(ad, &ad->time_hw);
+
+ /* Extract the lower 32 bits of the PHC time */
+ phc_time_lo = (uint32_t)(ad->time_hw);
+
+ /* Calculate the delta between the lower 32bits of the cached PHC
+ * time and the in_timestamp value.
+ */
+ delta = in_timestamp - phc_time_lo;
+
+ if (delta > mask / 2) {
+ /* Reverse the delta calculation here */
+ delta = phc_time_lo - in_timestamp;
+ ns = ad->time_hw - delta;
+ } else {
+ if (is_rx)
+ ns = ad->time_hw - delta;
+ else
+ ns = ad->time_hw + delta;
+ }
+
+ return ns;
+}
+#endif /* _IDPF_PTP_H_ */
diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
index a805d02ea2..5a4a3c2259 100644
--- a/drivers/net/intel/idpf/meson.build
+++ b/drivers/net/intel/idpf/meson.build
@@ -18,6 +18,7 @@ sources += files(
'idpf_ethdev.c',
'idpf_rxtx.c',
+ 'idpf_ptp.c',
)
if arch_subdir == 'x86' and dpdk_conf.get('RTE_IOVA_IN_MBUF') == 1
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v4 3/4] net/intel: add support for Precision Time Protocol
2026-01-20 19:11 ` [PATCH v4 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
@ 2026-01-20 19:11 ` Soumyadeep Hore
2026-01-23 0:04 ` [PATCH v5 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 " Soumyadeep Hore
3 siblings, 1 reply; 57+ messages in thread
From: Soumyadeep Hore @ 2026-01-20 19:11 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Add support for PTP that contains negotiation with CP to receive a
set of supported functionalities through capabilities,
get/set/adjust the time of the main timer and Tx timestamping
through secondary mailbox dedicated to PTP.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++++++++------
drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
drivers/net/intel/idpf/idpf_ethdev.c | 275 ++++++++++++++++++++++
4 files changed, 401 insertions(+), 71 deletions(-)
diff --git a/drivers/net/intel/common/tx.h b/drivers/net/intel/common/tx.h
index 5af64a4cfe..617e184976 100644
--- a/drivers/net/intel/common/tx.h
+++ b/drivers/net/intel/common/tx.h
@@ -109,6 +109,7 @@ struct ci_tx_queue {
struct ci_tx_queue *complq;
void **txqs; /*only valid for split queue mode*/
uint32_t tx_start_qid;
+ uint32_t latch_idx; /* Tx timestamp latch index */
uint16_t sw_nb_desc;
uint16_t sw_tail;
uint16_t rs_compl_count;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.c b/drivers/net/intel/idpf/idpf_common_rxtx.c
index a5d0795057..c8cd5b9d5b 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.c
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.c
@@ -9,6 +9,7 @@
#include "idpf_common_rxtx.h"
#include "idpf_common_device.h"
#include "../common/rx.h"
+#include "idpf_ptp.h"
int idpf_timestamp_dynfield_offset = -1;
uint64_t idpf_timestamp_dynflag;
@@ -435,58 +436,6 @@ idpf_qc_split_rxq_mbufs_alloc(struct idpf_rx_queue *rxq)
return 0;
}
-#define IDPF_TIMESYNC_REG_WRAP_GUARD_BAND 10000
-/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
-static inline uint64_t
-idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
- uint32_t in_timestamp)
-{
-#ifdef RTE_ARCH_X86_64
- struct idpf_hw *hw = &ad->hw;
- const uint64_t mask = 0xFFFFFFFF;
- uint32_t hi, lo, lo2, delta;
- uint64_t ns;
-
- if (flag != 0) {
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_EXEC_CMD_M |
- PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- /*
- * On typical system, the delta between lo and lo2 is ~1000ns,
- * so 10000 seems a large-enough but not overly-big guard band.
- */
- if (lo > (UINT32_MAX - IDPF_TIMESYNC_REG_WRAP_GUARD_BAND))
- lo2 = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- else
- lo2 = lo;
-
- if (lo2 < lo) {
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- }
-
- ad->time_hw = ((uint64_t)hi << 32) | lo;
- }
-
- delta = (in_timestamp - (uint32_t)(ad->time_hw & mask));
- if (delta > (mask / 2)) {
- delta = ((uint32_t)(ad->time_hw & mask) - in_timestamp);
- ns = ad->time_hw - delta;
- } else {
- ns = ad->time_hw + delta;
- }
-
- return ns;
-#else /* !RTE_ARCH_X86_64 */
- RTE_SET_USED(ad);
- RTE_SET_USED(flag);
- RTE_SET_USED(in_timestamp);
- return 0;
-#endif /* RTE_ARCH_X86_64 */
-}
-
#define IDPF_RX_FLEX_DESC_ADV_STATUS0_XSUM_S \
(RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S) | \
RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S) | \
@@ -655,8 +604,12 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rx_desc_ring = rxq->rx_ring;
ptype_tbl = rxq->adapter->ptype_tbl;
- if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
- rxq->hw_register_set = 1;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
while (nb_rx < nb_pkts) {
rx_desc = &rx_desc_ring[rx_id];
@@ -732,20 +685,38 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(rte_le_to_cpu_16(rx_desc->ptype_err_fflags0) &
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M) >>
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_S];
+
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
status_err0_qw1 = rte_le_to_cpu_16(rx_desc->status_err0_qw1);
pkt_flags = idpf_splitq_rx_csum_offload(status_err0_qw1);
pkt_flags |= idpf_splitq_rx_rss_offload(rxm, rx_desc);
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP)) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rx_desc->ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rx_desc->ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ if (rx_desc->ts_low & VIRTCHNL2_RX_FLEX_TSTAMP_VALID)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -839,10 +810,34 @@ idpf_split_tx_free(struct ci_tx_queue *cq)
static inline uint16_t
idpf_calc_context_desc(uint64_t flags)
{
- if ((flags & RTE_MBUF_F_TX_TCP_SEG) != 0)
- return 1;
+ static uint64_t mask = RTE_MBUF_F_TX_TCP_SEG |
+ RTE_MBUF_F_TX_IEEE1588_TMST;
- return 0;
+ return (flags & mask) ? 1 : 0;
+}
+
+/**
+ * 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 inline void
+idpf_tx_set_tstamp_desc(volatile union idpf_flex_tx_ctx_desc *ctx_desc,
+ uint32_t idx)
+{
+ ctx_desc->tsyn.qw1.cmd_dtype =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_CMD_M,
+ IDPF_TX_CTX_DESC_TSYN));
+ ctx_desc->tsyn.qw1.cmd_dtype |=
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_DTYPE_M,
+ IDPF_TX_DESC_DTYPE_CTX));
+ ctx_desc->tsyn.qw1.tsyn_reg_l =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_L_M,
+ idx));
+ ctx_desc->tsyn.qw1.tsyn_reg_h =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_H_M,
+ idx >> 2));
}
/* set TSO context descriptor
@@ -948,6 +943,9 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_desc);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_desc, txq->latch_idx);
+
tx_id++;
if (tx_id == txq->nb_tx_desc)
tx_id = 0;
@@ -1104,8 +1102,12 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rx_ring = rxq->rx_ring;
ptype_tbl = rxq->adapter->ptype_tbl;
- if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
- rxq->hw_register_set = 1;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
@@ -1167,17 +1169,33 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rxm->ol_flags |= pkt_flags;
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
rxm->ol_flags |= idpf_timestamp_dynflag;
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
rx_pkts[nb_rx++] = rxm;
@@ -1218,6 +1236,13 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
if (unlikely(!rxq) || unlikely(!rxq->q_started))
return nb_rx;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
+
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
rx_status0 = rte_le_to_cpu_16(rxdp->flex_nic_wb.status_error0);
@@ -1298,17 +1323,33 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(uint8_t)(rte_cpu_to_le_16(rxd.flex_nic_wb.ptype_flex_flags0) &
VIRTCHNL2_RX_FLEX_DESC_PTYPE_M)];
+ if ((first_seg->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -1474,6 +1515,9 @@ idpf_dp_singleq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_txd);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_txd, txq->latch_idx);
+
txe->last_id = tx_last;
tx_id = txe->next_id;
txe = txn;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.h b/drivers/net/intel/idpf/idpf_common_rxtx.h
index 3bc3323af4..6098d336a4 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.h
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.h
@@ -13,6 +13,12 @@
#include "../common/tx.h"
#include "../common/rx.h"
+#define FIELD_PREP(_mask, _val) \
+ (__extension__ ({ \
+ typeof(_mask) _x = (_mask); \
+ ((typeof(_x))(_val) << rte_bsf32(_x)) & (_x); \
+ }))
+
#define IDPF_RX_MAX_BURST 32
#define IDPF_RX_OFFLOAD_IPV4_CKSUM RTE_BIT64(1)
@@ -155,7 +161,11 @@ struct idpf_rx_queue {
struct idpf_rx_queue *bufq2;
uint64_t offloads;
+ uint64_t hw_time_update; /* Last time HW timestamp was updated */
uint32_t hw_register_set;
+ uint32_t time_high; /* high 32 bits of hardware timestamp register */
+ uint32_t hw_time_high; /* high 32 bits of timestamp */
+ uint32_t hw_time_low; /* low 32 bits of timestamp */
};
/* Offload features */
diff --git a/drivers/net/intel/idpf/idpf_ethdev.c b/drivers/net/intel/idpf/idpf_ethdev.c
index 5d786fbba6..22b7a1093c 100644
--- a/drivers/net/intel/idpf/idpf_ethdev.c
+++ b/drivers/net/intel/idpf/idpf_ethdev.c
@@ -2,6 +2,8 @@
* Copyright(c) 2022 Intel Corporation
*/
+#include <math.h>
+
#include <rte_atomic.h>
#include <rte_eal.h>
#include <rte_ether.h>
@@ -14,6 +16,7 @@
#include "idpf_ethdev.h"
#include "idpf_rxtx.h"
#include "../common/tx.h"
+#include "idpf_ptp.h"
#define IDPF_TX_SINGLE_Q "tx_single"
#define IDPF_RX_SINGLE_Q "rx_single"
@@ -841,6 +844,270 @@ idpf_dev_close(struct rte_eth_dev *dev)
return 0;
}
+static int
+idpf_timesync_enable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct timespec sys_ts;
+ uint64_t ns;
+ int ret = 0, q_id = 0;
+
+ if (dev->data->dev_started && !(dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP)) {
+ PMD_DRV_LOG(ERR, "Rx timestamp offload not configured");
+ return -1;
+ }
+
+ adapter->ptp = rte_zmalloc(NULL, sizeof(struct idpf_ptp), 0);
+ if (adapter->ptp == NULL) {
+ PMD_DRV_LOG(ERR, "Failed to allocate memory for PTP");
+ return -ENOMEM;
+ }
+
+ ret = idpf_ptp_get_caps(adapter);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get PTP capabilities, err=%d", ret);
+ goto err_ptp;
+ }
+ /* Write the default increment time value if the clock adjustments
+ * are enabled.
+ */
+ if (adapter->ptp->adj_dev_clk_time_access != IDPF_PTP_NONE) {
+ ret = idpf_ptp_adj_dev_clk_fine(adapter,
+ adapter->ptp->base_incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set incval failed, err=%d", ret);
+ goto err_ptp;
+ }
+ }
+
+ /* Do not initialize the PTP if the device clock time cannot be read. */
+ if (adapter->ptp->get_dev_clk_time_access == IDPF_PTP_NONE) {
+ PMD_DRV_LOG(ERR, "Getting device clock time is not supported");
+ ret = -EIO;
+ goto err_ptp;
+ }
+
+ /* Set the device clock time to system time. */
+ if (adapter->ptp->set_dev_clk_time_access != IDPF_PTP_NONE) {
+ clock_gettime(CLOCK_REALTIME, &sys_ts);
+ ns = rte_timespec_to_ns(&sys_ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err=%d", ret);
+ goto err_ptp;
+ }
+ }
+
+ ret = idpf_ptp_get_vport_tstamps_caps(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get vport timestamp capabilities, err=%d", ret);
+ goto err_ptp;
+ }
+
+ for (q_id = 0; q_id < dev->data->nb_tx_queues; q_id++) {
+ struct ci_tx_queue *txq = dev->data->tx_queues[q_id];
+ txq->latch_idx = vport->tx_tstamp_caps->tx_tstamp[q_id].idx;
+ }
+
+ 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;
+
+err_ptp:
+ if (ret != 0) {
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ }
+ return ret;
+}
+
+static int
+idpf_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp,
+ uint32_t flags)
+{
+ struct idpf_rx_queue *rxq;
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ts_ns;
+
+ rxq = dev->data->rx_queues[flags];
+
+ ts_ns = idpf_tstamp_convert_32b_64b(adapter, 1, true, rxq->time_high);
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ return 0;
+}
+
+static int
+idpf_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ uint16_t latch_idx;
+ uint64_t ts_ns, tstamp;
+ int ret = 0;
+
+ ret = idpf_ptp_get_tx_tstamp(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get TX timestamp");
+ return ret;
+ }
+
+ latch_idx = vport->tx_tstamp_caps->latched_idx;
+ tstamp = vport->tx_tstamp_caps->tx_tstamp[latch_idx].tstamp;
+ ts_ns = idpf_tstamp_convert_32b_64b(vport->adapter, 0, false, tstamp);
+
+ /* Convert to timespec */
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ vport->tx_tstamp_caps->latched_idx = -1;
+
+ return 0;
+}
+
+static int
+idpf_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint64_t time, ns;
+ int ret = 0;
+
+ if (ptp->adj_dev_clk_time_access != IDPF_PTP_MAILBOX) {
+ PMD_DRV_LOG(ERR, "Adjusting device clock time is not supported");
+ return -ENOTSUP;
+ }
+
+ if (delta > INT32_MAX || delta < INT32_MIN) {
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP read clock time failed, err %d", ret);
+ return ret;
+ }
+
+ ns = time + delta;
+
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err %d", ret);
+
+ return ret;
+ }
+
+ ret = idpf_ptp_adj_dev_clk_time(adapter, delta);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP adjusting clock failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ int64_t incval, diff = 0;
+ bool negative = false;
+ uint64_t div, rem;
+ uint64_t divisor = 1000000ULL << 16;
+ int shift;
+ int ret = 0;
+
+ incval = ptp->base_incval;
+
+ if (ppm < 0) {
+ negative = true;
+ ppm = -ppm;
+ }
+
+ /* can incval * ppm overflow ? */
+ if (log2(incval) + log2(ppm) > 62) {
+ rem = ppm % divisor;
+ div = ppm / divisor;
+ diff = div * incval;
+ ppm = rem;
+
+ shift = log2(incval) + log2(ppm) - 62;
+ if (shift > 0) {
+ /* drop precision */
+ ppm >>= shift;
+ divisor >>= shift;
+ }
+ }
+
+ if (divisor)
+ diff = diff + incval * ppm / divisor;
+
+ if (negative)
+ incval -= diff;
+ else
+ incval += diff;
+
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int
+idpf_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ns;
+ int ret = 0;
+
+ ns = rte_timespec_to_ns(ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP write time failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t time;
+ int ret = 0;
+
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP read time failed, err %d", ret);
+ else
+ *ts = rte_ns_to_timespec(time);
+
+ return ret;
+}
+
+static int
+idpf_timesync_disable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+
+ if (vport->tx_tstamp_caps) {
+ rte_free(vport->tx_tstamp_caps);
+ vport->tx_tstamp_caps = NULL;
+ }
+
+ if (adapter->ptp) {
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ }
+
+ return 0;
+}
+
+
static const struct eth_dev_ops idpf_eth_dev_ops = {
.dev_configure = idpf_dev_configure,
.dev_close = idpf_dev_close,
@@ -867,6 +1134,14 @@ static const struct eth_dev_ops idpf_eth_dev_ops = {
.xstats_get = idpf_dev_xstats_get,
.xstats_get_names = idpf_dev_xstats_get_names,
.xstats_reset = idpf_dev_xstats_reset,
+ .timesync_enable = idpf_timesync_enable,
+ .timesync_read_rx_timestamp = idpf_timesync_read_rx_timestamp,
+ .timesync_read_tx_timestamp = idpf_timesync_read_tx_timestamp,
+ .timesync_adjust_time = idpf_timesync_adjust_time,
+ .timesync_adjust_freq = idpf_timesync_adjust_freq,
+ .timesync_read_time = idpf_timesync_read_time,
+ .timesync_write_time = idpf_timesync_write_time,
+ .timesync_disable = idpf_timesync_disable,
};
static int
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v4 4/4] doc: add PTP IDPF documentation
2026-01-20 19:11 ` [PATCH v4 0/4] Enable PTP feature for MEV Soumyadeep Hore
` (2 preceding siblings ...)
2026-01-20 19:11 ` [PATCH v4 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
@ 2026-01-20 19:11 ` Soumyadeep Hore
3 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-01-20 19:11 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Updated the IDPF Documentation with PTP feature.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
doc/guides/nics/idpf.rst | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/doc/guides/nics/idpf.rst b/doc/guides/nics/idpf.rst
index b99cc18188..6e02aa3dbb 100644
--- a/doc/guides/nics/idpf.rst
+++ b/doc/guides/nics/idpf.rst
@@ -109,3 +109,18 @@ The paths are chosen based on 2 conditions:
A value "P" means the offload feature is not supported by vector path.
If any not supported features are used, idpf vector PMD is disabled
and the scalar paths are chosen.
+
+Time Synchronisation
+~~~~~~~~~~~~~~~~~~~~
+
+The system operator can run a PTP (Precision Time Protocol) client application
+to synchronise the time on the network card in ACC
+(and optionally the time on the system) to the PTP primary.
+
+IDPF PMD supports PTP client applications that use the DPDK IEEE 1588 API
+to communicate with the PTP primary clock.
+Note that PTP client application needs to run on PF in ACC.
+
+.. code-block:: console
+
+ examples/dpdk-ptpclient -l 0-3 -n 3 -a 0000:ec:00.1 -- -T 1 -p 0x1
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* Re: [PATCH v5 0/4] Enable PTP feature for MEV
2026-01-23 0:04 ` [PATCH v5 0/4] Enable PTP feature for MEV Soumyadeep Hore
@ 2026-01-22 23:40 ` Stephen Hemminger
2026-01-23 0:04 ` [PATCH v5 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
` (3 subsequent siblings)
4 siblings, 0 replies; 57+ messages in thread
From: Stephen Hemminger @ 2026-01-22 23:40 UTC (permalink / raw)
To: Soumyadeep Hore
Cc: dev, bruce.richardson, rajesh3.kumar, aman.deep.singh,
manoj.kumar.subbarao
On Thu, 22 Jan 2026 19:04:45 -0500
Soumyadeep Hore <soumyadeep.hore@intel.com> wrote:
> Enabling basic PTP feature in IDPF PMD using virtchnl messages.
>
> ---
> v5:
> - Addressed AI comments
> ---
> v4:
> - Addressed Bruce's comments
> ---
> v3:
> - Fixed checkpatch warnings
> - Addressed Bruce's comments
> ---
> v2:
> - Fixed essential checkpatch warnings
> ---
>
> Milena Olech (1):
> net/idpf: add a new API for PTP support
>
> Soumyadeep Hore (3):
> net/idpf: add PTP virtchnl2 support
> net/intel: add support for PTP
> doc: add PTP IDPF documentation
>
> doc/guides/nics/features/idpf.ini | 1 +
> doc/guides/nics/idpf.rst | 15 +
> doc/guides/rel_notes/release_26_03.rst | 4 +
> drivers/net/intel/common/tx.h | 1 +
> drivers/net/intel/idpf/base/virtchnl2.h | 324 ++++++---
> drivers/net/intel/idpf/idpf_common_device.h | 4 +
> drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++--
> drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
> drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
> drivers/net/intel/idpf/idpf_ethdev.c | 270 ++++++++
> drivers/net/intel/idpf/idpf_ptp.c | 639 ++++++++++++++++++
> drivers/net/intel/idpf/idpf_ptp.h | 227 +++++++
> drivers/net/intel/idpf/meson.build | 1 +
> 13 files changed, 1554 insertions(+), 162 deletions(-)
> create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
> create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
>
I used this for a trial balloon for automated patch review automation.
$ ./devtools/analyze-patch.sh -v -p xai ~/Downloads/bundle-1673-mev-v5.mbox
=== Request ===
Provider: xai
Model: grok-3
AGENTS file: AGENTS.md
Patch file: /home/shemminger/Downloads/bundle-1673-mev-v5.mbox
===============
=== Token Usage ===
Prompt tokens: 34732
Completion tokens: 2545
Total tokens: 37277
===================
=== Patch Review: bundle-1673-mev-v5.mbox (via Grok) ===
Below is a detailed review of the provided DPDK patch series ('bundle-1673-mev-v5.mbox') based on the AGENTS.md guidelines. The feedback is organized by severity level (Error, Warning, Info) as defined in the guidelines. I have reviewed all four patches in the series for compliance with commit message format, license/copyright, C coding style, API and documentation requirements, and other guideline violations.
---
### **Patch Series Overview**
- **Series Title**: [PATCH v5 1-4/4] net/idpf: Add PTP support for IDPF driver
- **Patches Reviewed**:
1. net/idpf: add a new API for PTP support
2. net/idpf: add PTP virtchnl2 support
3. net/intel: add support for PTP
4. doc: add PTP IDPF documentation
---
### **Feedback by Severity Level**
#### **Errors (Must Fix)**
1. **Commit Message Prefix Inconsistency (All Patches)**
- **Issue**: The commit message prefixes are inconsistent or incorrect in some patches.
- Patch 1: `net/idpf:` is correct.
- Patch 2: `net/idpf:` is correct.
- Patch 3: `net/intel:` is incorrect. According to AGENTS.md, the prefix should be `net/idpf:` since this is specific to the IDPF driver.
- Patch 4: `doc:` is incorrect. It should be `doc/guides:` or similar to reflect the documentation update context.
- **Guideline**: AGENTS.md specifies that the correct component prefix must be used (e.g., `net/idpf:` for IDPF driver changes).
- **Action**: Update Patch 3 prefix to `net/idpf:` and Patch 4 prefix to `doc/guides:` or an appropriate documentation prefix.
2. **Missing Co-author in Signed-off-by (Patch 1)**
- **Issue**: Patch 1 commit message body mentions authorship by Milena Olech (`From: Milena Olech <milena.olech@intel.com>`), and her `Signed-off-by` is included. However, the email header shows Soumyadeep Hore as the submitter, and both are in the `Signed-off-by` lines. This is fine, but ensure that all contributors are correctly acknowledged.
- **Guideline**: AGENTS.md requires `Signed-off-by:` with real names and valid emails for all contributors.
- **Action**: No immediate action needed as both are signed off, but confirm with maintainers if the `From:` field in the commit message should match the submitter or if this is acceptable as is.
3. **Missing Release Notes for API Changes (Patch 1 and Patch 2)**
- **Issue**: Patch 1 introduces new APIs for PTP in `virtchnl2.h`, and Patch 2 adds implementation support in `idpf_ptp.c`. While Patch 4 updates release notes, the notes only mention "Timesync features" under Patch 3's context. There is no specific mention of the new APIs introduced in Patch 1 and 2.
- **Guideline**: AGENTS.md requires release notes for API changes and new driver subsystems.
- **Action**: Update `doc/guides/rel_notes/release_26_03.rst` in Patch 4 to explicitly mention the new PTP APIs and virtchnl2 support introduced in Patches 1 and 2.
---
#### **Warnings (Should Fix)**
1. **Missing `__rte_experimental` Tag for New APIs (Patch 1)**
- **Issue**: Patch 1 introduces new APIs for PTP support in `virtchnl2.h` (e.g., structures and opcodes for PTP operations). These are not marked with `__rte_experimental` in the header file.
- **Guideline**: AGENTS.md mandates that new APIs must be marked as `__rte_experimental` and must be alone on the line immediately preceding the return type in header files.
- **Action**: Add `__rte_experimental` tags to all new API declarations in `virtchnl2.h` (e.g., before function prototypes or structure definitions if they are part of the public API).
2. **Missing Test Coverage for New APIs (Patch 1 and Patch 2)**
- **Issue**: The patches introduce new PTP APIs and implementation, but there is no mention of corresponding tests in `app/test` or hooks in `app/testpmd`.
- **Guideline**: AGENTS.md requires new APIs to have hooks in `app/testpmd` and tests in the functional test suite using `TEST_ASSERT` macros and `unit_test_suite_runner`.
- **Action**: Add test coverage in a subsequent patch or update the series to include test cases for the new PTP APIs. Mention plans for test addition if not included in this series.
3. **Missing Documentation for New APIs (Patch 1)**
- **Issue**: Patch 1 adds new structures and opcodes in `virtchnl2.h` for PTP support, but there are no Doxygen comments or detailed documentation for these new APIs in the header file. Patch 4 only documents high-level PTP support in the driver, not the specific APIs.
- **Guideline**: AGENTS.md requires Doxygen comments for public APIs.
- **Action**: Add Doxygen comments for new structures and enums in `virtchnl2.h` to explain their purpose and usage.
4. **Driver Global Variables Without Unique Prefixes (Patch 2 and Patch 3)**
- **Issue**: Patch 2 introduces global structures like `idpf_ptp` in `idpf_ptp.c`, and Patch 3 integrates them into `idpf_adapter`. These do not use a unique prefix (e.g., `idpf_`) for all global variables to prevent namespace clashes during static linking.
- **Guideline**: AGENTS.md requires driver-specific prefixes for global variables to avoid clashes.
- **Action**: Ensure all global variables and structures have a unique prefix like `idpf_` to avoid potential conflicts.
5. **Inappropriate Use of `rte_zmalloc` Without Justification (Patch 2)**
- **Issue**: Patch 2 uses `rte_zmalloc` for memory allocation in functions like `idpf_ptp_get_vport_tstamps_caps` and `idpf_ptp_get_tx_tstamp`. There is no justification provided for using `rte_zmalloc` over standard `malloc`, especially since this does not appear to be for DMA-accessible or hugepage memory.
- **Guideline**: AGENTS.md specifies that `rte_malloc` should only be used for specific cases like DMA or hugepage memory. For general allocations, `malloc` is preferred.
- **Action**: Replace `rte_zmalloc` with `malloc` unless there is a specific need for hugepage memory, and justify the choice if `rte_zmalloc` is retained.
6. **Missing `Cc: stable@dpdk.org` for Potential Backport (All Patches)**
- **Issue**: The patches introduce new features for PTP support, which might be candidates for stable backporting. However, none of the commit messages include `Cc: stable@dpdk.org`.
- **Guideline**: AGENTS.md requires `Cc: stable@dpdk.org` for patches that are candidates for stable backport.
- **Action**: Add `Cc: stable@dpdk.org` to the commit messages if these changes are intended for backporting to stable branches.
7. **Documentation Does Not Match Code Behavior (Patch 4)**
- **Issue**: Patch 4 adds documentation for PTP support in `idpf.rst`, mentioning that the PTP client needs to run on PF in ACC. However, the code in Patch 3 (`idpf_ethdev.c`) does not explicitly restrict or document this behavior in the implementation.
- **Guideline**: AGENTS.md requires documentation to match code behavior.
- **Action**: Ensure the documentation reflects the exact behavior implemented in the code, or add a code-level check/comment to enforce this requirement.
8. **Commit Message Body Content (Patch 1 and Patch 3)**
- **Issue**: The commit message bodies for Patch 1 and Patch 3 are brief and lack detailed context about the changes or the problem being solved.
- **Guideline**: AGENTS.md requires commit message bodies to describe the issue being fixed or feature being added with sufficient context for reviewers.
- **Action**: Expand the commit message bodies to provide more context about why PTP support is being added and what specific problems it addresses.
---
#### **Info (Consider)**
1. **Commit Message Style Consistency (All Patches)**
- **Issue**: The commit message subjects are generally compliant, but Patch 3's subject (`net/intel: add support for PTP`) could be more specific, e.g., `net/idpf: add PTP timesync support`.
- **Guideline**: AGENTS.md prefers descriptive and specific subject lines.
- **Suggestion**: Refine subject lines for better clarity and consistency across the series (e.g., use `net/idpf:` consistently and add more specificity).
2. **Code Style Minor Preferences (Patch 2 and Patch 3)**
- **Issue**: Some functions in `idpf_ptp.c` and `idpf_ethdev.c` have long lines or inconsistent indentation in comments (e.g., mixed use of tabs/spaces in alignment). While not a major issue, consistency is preferred.
- **Guideline**: AGENTS.md suggests consistent style within files (hard tabs for indentation, spaces for alignment).
- **Suggestion**: Run `checkpatches.sh` to ensure style consistency and fix any minor formatting issues.
3. **Optimization Suggestion for Timestamp Handling (Patch 3)**
- **Issue**: The timestamp handling in `idpf_common_rxtx.c` uses a software timer to update hardware timestamps periodically. This could potentially be optimized by reducing unnecessary checks or using hardware events if supported.
- **Guideline**: AGENTS.md encourages optimization suggestions as "Info" level feedback.
- **Suggestion**: Consider optimizing the timestamp update logic if hardware events or interrupts can be used to reduce software overhead.
4. **Alternative Approach for PTP Mailbox Handling (Patch 2)**
- **Issue**: Patch 2 introduces a secondary mailbox for PTP communication. An alternative could be to handle this within the primary mailbox with specific opcodes to reduce complexity.
- **Guideline**: AGENTS.md allows "Info" level feedback for alternative approaches.
- **Suggestion**: Evaluate if using a single mailbox with tagged messages could simplify the design, and document the rationale for using a secondary mailbox if it is retained.
---
### **Summary of Actions Required**
- **Errors**:
- Fix commit message prefixes in Patch 3 and Patch 4.
- Update release notes in Patch 4 to cover API changes from Patches 1 and 2.
- **Warnings**:
- Add `__rte_experimental` tags for new APIs in Patch 1.
- Add test coverage for new APIs or document plans for testing.
- Add Doxygen comments for new APIs in `virtchnl2.h`.
- Use unique prefixes for driver global variables.
- Replace `rte_zmalloc` with `malloc` unless justified.
- Consider adding `Cc: stable@dpdk.org` for backport candidates.
- Ensure documentation matches code behavior.
- Expand commit message bodies for better context.
- **Info**:
- Refine commit message subjects for clarity.
- Address minor code style issues.
- Consider optimizations and alternative approaches as suggested.
---
### **Conclusion**
This patch series introduces significant functionality for PTP support in the IDPF driver, but it requires several fixes to comply with DPDK guidelines, particularly around API tagging, documentation, and testing. Addressing the "Errors" and "Warnings" will ensure the patches are ready for upstream submission. The "Info" items are optional but could improve the overall quality of the submission.
Please revise the patches based on the feedback provided and resubmit for further review. If you have questions or need clarification on any point, feel free to ask.
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH v5 0/4] Enable PTP feature for MEV
2026-01-20 19:11 ` [PATCH v4 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
@ 2026-01-23 0:04 ` Soumyadeep Hore
2026-01-22 23:40 ` Stephen Hemminger
` (4 more replies)
0 siblings, 5 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-01-23 0:04 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Enabling basic PTP feature in IDPF PMD using virtchnl messages.
---
v5:
- Addressed AI comments
---
v4:
- Addressed Bruce's comments
---
v3:
- Fixed checkpatch warnings
- Addressed Bruce's comments
---
v2:
- Fixed essential checkpatch warnings
---
Milena Olech (1):
net/idpf: add a new API for PTP support
Soumyadeep Hore (3):
net/idpf: add PTP virtchnl2 support
net/intel: add support for PTP
doc: add PTP IDPF documentation
doc/guides/nics/features/idpf.ini | 1 +
doc/guides/nics/idpf.rst | 15 +
doc/guides/rel_notes/release_26_03.rst | 4 +
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/base/virtchnl2.h | 324 ++++++---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++--
drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
drivers/net/intel/idpf/idpf_ethdev.c | 270 ++++++++
drivers/net/intel/idpf/idpf_ptp.c | 639 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 227 +++++++
drivers/net/intel/idpf/meson.build | 1 +
13 files changed, 1554 insertions(+), 162 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
--
2.47.1
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH v5 1/4] net/idpf: add a new API for PTP support
2026-01-23 0:04 ` [PATCH v5 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-01-22 23:40 ` Stephen Hemminger
@ 2026-01-23 0:04 ` Soumyadeep Hore
2026-02-03 11:56 ` Bruce Richardson
2026-01-23 0:04 ` [PATCH v5 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
` (2 subsequent siblings)
4 siblings, 1 reply; 57+ messages in thread
From: Soumyadeep Hore @ 2026-01-23 0:04 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao,
Milena Olech
From: Milena Olech <milena.olech@intel.com>
Add PTP capabilities to determine which PTP features are enabled -
get/set the time, cross timestamp, adjust the clock and timestamping.
Create appropriate structures and opcodes to support features introduced by
capabilities.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
---
drivers/net/intel/idpf/base/virtchnl2.h | 324 +++++++++++++++++-------
1 file changed, 235 insertions(+), 89 deletions(-)
diff --git a/drivers/net/intel/idpf/base/virtchnl2.h b/drivers/net/intel/idpf/base/virtchnl2.h
index 6cfb4f56fa..0b01daee30 100644
--- a/drivers/net/intel/idpf/base/virtchnl2.h
+++ b/drivers/net/intel/idpf/base/virtchnl2.h
@@ -118,8 +118,14 @@ enum virtchnl2_op {
VIRTCHNL2_OP_DEL_QUEUE_GROUPS = 539,
VIRTCHNL2_OP_GET_PORT_STATS = 540,
/* TimeSync opcodes */
- VIRTCHNL2_OP_GET_PTP_CAPS = 541,
- VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES = 542,
+ 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,
};
#define VIRTCHNL2_RDMA_INVALID_QUEUE_IDX 0xFFFF
@@ -1813,123 +1819,180 @@ VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_promisc_info);
* enum virtchnl2_ptp_caps - PTP capabilities
*/
enum virtchnl2_ptp_caps {
- VIRTCHNL2_PTP_CAP_LEGACY_CROSS_TIME = BIT(0),
- VIRTCHNL2_PTP_CAP_PTM = BIT(1),
- VIRTCHNL2_PTP_CAP_DEVICE_CLOCK_CONTROL = BIT(2),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_DIRECT = BIT(3),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_VIRTCHNL = BIT(4),
-};
-
-/**
- * struct virtchnl2_ptp_legacy_cross_time_reg - Legacy cross time registers
- * offsets.
+ 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_legacy_cross_time_reg {
- __le32 shadow_time_0;
- __le32 shadow_time_l;
- __le32 shadow_time_h;
- __le32 cmd_sync;
+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(16, virtchnl2_ptp_legacy_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_clk_reg_offsets);
/**
- * struct virtchnl2_ptp_ptm_cross_time_reg - PTM cross time registers 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_ptm_cross_time_reg {
- __le32 art_l;
- __le32 art_h;
- __le32 cmd_sync;
+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_ptm_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_cross_time_reg_offsets);
/**
- * struct virtchnl2_ptp_device_clock_control - Registers needed to control the
- * main clock.
+ * 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_device_clock_control {
- __le32 cmd;
- __le32 incval_l;
- __le32 incval_h;
- __le32 shadj_l;
- __le32 shadj_h;
- u8 pad[4];
+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(24, virtchnl2_ptp_device_clock_control);
+VIRTCHNL2_CHECK_STRUCT_LEN(40, virtchnl2_ptp_clk_adj_reg_offsets);
/**
- * struct virtchnl2_ptp_tx_tstamp_entry - PTP TX timestamp entry
- * @tx_latch_register_base: TX latch register base
- * @tx_latch_register_offset: TX latch register offset
- * @index: Index
- * @pad: Padding
+ * 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_entry {
- __le32 tx_latch_register_base;
- __le32 tx_latch_register_offset;
+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_entry);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch_caps);
/**
- * struct virtchnl2_ptp_tx_tstamp - Structure that defines tx tstamp entries
+ * struct virtchnl2_ptp_get_vport_tx_tstamp_caps - Structure that defines Tx
+ * tstamp entries
+ * @vport_id: Vport number
* @num_latches: Total number of latches
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
- * @ptp_tx_tstamp_entries: Aarray of TX timestamp entries
+ * @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_tx_tstamp {
+struct virtchnl2_ptp_get_vport_tx_tstamp_caps {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
- struct virtchnl2_ptp_tx_tstamp_entry ptp_tx_tstamp_entries[STRUCT_VAR_LEN];
+ u8 tstamp_ns_lo_bit;
+ u8 tstamp_ns_hi_bit;
+ u8 pad[8];
+
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp,
- ptp_tx_tstamp_entries);
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_caps,
+ tstamp_latches);
/**
- * struct virtchnl2_get_ptp_caps - Get PTP capabilities
- * @ptp_caps: PTP capability bitmap. See enum virtchnl2_ptp_caps.
- * @pad: Padding
- * @legacy_cross_time_reg: Legacy cross time register
- * @ptm_cross_time_reg: PTM cross time register
- * @device_clock_control: Device clock control
- * @tx_tstamp: TX timestamp
+ * 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
*
- * PV/VF sends this message to negotiate PTP capabilities. CP updates bitmap
+ * PF/VF sends this message to negotiate PTP capabilities. CP updates bitmap
* with supported features and fulfills appropriate structures.
- *
- * Associated with VIRTCHNL2_OP_GET_PTP_CAPS.
- */
-struct virtchnl2_get_ptp_caps {
- __le32 ptp_caps;
+ * 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_legacy_cross_time_reg legacy_cross_time_reg;
- struct virtchnl2_ptp_ptm_cross_time_reg ptm_cross_time_reg;
- struct virtchnl2_ptp_device_clock_control device_clock_control;
- struct virtchnl2_ptp_tx_tstamp tx_tstamp;
+ 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_VAR_LEN(88, virtchnl2_get_ptp_caps,
- tx_tstamp.ptp_tx_tstamp_entries);
+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_h: Timestamp high
- * @tstamp_l: Timestamp low
+ * @tstamp: Timestamp value
* @index: Index
* @valid: Timestamp validity
- * @pad: Padding
+ * @pad: Padding for future extensions
*/
struct virtchnl2_ptp_tx_tstamp_latch {
- __le32 tstamp_h;
- __le32 tstamp_l;
+ __le64 tstamp;
u8 index;
u8 valid;
u8 pad[6];
@@ -1938,26 +2001,97 @@ struct virtchnl2_ptp_tx_tstamp_latch {
VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch);
/**
- * struct virtchnl2_ptp_tx_tstamp_latches - PTP TX timestamp latches
+ * 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
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
+ * @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_GET_PTP_TX_TSTAMP_LATCHES.
+ * Associated with VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP.
*/
-struct virtchnl2_ptp_tx_tstamp_latches {
+struct virtchnl2_ptp_get_vport_tx_tstamp_latches {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
+ u8 get_devtime_with_txtstmp;
+ u8 pad[1];
+ u64 device_time;
struct virtchnl2_ptp_tx_tstamp_latch tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp_latches,
+
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_latches,
tstamp_latches);
+/* VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME
+ * @dev_time_ns: Device clock time value in nanoseconds
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the time from the main timer
+ */
+struct virtchnl2_ptp_get_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_dev_clk_time);
+
+/* 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
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the cross time
+ */
+struct virtchnl2_ptp_get_cross_time {
+ __le64 sys_time_ns;
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_get_cross_time);
+
+/* VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME
+ * @dev_time_ns: Device time value expressed in nanoseconds to set
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to set the time of the main timer
+ */
+struct virtchnl2_ptp_set_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_set_dev_clk_time);
+
+/* 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);
+
+/* 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);
+
static inline const char *virtchnl2_op_str(__le32 v_opcode)
{
switch (v_opcode) {
@@ -2025,10 +2159,22 @@ static inline const char *virtchnl2_op_str(__le32 v_opcode)
return "VIRTCHNL2_OP_DEL_QUEUE_GROUPS";
case VIRTCHNL2_OP_GET_PORT_STATS:
return "VIRTCHNL2_OP_GET_PORT_STATS";
- case VIRTCHNL2_OP_GET_PTP_CAPS:
- return "VIRTCHNL2_OP_GET_PTP_CAPS";
- case VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES:
- return "VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES";
+ case VIRTCHNL2_OP_PTP_GET_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_CAPS";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP";
+ case VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_CROSS_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_CROSS_TIME";
+ case VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS";
default:
return "Unsupported (update virtchnl2.h)";
}
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v5 2/4] net/idpf: add PTP virtchnl2 support
2026-01-23 0:04 ` [PATCH v5 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-01-22 23:40 ` Stephen Hemminger
2026-01-23 0:04 ` [PATCH v5 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
@ 2026-01-23 0:04 ` Soumyadeep Hore
2026-02-03 12:52 ` Bruce Richardson
2026-02-14 19:49 ` [PATCH v6 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-01-23 0:04 ` [PATCH v5 3/4] net/intel: add support for PTP Soumyadeep Hore
2026-01-23 0:04 ` [PATCH v5 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
4 siblings, 2 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-01-23 0:04 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Add a new file - idpf_ptp - to handle PTP virtchnl messages.
Keep the registers addresses in the PTP struct.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
drivers/net/intel/idpf/idpf_ptp.c | 639 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 227 +++++++
drivers/net/intel/idpf/meson.build | 1 +
5 files changed, 903 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
diff --git a/drivers/net/intel/idpf/idpf_common_device.h b/drivers/net/intel/idpf/idpf_common_device.h
index c32dcfbb12..3c84b96225 100644
--- a/drivers/net/intel/idpf/idpf_common_device.h
+++ b/drivers/net/intel/idpf/idpf_common_device.h
@@ -90,6 +90,7 @@ struct idpf_adapter {
/* For timestamp */
uint64_t time_hw;
+ struct idpf_ptp *ptp;
enum idpf_rx_func_type rx_func_type;
};
@@ -161,6 +162,9 @@ struct idpf_vport {
/* Event from ipf */
bool link_up;
uint32_t link_speed;
+
+ /* For PTP */
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
};
/* Message type read in virtual channel from PF */
diff --git a/drivers/net/intel/idpf/idpf_common_virtchnl.c b/drivers/net/intel/idpf/idpf_common_virtchnl.c
index e927d7415a..a28bc9bee6 100644
--- a/drivers/net/intel/idpf/idpf_common_virtchnl.c
+++ b/drivers/net/intel/idpf/idpf_common_virtchnl.c
@@ -4,6 +4,7 @@
#include "idpf_common_virtchnl.h"
#include "idpf_common_logs.h"
+#include "idpf_ptp.h"
#include <eal_export.h>
@@ -38,6 +39,28 @@ idpf_vc_clean(struct idpf_adapter *adapter)
return 0;
}
+/**
+ * idpf_ptp_is_mb_msg - Check if the message is PTP-related
+ * @op: virtchnl opcode
+ *
+ * Returns true if msg is PTP-related, false otherwise
+ */
+static inline bool idpf_ptp_is_mb_msg(uint32_t op)
+{
+ switch (op) {
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ 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:
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int
idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
uint16_t msg_size, uint8_t *msg)
@@ -71,8 +94,15 @@ idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
memcpy(dma_mem->va, msg, msg_size);
- ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
- ctlq_msg->func_id = 0;
+ if (idpf_ptp_is_mb_msg(op) && adapter->ptp->secondary_mbx.valid) {
+ 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 {
+ ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
+ ctlq_msg->func_id = 0;
+ }
+
ctlq_msg->data_len = msg_size;
ctlq_msg->cookie.mbx.chnl_opcode = op;
ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
diff --git a/drivers/net/intel/idpf/idpf_ptp.c b/drivers/net/intel/idpf/idpf_ptp.c
new file mode 100644
index 0000000000..b8f6afa518
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.c
@@ -0,0 +1,639 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#include "idpf_ptp.h"
+#include <base/virtchnl2.h>
+#include "idpf_common_virtchnl.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.
+ */
+static 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 = (uint8_t)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 = (uint8_t)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_MB;
+ ptp->set_dev_clk_time_access = (uint8_t)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 = (uint8_t)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 = (uint8_t)idpf_ptp_get_access(adapter,
+ direct,
+ mailbox);
+}
+
+/**
+ * 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_cross_time_reg_offsets cross_tstamp_offsets;
+ struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
+ struct virtchnl2_ptp_get_caps send_ptp_caps_msg = { };
+ struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
+ struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
+ struct idpf_cmd_info args = { };
+ struct idpf_ptp_secondary_mbx *scnd_mbx;
+ struct idpf_ptp *ptp = adapter->ptp;
+ struct idpf_hw *hw = &adapter->hw;
+ enum idpf_ptp_access access_type;
+ int err;
+ u32 temp_offset;
+
+ 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 |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CAPS;
+ args.in_args = (uint8_t *)&send_ptp_caps_msg;
+ args.in_args_size = sizeof(send_ptp_caps_msg);
+
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(*recv_ptp_caps_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
+ 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);
+
+ /* 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);
+
+ access_type = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ goto cross_tstamp;
+
+ 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_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.dev_clk_ns_h);
+ ptp->dev_clk_regs.dev_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_l);
+ ptp->dev_clk_regs.phy_clk_ns_l = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_h);
+ ptp->dev_clk_regs.phy_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+cross_tstamp:
+ access_type = (enum idpf_ptp_access)ptp->get_cross_tstamp_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ goto discipline_clock;
+
+ 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_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.sys_time_ns_h);
+ ptp->dev_clk_regs.sys_time_ns_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+discipline_clock:
+ access_type = (enum idpf_ptp_access)ptp->adj_dev_clk_time_access;
+ if (access_type != IDPF_PTP_DIRECT)
+ return err;
+
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_l);
+ ptp->dev_clk_regs.incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_h);
+ ptp->dev_clk_regs.incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_l);
+ ptp->dev_clk_regs.shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_h);
+ ptp->dev_clk_regs.shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+ /* PHY clock offsets */
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_cmd_type);
+ ptp->dev_clk_regs.phy_cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_l);
+ ptp->dev_clk_regs.phy_incval_l = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_h);
+ ptp->dev_clk_regs.phy_incval_h = IDPF_PCI_REG_ADDR(hw,
+ temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_l);
+ ptp->dev_clk_regs.phy_shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_h);
+ ptp->dev_clk_regs.phy_shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+ return err;
+}
+
+/**
+ * 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)
+{
+ uint32_t 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 */
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, shtime_enable);
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, exec_cmd | shtime_enable);
+}
+
+/**
+ * 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_cmd_info args = { };
+ int err;
+ u64 dev_time;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&get_dev_clk_time_msg;
+ args.in_args_size = sizeof(get_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(get_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ get_dev_clk_time_msg = *(struct virtchnl2_ptp_get_dev_clk_time *)args.out_buffer;
+ dev_time = LE64_TO_CPU(get_dev_clk_time_msg.dev_time_ns);
+ dev_clk_time->dev_clk_time_ns = dev_time;
+
+ return err;
+}
+
+/**
+ * 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_cmd_info args = { };
+ int err;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CROSS_TIME;
+ args.in_args = (uint8_t *)&cross_time_msg;
+ args.in_args_size = sizeof(cross_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(cross_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ cross_time_msg = *(struct virtchnl2_ptp_get_cross_time *)args.out_buffer;
+ 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 err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err;
+
+ set_dev_clk_time_msg.dev_time_ns = CPU_TO_LE64(time);
+
+ args.ops = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&set_dev_clk_time_msg;
+ args.in_args_size = sizeof(set_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(set_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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, int64_t delta)
+{
+ struct virtchnl2_ptp_adj_dev_clk_time adj_dev_clk_time_msg = { };
+ struct idpf_cmd_info args = { };
+ int err;
+
+ adj_dev_clk_time_msg.delta = CPU_TO_LE64(delta);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&adj_dev_clk_time_msg;
+ args.in_args_size = sizeof(adj_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 = { };
+ struct idpf_cmd_info args = { };
+ int err;
+
+ adj_dev_clk_fine_msg.incval = CPU_TO_LE64(incval);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE;
+ args.in_args = (uint8_t *)&adj_dev_clk_fine_msg;
+ args.in_args_size = sizeof(adj_dev_clk_fine_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_fine_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+/**
+ * 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 virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
+ enum idpf_ptp_access tstamp_access, get_dev_clk_access;
+ struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
+ struct idpf_ptp *ptp = vport->adapter->ptp;
+ struct idpf_cmd_info args = { };
+ int err;
+ u16 num_latches, i;
+ u32 size;
+
+ if (ptp == NULL)
+ return -EOPNOTSUPP;
+
+ tstamp_access = (enum idpf_ptp_access)ptp->tx_tstamp_access;
+ get_dev_clk_access = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (tstamp_access == IDPF_PTP_NONE ||
+ get_dev_clk_access == IDPF_PTP_NONE)
+ return -EOPNOTSUPP;
+
+ send_tx_tstamp_caps.vport_id = CPU_TO_LE32(vport->vport_id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS;
+ args.in_args = (uint8_t *)&send_tx_tstamp_caps;
+ args.in_args_size = sizeof(send_tx_tstamp_caps);
+ args.out_size = IDPF_CTLQ_MAX_BUF_LEN;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ if (err < 0)
+ return err;
+
+ rcv_tx_tstamp_caps = (struct virtchnl2_ptp_get_vport_tx_tstamp_caps *)
+ args.out_buffer;
+ num_latches = LE16_TO_CPU(rcv_tx_tstamp_caps->num_latches);
+ size = sizeof(struct idpf_ptp_vport_tx_tstamp_caps) +
+ sizeof(struct idpf_ptp_tx_tstamp) * num_latches;
+ tstamp_caps = rte_zmalloc(NULL, size, 0);
+ if (tstamp_caps == NULL)
+ return -ENOMEM;
+
+ tstamp_caps->access = true;
+ tstamp_caps->num_entries = num_latches;
+
+ tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ for (i = 0; i < tstamp_caps->num_entries; i++) {
+ __le32 offset_l, offset_h;
+
+ tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i];
+
+ if (tstamp_access == IDPF_PTP_DIRECT) {
+ offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
+ offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_l = LE32_TO_CPU(offset_l);
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_h = LE32_TO_CPU(offset_h);
+ }
+ tstamp_caps->tx_tstamp[i].idx = tx_tstamp_latch_caps.index;
+ }
+
+ tstamp_caps->latched_idx = -1;
+ vport->tx_tstamp_caps = tstamp_caps;
+
+ return err;
+}
+
+/**
+ * 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
+ * @tx_tstamp: Tx timestamp structure to be fulfilled with the timestamp value
+ *
+ * Read the value of the Tx timestamp for a given latch received from the
+ * Control Plane.
+ *
+ * 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 *tx_tstamp)
+{
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ u8 tstamp_ns_lo_bit;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ tx_tstamp->tstamp = LE64_TO_CPU(tstamp_latch->tstamp);
+ tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
+
+ return 0;
+}
+
+/**
+ * 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 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 *ptp_tx_tstamp;
+ struct idpf_cmd_info args = { };
+ int size, msg_size;
+ u32 vport_id;
+ u16 num_latches, id;
+ int err;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+
+ size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) *
+ tx_tstamp_caps->num_entries;
+ send_tx_tstamp_msg = rte_zmalloc(NULL, size, 0);
+ if (send_tx_tstamp_msg == NULL)
+ return -ENOMEM;
+
+ for (id = 0; id < tx_tstamp_caps->num_entries; id++,
+ ptp_tx_tstamp++)
+ send_tx_tstamp_msg->tstamp_latches[id].index =
+ ptp_tx_tstamp->idx;
+ send_tx_tstamp_msg->get_devtime_with_txtstmp = 1;
+
+ msg_size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) * id;
+ send_tx_tstamp_msg->vport_id = CPU_TO_LE32(vport->vport_id);
+ send_tx_tstamp_msg->num_latches = CPU_TO_LE16(id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP;
+ args.in_args = (uint8_t *)send_tx_tstamp_msg;
+ args.in_args_size = msg_size;
+ args.out_size = msg_size;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ rte_free(send_tx_tstamp_msg);
+ if (err < 0)
+ return err;
+
+ recv_tx_tstamp_msg = (struct virtchnl2_ptp_get_vport_tx_tstamp_latches *)
+ args.out_buffer;
+ vport_id = LE32_TO_CPU(recv_tx_tstamp_msg->vport_id);
+ if (vport->vport_id != vport_id)
+ return -EINVAL;
+
+ num_latches = LE16_TO_CPU(recv_tx_tstamp_msg->num_latches);
+
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+ for (id = 0; id < num_latches; id++, ptp_tx_tstamp++) {
+ tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[id];
+
+ if (!tstamp_latch.valid)
+ continue;
+
+ err = idpf_ptp_get_tstamp_value(vport, &tstamp_latch,
+ ptp_tx_tstamp);
+ if (err == 0) {
+ tx_tstamp_caps->latched_idx = id;
+ vport->adapter->time_hw = recv_tx_tstamp_msg->device_time;
+ }
+ break;
+ }
+ return err;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer value
+ * @adapter: Driver specific private structure
+ *
+ * Return: the device clock time.
+ */
+static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+ u32 hi, lo;
+
+ idpf_ptp_enable_shtime(adapter);
+
+ lo = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_l);
+ hi = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_h);
+
+ return ((u64)hi << 32) | lo;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value through mailbox
+ * @adapter: Driver specific private structure
+ * @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,
+ u64 *src_clk)
+{
+ struct idpf_ptp_dev_timers clk_time;
+ int err;
+
+ err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
+ if (err)
+ return err;
+
+ *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
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: the device clock time on success, -errno otherwise.
+ */
+int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk)
+{
+ if (adapter->ptp == NULL)
+ return 0;
+ switch ((enum idpf_ptp_access)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, src_clk);
+ case IDPF_PTP_DIRECT:
+ *src_clk = idpf_ptp_read_src_clk_reg_direct(adapter);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/intel/idpf/idpf_ptp.h b/drivers/net/intel/idpf/idpf_ptp.h
new file mode 100644
index 0000000000..68ac66ae91
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.h
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#ifndef _IDPF_PTP_H_
+#define _IDPF_PTP_H_
+
+#include "idpf_osdep.h"
+#include <rte_time.h>
+#include "idpf_common_device.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 {
+ uint32_t exec_cmd_mask;
+ uint32_t 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
+ * @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
+ */
+struct idpf_ptp_dev_clk_regs {
+ /* Main clock */
+ volatile uint32_t *dev_clk_ns_l;
+ volatile uint32_t *dev_clk_ns_h;
+
+ /* PHY timer */
+ volatile uint32_t *phy_clk_ns_l;
+ volatile uint32_t *phy_clk_ns_h;
+
+ /* System time */
+ volatile uint32_t *sys_time_ns_l;
+ volatile uint32_t *sys_time_ns_h;
+
+ /* Main timer adjustments */
+ volatile uint32_t *incval_l;
+ volatile uint32_t *incval_h;
+ volatile uint32_t *shadj_l;
+ volatile uint32_t *shadj_h;
+
+ /* PHY timer adjustments */
+ volatile uint32_t *phy_incval_l;
+ volatile uint32_t *phy_incval_h;
+ volatile uint32_t *phy_shadj_l;
+ volatile uint32_t *phy_shadj_h;
+
+ /* Command */
+ volatile uint32_t *cmd;
+ volatile uint32_t *phy_cmd;
+ volatile uint32_t *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_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 {
+ uint16_t peer_mbx_q_id;
+ uint16_t peer_id;
+ 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 - 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
+ * @tstamp: the Tx tstamp value
+ * @idx: the index of the Tx tstamp
+ */
+struct idpf_ptp_tx_tstamp {
+ uint64_t tstamp;
+ uint32_t tx_latch_reg_offset_l;
+ uint32_t tx_latch_reg_offset_h;
+ uint32_t 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
+ * @access: indicates an access to Tx timestamp
+ * @latches_index: the index of the latched Tx timestamps
+ * @tx_tstamp: array of Tx timestamp parameters
+ */
+struct idpf_ptp_vport_tx_tstamp_caps {
+ uint32_t vport_id;
+ uint16_t num_entries;
+ uint16_t tstamp_ns_lo_bit;
+ uint16_t latched_idx;
+ bool access:1;
+ struct idpf_ptp_tx_tstamp tx_tstamp[];
+};
+
+/**
+ * @struct idpf_ptp - PTP parameters
+ * @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
+ * @tx_tstamp_access: access type for the Tx timestamp value read
+ * @rsv: Reserved fields
+ * @secondary_mbx: parameters for using dedicated PTP mailbox
+ */
+struct idpf_ptp {
+ uint64_t base_incval;
+ uint64_t max_adj;
+ struct idpf_ptp_cmd cmd;
+ struct idpf_ptp_dev_clk_regs dev_clk_regs;
+ uint32_t caps;
+ uint8_t get_dev_clk_time_access:2;
+ uint8_t get_cross_tstamp_access:2;
+ uint8_t set_dev_clk_time_access:2;
+ uint8_t adj_dev_clk_time_access:2;
+ uint8_t tx_tstamp_access:2;
+ uint8_t rsv:6;
+ struct idpf_ptp_secondary_mbx secondary_mbx;
+};
+
+/**
+ * @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 {
+ uint64_t sys_time_ns;
+ uint64_t dev_clk_time_ns;
+};
+
+int idpf_ptp_get_caps(struct idpf_adapter *adapter);
+int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t *src_clk);
+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, uint64_t time);
+int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t incval);
+int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, int64_t delta);
+int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport);
+int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
+
+/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
+static inline uint64_t
+idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
+ bool is_rx, uint32_t in_timestamp)
+{
+ const uint64_t mask = 0xFFFFFFFF;
+ uint32_t phc_time_lo, delta;
+ uint64_t ns;
+
+ if (flag != 0)
+ idpf_ptp_read_src_clk_reg(ad, &ad->time_hw);
+
+ /* Extract the lower 32 bits of the PHC time */
+ phc_time_lo = (uint32_t)(ad->time_hw);
+
+ /* Calculate the delta between the lower 32bits of the cached PHC
+ * time and the in_timestamp value.
+ */
+ delta = in_timestamp - phc_time_lo;
+
+ if (delta > mask / 2) {
+ /* Reverse the delta calculation here */
+ delta = phc_time_lo - in_timestamp;
+ ns = ad->time_hw - delta;
+ } else {
+ if (is_rx)
+ ns = ad->time_hw - delta;
+ else
+ ns = ad->time_hw + delta;
+ }
+
+ return ns;
+}
+#endif /* _IDPF_PTP_H_ */
diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
index a805d02ea2..5a4a3c2259 100644
--- a/drivers/net/intel/idpf/meson.build
+++ b/drivers/net/intel/idpf/meson.build
@@ -18,6 +18,7 @@ sources += files(
'idpf_ethdev.c',
'idpf_rxtx.c',
+ 'idpf_ptp.c',
)
if arch_subdir == 'x86' and dpdk_conf.get('RTE_IOVA_IN_MBUF') == 1
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v5 3/4] net/intel: add support for PTP
2026-01-23 0:04 ` [PATCH v5 0/4] Enable PTP feature for MEV Soumyadeep Hore
` (2 preceding siblings ...)
2026-01-23 0:04 ` [PATCH v5 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
@ 2026-01-23 0:04 ` Soumyadeep Hore
2026-02-03 15:37 ` Bruce Richardson
2026-01-23 0:04 ` [PATCH v5 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
4 siblings, 1 reply; 57+ messages in thread
From: Soumyadeep Hore @ 2026-01-23 0:04 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Add support for PTP that contains negotiation with CP to receive a
set of supported functionalities through capabilities,
get/set/adjust the time of the main timer and Tx timestamping
through secondary mailbox dedicated to PTP.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++++++++------
drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
drivers/net/intel/idpf/idpf_ethdev.c | 270 ++++++++++++++++++++++
4 files changed, 396 insertions(+), 71 deletions(-)
diff --git a/drivers/net/intel/common/tx.h b/drivers/net/intel/common/tx.h
index 5af64a4cfe..617e184976 100644
--- a/drivers/net/intel/common/tx.h
+++ b/drivers/net/intel/common/tx.h
@@ -109,6 +109,7 @@ struct ci_tx_queue {
struct ci_tx_queue *complq;
void **txqs; /*only valid for split queue mode*/
uint32_t tx_start_qid;
+ uint32_t latch_idx; /* Tx timestamp latch index */
uint16_t sw_nb_desc;
uint16_t sw_tail;
uint16_t rs_compl_count;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.c b/drivers/net/intel/idpf/idpf_common_rxtx.c
index a5d0795057..c8cd5b9d5b 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.c
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.c
@@ -9,6 +9,7 @@
#include "idpf_common_rxtx.h"
#include "idpf_common_device.h"
#include "../common/rx.h"
+#include "idpf_ptp.h"
int idpf_timestamp_dynfield_offset = -1;
uint64_t idpf_timestamp_dynflag;
@@ -435,58 +436,6 @@ idpf_qc_split_rxq_mbufs_alloc(struct idpf_rx_queue *rxq)
return 0;
}
-#define IDPF_TIMESYNC_REG_WRAP_GUARD_BAND 10000
-/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
-static inline uint64_t
-idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
- uint32_t in_timestamp)
-{
-#ifdef RTE_ARCH_X86_64
- struct idpf_hw *hw = &ad->hw;
- const uint64_t mask = 0xFFFFFFFF;
- uint32_t hi, lo, lo2, delta;
- uint64_t ns;
-
- if (flag != 0) {
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_EXEC_CMD_M |
- PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- /*
- * On typical system, the delta between lo and lo2 is ~1000ns,
- * so 10000 seems a large-enough but not overly-big guard band.
- */
- if (lo > (UINT32_MAX - IDPF_TIMESYNC_REG_WRAP_GUARD_BAND))
- lo2 = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- else
- lo2 = lo;
-
- if (lo2 < lo) {
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- }
-
- ad->time_hw = ((uint64_t)hi << 32) | lo;
- }
-
- delta = (in_timestamp - (uint32_t)(ad->time_hw & mask));
- if (delta > (mask / 2)) {
- delta = ((uint32_t)(ad->time_hw & mask) - in_timestamp);
- ns = ad->time_hw - delta;
- } else {
- ns = ad->time_hw + delta;
- }
-
- return ns;
-#else /* !RTE_ARCH_X86_64 */
- RTE_SET_USED(ad);
- RTE_SET_USED(flag);
- RTE_SET_USED(in_timestamp);
- return 0;
-#endif /* RTE_ARCH_X86_64 */
-}
-
#define IDPF_RX_FLEX_DESC_ADV_STATUS0_XSUM_S \
(RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S) | \
RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S) | \
@@ -655,8 +604,12 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rx_desc_ring = rxq->rx_ring;
ptype_tbl = rxq->adapter->ptype_tbl;
- if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
- rxq->hw_register_set = 1;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
while (nb_rx < nb_pkts) {
rx_desc = &rx_desc_ring[rx_id];
@@ -732,20 +685,38 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(rte_le_to_cpu_16(rx_desc->ptype_err_fflags0) &
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M) >>
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_S];
+
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
status_err0_qw1 = rte_le_to_cpu_16(rx_desc->status_err0_qw1);
pkt_flags = idpf_splitq_rx_csum_offload(status_err0_qw1);
pkt_flags |= idpf_splitq_rx_rss_offload(rxm, rx_desc);
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP)) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rx_desc->ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rx_desc->ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ if (rx_desc->ts_low & VIRTCHNL2_RX_FLEX_TSTAMP_VALID)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -839,10 +810,34 @@ idpf_split_tx_free(struct ci_tx_queue *cq)
static inline uint16_t
idpf_calc_context_desc(uint64_t flags)
{
- if ((flags & RTE_MBUF_F_TX_TCP_SEG) != 0)
- return 1;
+ static uint64_t mask = RTE_MBUF_F_TX_TCP_SEG |
+ RTE_MBUF_F_TX_IEEE1588_TMST;
- return 0;
+ return (flags & mask) ? 1 : 0;
+}
+
+/**
+ * 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 inline void
+idpf_tx_set_tstamp_desc(volatile union idpf_flex_tx_ctx_desc *ctx_desc,
+ uint32_t idx)
+{
+ ctx_desc->tsyn.qw1.cmd_dtype =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_CMD_M,
+ IDPF_TX_CTX_DESC_TSYN));
+ ctx_desc->tsyn.qw1.cmd_dtype |=
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_DTYPE_M,
+ IDPF_TX_DESC_DTYPE_CTX));
+ ctx_desc->tsyn.qw1.tsyn_reg_l =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_L_M,
+ idx));
+ ctx_desc->tsyn.qw1.tsyn_reg_h =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_H_M,
+ idx >> 2));
}
/* set TSO context descriptor
@@ -948,6 +943,9 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_desc);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_desc, txq->latch_idx);
+
tx_id++;
if (tx_id == txq->nb_tx_desc)
tx_id = 0;
@@ -1104,8 +1102,12 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rx_ring = rxq->rx_ring;
ptype_tbl = rxq->adapter->ptype_tbl;
- if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
- rxq->hw_register_set = 1;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
@@ -1167,17 +1169,33 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rxm->ol_flags |= pkt_flags;
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
rxm->ol_flags |= idpf_timestamp_dynflag;
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
rx_pkts[nb_rx++] = rxm;
@@ -1218,6 +1236,13 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
if (unlikely(!rxq) || unlikely(!rxq->q_started))
return nb_rx;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
+ uint64_t sw_cur_time = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
+ if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
+ rxq->hw_register_set = 1;
+ }
+
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
rx_status0 = rte_le_to_cpu_16(rxdp->flex_nic_wb.status_error0);
@@ -1298,17 +1323,33 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(uint8_t)(rte_cpu_to_le_16(rxd.flex_nic_wb.ptype_flex_flags0) &
VIRTCHNL2_RX_FLEX_DESC_PTYPE_M)];
+ if ((first_seg->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
- ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
- rxq->hw_register_set = 0;
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
+ if (unlikely(rxq->hw_register_set)) {
+ ts_ns = idpf_tstamp_convert_32b_64b(ad,
+ rxq->hw_register_set, true,
+ rxq->time_high);
+ rxq->hw_time_low = (uint32_t)ts_ns;
+ rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
+ rxq->hw_register_set = 0;
+ } else {
+ if (rxq->time_high < rxq->hw_time_low)
+ rxq->hw_time_high += 1;
+ ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
+ rxq->hw_time_low = rxq->time_high;
+ }
+ rxq->hw_time_update = rte_get_timer_cycles() /
+ (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -1474,6 +1515,9 @@ idpf_dp_singleq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_txd);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_txd, txq->latch_idx);
+
txe->last_id = tx_last;
tx_id = txe->next_id;
txe = txn;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.h b/drivers/net/intel/idpf/idpf_common_rxtx.h
index 3bc3323af4..6098d336a4 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.h
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.h
@@ -13,6 +13,12 @@
#include "../common/tx.h"
#include "../common/rx.h"
+#define FIELD_PREP(_mask, _val) \
+ (__extension__ ({ \
+ typeof(_mask) _x = (_mask); \
+ ((typeof(_x))(_val) << rte_bsf32(_x)) & (_x); \
+ }))
+
#define IDPF_RX_MAX_BURST 32
#define IDPF_RX_OFFLOAD_IPV4_CKSUM RTE_BIT64(1)
@@ -155,7 +161,11 @@ struct idpf_rx_queue {
struct idpf_rx_queue *bufq2;
uint64_t offloads;
+ uint64_t hw_time_update; /* Last time HW timestamp was updated */
uint32_t hw_register_set;
+ uint32_t time_high; /* high 32 bits of hardware timestamp register */
+ uint32_t hw_time_high; /* high 32 bits of timestamp */
+ uint32_t hw_time_low; /* low 32 bits of timestamp */
};
/* Offload features */
diff --git a/drivers/net/intel/idpf/idpf_ethdev.c b/drivers/net/intel/idpf/idpf_ethdev.c
index 5d786fbba6..5e57a45775 100644
--- a/drivers/net/intel/idpf/idpf_ethdev.c
+++ b/drivers/net/intel/idpf/idpf_ethdev.c
@@ -14,6 +14,7 @@
#include "idpf_ethdev.h"
#include "idpf_rxtx.h"
#include "../common/tx.h"
+#include "idpf_ptp.h"
#define IDPF_TX_SINGLE_Q "tx_single"
#define IDPF_RX_SINGLE_Q "rx_single"
@@ -841,6 +842,267 @@ idpf_dev_close(struct rte_eth_dev *dev)
return 0;
}
+static int
+idpf_timesync_enable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct timespec sys_ts;
+ uint64_t ns;
+ int ret, q_id;
+ if (dev->data->dev_started && !(dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP)) {
+ PMD_DRV_LOG(ERR, "Rx timestamp offload not configured");
+ return -1;
+ }
+
+ adapter->ptp = rte_zmalloc(NULL, sizeof(struct idpf_ptp), 0);
+ if (adapter->ptp == NULL) {
+ PMD_DRV_LOG(ERR, "Failed to allocate memory for PTP");
+ return -ENOMEM;
+ }
+
+ ret = idpf_ptp_get_caps(adapter);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get PTP capabilities, err=%d", ret);
+ goto fail_ptp;
+ }
+
+ /* Write the default increment time value if the clock adjustments are enabled. */
+ if (adapter->ptp->adj_dev_clk_time_access != IDPF_PTP_NONE) {
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, adapter->ptp->base_incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set incval failed, err=%d", ret);
+ goto fail_ptp;
+ }
+ }
+
+ /* Do not initialize the PTP if the device clock time cannot be read. */
+ if (adapter->ptp->get_dev_clk_time_access == IDPF_PTP_NONE) {
+ PMD_DRV_LOG(ERR, "Getting device clock time is not supported");
+ ret = -EIO;
+ goto fail_ptp;
+ }
+
+ /* Set the device clock time to system time. */
+ if (adapter->ptp->set_dev_clk_time_access != IDPF_PTP_NONE) {
+ clock_gettime(CLOCK_REALTIME, &sys_ts);
+ ns = rte_timespec_to_ns(&sys_ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err=%d", ret);
+ goto fail_ptp;
+ }
+ }
+
+ ret = idpf_ptp_get_vport_tstamps_caps(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get vport timestamp capabilities, err=%d", ret);
+ goto fail_ptp;
+ }
+
+ for (q_id = 0; q_id < dev->data->nb_tx_queues; q_id++) {
+ struct ci_tx_queue *txq = dev->data->tx_queues[q_id];
+ txq->latch_idx = vport->tx_tstamp_caps->tx_tstamp[q_id].idx;
+ }
+
+ 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;
+
+ return 0;
+
+fail_ptp:
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ return ret;
+}
+
+static int
+idpf_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp,
+ uint32_t flags)
+{
+ struct idpf_rx_queue *rxq;
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ts_ns;
+
+ rxq = dev->data->rx_queues[flags];
+
+ ts_ns = idpf_tstamp_convert_32b_64b(adapter, 1, true, rxq->time_high);
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ return 0;
+}
+
+static int
+idpf_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ uint16_t latch_idx;
+ uint64_t ts_ns, tstamp;
+ int ret;
+
+ ret = idpf_ptp_get_tx_tstamp(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get TX timestamp");
+ return ret;
+ }
+
+ latch_idx = vport->tx_tstamp_caps->latched_idx;
+ tstamp = vport->tx_tstamp_caps->tx_tstamp[latch_idx].tstamp;
+ ts_ns = idpf_tstamp_convert_32b_64b(vport->adapter, 0, false, tstamp);
+
+ /* Convert to timespec */
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ vport->tx_tstamp_caps->latched_idx = -1;
+
+ return 0;
+}
+
+static int
+idpf_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint64_t time, ns;
+ int ret;
+
+ if (ptp->adj_dev_clk_time_access != IDPF_PTP_MAILBOX) {
+ PMD_DRV_LOG(ERR, "Adjusting device clock time is not supported");
+ return -ENOTSUP;
+ }
+
+ if (delta > INT32_MAX || delta < INT32_MIN) {
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP read clock time failed, err %d", ret);
+ return ret;
+ }
+
+ ns = time + delta;
+
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err %d", ret);
+
+ return ret;
+ }
+
+ ret = idpf_ptp_adj_dev_clk_time(adapter, delta);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP adjusting clock failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ int64_t incval, diff = 0;
+ bool negative = false;
+ uint64_t div, rem;
+ uint64_t divisor = 1000000ULL << 16;
+ int shift;
+ int ret;
+
+ incval = ptp->base_incval;
+
+ if (ppm < 0) {
+ negative = true;
+ ppm = -ppm;
+ }
+
+ /* can incval * ppm overflow ? */
+ if (rte_log2_u64(incval) + rte_log2_u64(ppm) > 62) {
+ rem = ppm % divisor;
+ div = ppm / divisor;
+ diff = div * incval;
+ ppm = rem;
+
+ shift = rte_log2_u64(incval) + rte_log2_u64(ppm) - 62;
+ if (shift > 0) {
+ /* drop precision */
+ ppm >>= shift;
+ divisor >>= shift;
+ }
+ }
+
+ if (divisor)
+ diff = diff + incval * ppm / divisor;
+
+ if (negative)
+ incval -= diff;
+ else
+ incval += diff;
+
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int
+idpf_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ns;
+ int ret;
+
+ ns = rte_timespec_to_ns(ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP write time failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t time;
+ int ret;
+
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP read time failed, err %d", ret);
+ else
+ *ts = rte_ns_to_timespec(time);
+
+ return ret;
+}
+
+static int
+idpf_timesync_disable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+
+ if (vport->tx_tstamp_caps) {
+ rte_free(vport->tx_tstamp_caps);
+ vport->tx_tstamp_caps = NULL;
+ }
+
+ if (adapter->ptp) {
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ }
+
+ return 0;
+}
+
+
static const struct eth_dev_ops idpf_eth_dev_ops = {
.dev_configure = idpf_dev_configure,
.dev_close = idpf_dev_close,
@@ -867,6 +1129,14 @@ static const struct eth_dev_ops idpf_eth_dev_ops = {
.xstats_get = idpf_dev_xstats_get,
.xstats_get_names = idpf_dev_xstats_get_names,
.xstats_reset = idpf_dev_xstats_reset,
+ .timesync_enable = idpf_timesync_enable,
+ .timesync_read_rx_timestamp = idpf_timesync_read_rx_timestamp,
+ .timesync_read_tx_timestamp = idpf_timesync_read_tx_timestamp,
+ .timesync_adjust_time = idpf_timesync_adjust_time,
+ .timesync_adjust_freq = idpf_timesync_adjust_freq,
+ .timesync_read_time = idpf_timesync_read_time,
+ .timesync_write_time = idpf_timesync_write_time,
+ .timesync_disable = idpf_timesync_disable,
};
static int
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v5 4/4] doc: add PTP IDPF documentation
2026-01-23 0:04 ` [PATCH v5 0/4] Enable PTP feature for MEV Soumyadeep Hore
` (3 preceding siblings ...)
2026-01-23 0:04 ` [PATCH v5 3/4] net/intel: add support for PTP Soumyadeep Hore
@ 2026-01-23 0:04 ` Soumyadeep Hore
4 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-01-23 0:04 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Updates the IDPF Documentation with PTP feature.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
doc/guides/nics/features/idpf.ini | 1 +
doc/guides/nics/idpf.rst | 15 +++++++++++++++
doc/guides/rel_notes/release_26_03.rst | 4 ++++
3 files changed, 20 insertions(+)
diff --git a/doc/guides/nics/features/idpf.ini b/doc/guides/nics/features/idpf.ini
index 63b799289a..f546648924 100644
--- a/doc/guides/nics/features/idpf.ini
+++ b/doc/guides/nics/features/idpf.ini
@@ -19,6 +19,7 @@ RSS reta update = Y
L3 checksum offload = P
L4 checksum offload = P
Timestamp offload = P
+Timesync = Y
Packet type parsing = Y
Basic stats = Y
Linux = Y
diff --git a/doc/guides/nics/idpf.rst b/doc/guides/nics/idpf.rst
index b99cc18188..1435cb78ca 100644
--- a/doc/guides/nics/idpf.rst
+++ b/doc/guides/nics/idpf.rst
@@ -109,3 +109,18 @@ The paths are chosen based on 2 conditions:
A value "P" means the offload feature is not supported by vector path.
If any not supported features are used, idpf vector PMD is disabled
and the scalar paths are chosen.
+
+Time Synchronization
+~~~~~~~~~~~~~~~~~~~~
+
+The system operator can run a PTP (Precision Time Protocol) client application
+to synchronize the time on the network card in ACC
+(and optionally the time on the system) to the PTP primary.
+
+IDPF PMD supports PTP client applications that use the DPDK IEEE 1588 API
+to communicate with the PTP primary clock.
+Note that PTP client application needs to run on PF in ACC.
+
+.. code-block:: console
+
+ examples/dpdk-ptpclient -l 0-3 -n 3 -a 0000:ec:00.1 -- -T 1 -p 0x1
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index 15dabee7a1..597147f789 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -55,6 +55,10 @@ New Features
Also, make sure to start the actual text at the margin.
=======================================================
+* **Updated Intel idpf ethernet driver.**
+
+ * Added support for Timesync features.
+
Removed Items
-------------
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* Re: [PATCH v5 1/4] net/idpf: add a new API for PTP support
2026-01-23 0:04 ` [PATCH v5 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
@ 2026-02-03 11:56 ` Bruce Richardson
0 siblings, 0 replies; 57+ messages in thread
From: Bruce Richardson @ 2026-02-03 11:56 UTC (permalink / raw)
To: Soumyadeep Hore
Cc: dev, rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao,
Milena Olech
Minor nit, subject prefix should be "net/idpf/base"
On Thu, Jan 22, 2026 at 07:04:46PM -0500, Soumyadeep Hore wrote:
> From: Milena Olech <milena.olech@intel.com>
>
> Add PTP capabilities to determine which PTP features are enabled -
> get/set the time, cross timestamp, adjust the clock and timestamping.
>
> Create appropriate structures and opcodes to support features introduced by
> capabilities.
>
> Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
> Signed-off-by: Milena Olech <milena.olech@intel.com>
> ---
> drivers/net/intel/idpf/base/virtchnl2.h | 324 +++++++++++++++++-------
> 1 file changed, 235 insertions(+), 89 deletions(-)
>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH v5 2/4] net/idpf: add PTP virtchnl2 support
2026-01-23 0:04 ` [PATCH v5 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
@ 2026-02-03 12:52 ` Bruce Richardson
2026-02-14 7:10 ` Hore, Soumyadeep
2026-02-14 19:49 ` [PATCH v6 0/4] Enable PTP feature for MEV Soumyadeep Hore
1 sibling, 1 reply; 57+ messages in thread
From: Bruce Richardson @ 2026-02-03 12:52 UTC (permalink / raw)
To: Soumyadeep Hore; +Cc: dev, rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
On Thu, Jan 22, 2026 at 07:04:47PM -0500, Soumyadeep Hore wrote:
> Add a new file - idpf_ptp - to handle PTP virtchnl messages.
> Keep the registers addresses in the PTP struct.
>
> Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
> ---
Some review feedback inline below. See also review feedback from AI review
that was posted by Stephen.
> drivers/net/intel/idpf/idpf_common_device.h | 4 +
> drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
> drivers/net/intel/idpf/idpf_ptp.c | 639 ++++++++++++++++++
> drivers/net/intel/idpf/idpf_ptp.h | 227 +++++++
> drivers/net/intel/idpf/meson.build | 1 +
> 5 files changed, 903 insertions(+), 2 deletions(-)
> create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
> create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
>
> diff --git a/drivers/net/intel/idpf/idpf_common_device.h b/drivers/net/intel/idpf/idpf_common_device.h
> index c32dcfbb12..3c84b96225 100644
> --- a/drivers/net/intel/idpf/idpf_common_device.h
> +++ b/drivers/net/intel/idpf/idpf_common_device.h
> @@ -90,6 +90,7 @@ struct idpf_adapter {
>
> /* For timestamp */
> uint64_t time_hw;
> + struct idpf_ptp *ptp;
>
> enum idpf_rx_func_type rx_func_type;
> };
> @@ -161,6 +162,9 @@ struct idpf_vport {
> /* Event from ipf */
> bool link_up;
> uint32_t link_speed;
> +
> + /* For PTP */
> + struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
> };
>
> /* Message type read in virtual channel from PF */
> diff --git a/drivers/net/intel/idpf/idpf_common_virtchnl.c b/drivers/net/intel/idpf/idpf_common_virtchnl.c
> index e927d7415a..a28bc9bee6 100644
> --- a/drivers/net/intel/idpf/idpf_common_virtchnl.c
> +++ b/drivers/net/intel/idpf/idpf_common_virtchnl.c
> @@ -4,6 +4,7 @@
>
> #include "idpf_common_virtchnl.h"
> #include "idpf_common_logs.h"
> +#include "idpf_ptp.h"
>
> #include <eal_export.h>
>
> @@ -38,6 +39,28 @@ idpf_vc_clean(struct idpf_adapter *adapter)
> return 0;
> }
>
> +/**
> + * idpf_ptp_is_mb_msg - Check if the message is PTP-related
> + * @op: virtchnl opcode
> + *
> + * Returns true if msg is PTP-related, false otherwise
> + */
> +static inline bool idpf_ptp_is_mb_msg(uint32_t op)
> +{
The name of this function seems backward to me. We are not returning if
it's a mailbox message, we are returning if it's PTP related, no? Therefore
would idpf_mb_msg_is_ptp, not be a better name?
> + switch (op) {
> + case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
> + 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:
> + case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> static int
> idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
> uint16_t msg_size, uint8_t *msg)
> @@ -71,8 +94,15 @@ idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
>
> memcpy(dma_mem->va, msg, msg_size);
>
> - ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
> - ctlq_msg->func_id = 0;
> + if (idpf_ptp_is_mb_msg(op) && adapter->ptp->secondary_mbx.valid) {
If the first of these is true and the second not, is that an error
condition and do we need to handle it?
> + 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 {
> + ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
> + ctlq_msg->func_id = 0;
> + }
> +
> ctlq_msg->data_len = msg_size;
> ctlq_msg->cookie.mbx.chnl_opcode = op;
> ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
> diff --git a/drivers/net/intel/idpf/idpf_ptp.c b/drivers/net/intel/idpf/idpf_ptp.c
> new file mode 100644
> index 0000000000..b8f6afa518
> --- /dev/null
> +++ b/drivers/net/intel/idpf/idpf_ptp.c
> @@ -0,0 +1,639 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2025 Intel Corporation
> + */
> +
> +#include "idpf_ptp.h"
> +#include <base/virtchnl2.h>
> +#include "idpf_common_virtchnl.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.
> + */
> +static 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 = (uint8_t)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 = (uint8_t)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_MB;
> + ptp->set_dev_clk_time_access = (uint8_t)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 = (uint8_t)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 = (uint8_t)idpf_ptp_get_access(adapter,
> + direct,
> + mailbox);
> +}
This function could be much shorter if you just removed the variables "direct"
and "mailbox". Rather than doing
direct = VIRTCHNL2_CAP_PTP_TX_TSTAMPS;
mailbox = VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB;
ptp->tx_tstamp_access = (uint8_t)idpf_ptp_get_access(adapter,
direct,
mailbox);
You can shorten the 5 lines down to 3 as:
ptp->tx_tstamp_access = idpf_ptp_get_access(adapter,
VIRTCHNL2_CAP_PTP_TX_TSTAMPS,
VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
Furthermore, since all the defines used for mailbox are the same as those
used for direct, except for an _MB suffix, you could use a macro to shorten
thngs further if you want - maybe even fit each assignment on one line.
For example (untested, and also shortening adapter to ad to fit one line):
#define IDPF_PTP_ACCESS(a, d) (uint8_t)idpf_ptp_get_access(a, d, d##_MB)
static void
idpf_ptp_get_features_access(const struct idpf_adapter *ad)
{
struct idpf_ptp *ptp = ad->ptp;
ptp->get_dev_clk_time_access = IDPF_PTP_ACCESS(ad, VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME);
ptp->get_cross_tstamp_access = IDPF_PTP_ACCESS(ad, VIRTCHNL2_CAP_PTP_GET_CROSS_TIME);
ptp->set_dev_clk_time_access = IDPF_PTP_ACCESS(ad, VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME);
ptp->adj_dev_clk_time_access = IDPF_PTP_ACCESS(ad, VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK);
ptp->tx_stamp_access = IDPF_PTP_ACCESS(ad, VIRTCHNL2_CAP_PTP_TX_TSTAMPS);
}
Much shorter! :-)
> +/**
> + * 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_cross_time_reg_offsets cross_tstamp_offsets;
> + struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
> + struct virtchnl2_ptp_get_caps send_ptp_caps_msg = { };
Using {} is a GNU extension to C11, so its best to use {0} as initializer.
> + struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
> + struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
> + struct idpf_cmd_info args = { };
> + struct idpf_ptp_secondary_mbx *scnd_mbx;
> + struct idpf_ptp *ptp = adapter->ptp;
> + struct idpf_hw *hw = &adapter->hw;
> + enum idpf_ptp_access access_type;
> + int err;
> + u32 temp_offset;
> +
> + 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 |
> + VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME |
> + VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
> + VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK |
> + VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
> + VIRTCHNL2_CAP_PTP_TX_TSTAMPS |
> + VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
> +
> + args.ops = VIRTCHNL2_OP_PTP_GET_CAPS;
> + args.in_args = (uint8_t *)&send_ptp_caps_msg;
> + args.in_args_size = sizeof(send_ptp_caps_msg);
> +
> + args.out_buffer = adapter->mbx_resp;
> + args.out_size = sizeof(*recv_ptp_caps_msg);
> +
> + err = idpf_vc_cmd_execute(adapter, &args);
> + if (err < 0)
> + return err;
> +
> + recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
> + 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);
> +
> + /* 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);
> +
> + access_type = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
> + if (access_type != IDPF_PTP_DIRECT)
> + goto cross_tstamp;
> +
> + 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_PCI_REG_ADDR(hw,
> + temp_offset);
No need to wrap these lines. Use the full 100 chars.
(Also, when wrapping, double tab indent is sufficient, no need to put in 7
tabs + spaces to try and align braces in cases like this)
> + temp_offset = LE32_TO_CPU(clock_offsets.dev_clk_ns_h);
> + ptp->dev_clk_regs.dev_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
> + temp_offset);
> + temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_l);
> + ptp->dev_clk_regs.phy_clk_ns_l = IDPF_PCI_REG_ADDR(hw,
> + temp_offset);
> + temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_h);
> + ptp->dev_clk_regs.phy_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
> + temp_offset);
> + temp_offset = LE32_TO_CPU(clock_offsets.cmd_sync_trigger);
> + ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
> +
> +cross_tstamp:
> + access_type = (enum idpf_ptp_access)ptp->get_cross_tstamp_access;
> + if (access_type != IDPF_PTP_DIRECT)
> + goto discipline_clock;
> +
> + 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_PCI_REG_ADDR(hw,
> + temp_offset);
Again, don't wrap. Also, this indentation used neither is a simple
double-indent, nor does it align to brace so please use one style
consistently.
> + temp_offset = LE32_TO_CPU(cross_tstamp_offsets.sys_time_ns_h);
> + ptp->dev_clk_regs.sys_time_ns_h = IDPF_PCI_REG_ADDR(hw,
> + temp_offset);
> + temp_offset = LE32_TO_CPU(cross_tstamp_offsets.cmd_sync_trigger);
> + ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
> +
> +discipline_clock:
> + access_type = (enum idpf_ptp_access)ptp->adj_dev_clk_time_access;
> + if (access_type != IDPF_PTP_DIRECT)
> + return err;
> +
> + 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_PCI_REG_ADDR(hw, temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_l);
> + ptp->dev_clk_regs.incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_h);
> + ptp->dev_clk_regs.incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_l);
> + ptp->dev_clk_regs.shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_h);
> + ptp->dev_clk_regs.shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
> +
> + /* PHY clock offsets */
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_cmd_type);
> + ptp->dev_clk_regs.phy_cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_l);
> + ptp->dev_clk_regs.phy_incval_l = IDPF_PCI_REG_ADDR(hw,
> + temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_h);
> + ptp->dev_clk_regs.phy_incval_h = IDPF_PCI_REG_ADDR(hw,
> + temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_l);
> + ptp->dev_clk_regs.phy_shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_h);
> + ptp->dev_clk_regs.phy_shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
> +
> + return err;
> +}
> +
> +/**
> + * 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)
> +{
> + uint32_t 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 */
> + IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, shtime_enable);
> + IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, exec_cmd | shtime_enable);
> +}
> +
> +/**
> + * 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_cmd_info args = { };
As above, use {0} as initializer.
> + int err;
> + u64 dev_time;
> +
> + args.ops = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME;
> + args.in_args = (uint8_t *)&get_dev_clk_time_msg;
> + args.in_args_size = sizeof(get_dev_clk_time_msg);
> + args.out_buffer = adapter->mbx_resp;
> + args.out_size = sizeof(get_dev_clk_time_msg);
> +
> + err = idpf_vc_cmd_execute(adapter, &args);
> + if (err < 0)
> + return err;
> +
> + get_dev_clk_time_msg = *(struct virtchnl2_ptp_get_dev_clk_time *)args.out_buffer;
> + dev_time = LE64_TO_CPU(get_dev_clk_time_msg.dev_time_ns);
> + dev_clk_time->dev_clk_time_ns = dev_time;
> +
> + return err;
> +}
> +
> +/**
> + * 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_cmd_info args = { };
> + int err;
> +
> + args.ops = VIRTCHNL2_OP_PTP_GET_CROSS_TIME;
> + args.in_args = (uint8_t *)&cross_time_msg;
> + args.in_args_size = sizeof(cross_time_msg);
> + args.out_buffer = adapter->mbx_resp;
> + args.out_size = sizeof(cross_time_msg);
> +
> + err = idpf_vc_cmd_execute(adapter, &args);
> + if (err < 0)
> + return err;
> +
> + cross_time_msg = *(struct virtchnl2_ptp_get_cross_time *)args.out_buffer;
> + 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 err;
> +}
> +
> +/**
> + * 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 = { };
> + struct idpf_cmd_info args = { };
> + int err;
> +
> + set_dev_clk_time_msg.dev_time_ns = CPU_TO_LE64(time);
> +
> + args.ops = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME;
> + args.in_args = (uint8_t *)&set_dev_clk_time_msg;
> + args.in_args_size = sizeof(set_dev_clk_time_msg);
> + args.out_buffer = adapter->mbx_resp;
> + args.out_size = sizeof(set_dev_clk_time_msg);
> +
> + err = idpf_vc_cmd_execute(adapter, &args);
> + if (err < 0)
> + return err;
> +
> + return err;
> +}
> +
> +/**
> + * 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, int64_t delta)
> +{
> + struct virtchnl2_ptp_adj_dev_clk_time adj_dev_clk_time_msg = { };
> + struct idpf_cmd_info args = { };
> + int err;
> +
> + adj_dev_clk_time_msg.delta = CPU_TO_LE64(delta);
> +
> + args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME;
> + args.in_args = (uint8_t *)&adj_dev_clk_time_msg;
> + args.in_args_size = sizeof(adj_dev_clk_time_msg);
> + args.out_buffer = adapter->mbx_resp;
> + args.out_size = sizeof(adj_dev_clk_time_msg);
> +
> + err = idpf_vc_cmd_execute(adapter, &args);
> + if (err < 0)
> + return err;
> +
> + return err;
> +}
> +
> +/**
> + * 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 = { };
> + struct idpf_cmd_info args = { };
> + int err;
> +
> + adj_dev_clk_fine_msg.incval = CPU_TO_LE64(incval);
> +
> + args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE;
> + args.in_args = (uint8_t *)&adj_dev_clk_fine_msg;
> + args.in_args_size = sizeof(adj_dev_clk_fine_msg);
> + args.out_buffer = adapter->mbx_resp;
> + args.out_size = sizeof(adj_dev_clk_fine_msg);
> +
> + err = idpf_vc_cmd_execute(adapter, &args);
> + if (err < 0)
> + return err;
> +
> + return err;
> +}
> +
> +/**
> + * 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 virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
> + enum idpf_ptp_access tstamp_access, get_dev_clk_access;
> + struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
> + struct idpf_ptp *ptp = vport->adapter->ptp;
> + struct idpf_cmd_info args = { };
> + int err;
> + u16 num_latches, i;
> + u32 size;
> +
> + if (ptp == NULL)
> + return -EOPNOTSUPP;
> +
> + tstamp_access = (enum idpf_ptp_access)ptp->tx_tstamp_access;
> + get_dev_clk_access = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
> + if (tstamp_access == IDPF_PTP_NONE ||
> + get_dev_clk_access == IDPF_PTP_NONE)
> + return -EOPNOTSUPP;
> +
> + send_tx_tstamp_caps.vport_id = CPU_TO_LE32(vport->vport_id);
> +
> + args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS;
> + args.in_args = (uint8_t *)&send_tx_tstamp_caps;
> + args.in_args_size = sizeof(send_tx_tstamp_caps);
> + args.out_size = IDPF_CTLQ_MAX_BUF_LEN;
> + args.out_buffer = vport->adapter->mbx_resp;
> +
> + err = idpf_vc_cmd_execute(vport->adapter, &args);
> + if (err < 0)
> + return err;
> +
> + rcv_tx_tstamp_caps = (struct virtchnl2_ptp_get_vport_tx_tstamp_caps *)
> + args.out_buffer;
> + num_latches = LE16_TO_CPU(rcv_tx_tstamp_caps->num_latches);
> + size = sizeof(struct idpf_ptp_vport_tx_tstamp_caps) +
> + sizeof(struct idpf_ptp_tx_tstamp) * num_latches;
> + tstamp_caps = rte_zmalloc(NULL, size, 0);
> + if (tstamp_caps == NULL)
> + return -ENOMEM;
> +
> + tstamp_caps->access = true;
> + tstamp_caps->num_entries = num_latches;
> +
> + tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
> +
> + for (i = 0; i < tstamp_caps->num_entries; i++) {
> + __le32 offset_l, offset_h;
> +
> + tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i];
> +
> + if (tstamp_access == IDPF_PTP_DIRECT) {
> + offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
> + offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
> + tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_l = LE32_TO_CPU(offset_l);
> + tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_h = LE32_TO_CPU(offset_h);
> + }
> + tstamp_caps->tx_tstamp[i].idx = tx_tstamp_latch_caps.index;
> + }
> +
> + tstamp_caps->latched_idx = -1;
> + vport->tx_tstamp_caps = tstamp_caps;
> +
> + return err;
> +}
> +
> +/**
> + * 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
> + * @tx_tstamp: Tx timestamp structure to be fulfilled with the timestamp value
> + *
> + * Read the value of the Tx timestamp for a given latch received from the
> + * Control Plane.
> + *
> + * 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 *tx_tstamp)
> +{
> + struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
> + u8 tstamp_ns_lo_bit;
> +
> + tx_tstamp_caps = vport->tx_tstamp_caps;
> + tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
> +
> + tx_tstamp->tstamp = LE64_TO_CPU(tstamp_latch->tstamp);
> + tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
> +
> + return 0;
> +}
> +
> +/**
> + * 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 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 *ptp_tx_tstamp;
> + struct idpf_cmd_info args = { };
> + int size, msg_size;
> + u32 vport_id;
> + u16 num_latches, id;
> + int err;
> +
> + tx_tstamp_caps = vport->tx_tstamp_caps;
> + ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
> +
> + size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
> + sizeof(struct virtchnl2_ptp_tx_tstamp_latch) *
> + tx_tstamp_caps->num_entries;
> + send_tx_tstamp_msg = rte_zmalloc(NULL, size, 0);
> + if (send_tx_tstamp_msg == NULL)
> + return -ENOMEM;
> +
> + for (id = 0; id < tx_tstamp_caps->num_entries; id++,
> + ptp_tx_tstamp++)
> + send_tx_tstamp_msg->tstamp_latches[id].index =
> + ptp_tx_tstamp->idx;
Identation in this loop badly needs to be reworked.
> + send_tx_tstamp_msg->get_devtime_with_txtstmp = 1;
> +
> + msg_size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
> + sizeof(struct virtchnl2_ptp_tx_tstamp_latch) * id;
> + send_tx_tstamp_msg->vport_id = CPU_TO_LE32(vport->vport_id);
> + send_tx_tstamp_msg->num_latches = CPU_TO_LE16(id);
> +
> + args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP;
> + args.in_args = (uint8_t *)send_tx_tstamp_msg;
> + args.in_args_size = msg_size;
> + args.out_size = msg_size;
> + args.out_buffer = vport->adapter->mbx_resp;
> +
> + err = idpf_vc_cmd_execute(vport->adapter, &args);
> + rte_free(send_tx_tstamp_msg);
> + if (err < 0)
> + return err;
> +
> + recv_tx_tstamp_msg = (struct virtchnl2_ptp_get_vport_tx_tstamp_latches *)
> + args.out_buffer;
> + vport_id = LE32_TO_CPU(recv_tx_tstamp_msg->vport_id);
> + if (vport->vport_id != vport_id)
> + return -EINVAL;
> +
> + num_latches = LE16_TO_CPU(recv_tx_tstamp_msg->num_latches);
> +
> + ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
> + for (id = 0; id < num_latches; id++, ptp_tx_tstamp++) {
> + tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[id];
> +
> + if (!tstamp_latch.valid)
> + continue;
> +
> + err = idpf_ptp_get_tstamp_value(vport, &tstamp_latch,
> + ptp_tx_tstamp);
> + if (err == 0) {
> + tx_tstamp_caps->latched_idx = id;
> + vport->adapter->time_hw = recv_tx_tstamp_msg->device_time;
> + }
> + break;
> + }
> + return err;
> +}
> +
> +/**
> + * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer value
> + * @adapter: Driver specific private structure
> + *
> + * Return: the device clock time.
> + */
> +static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter)
> +{
> + struct idpf_ptp *ptp = adapter->ptp;
> + u32 hi, lo;
> +
> + idpf_ptp_enable_shtime(adapter);
> +
> + lo = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_l);
> + hi = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_h);
> +
> + return ((u64)hi << 32) | lo;
> +}
> +
> +/**
> + * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value through mailbox
> + * @adapter: Driver specific private structure
> + * @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,
> + u64 *src_clk)
> +{
> + struct idpf_ptp_dev_timers clk_time;
> + int err;
> +
> + err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
> + if (err)
> + return err;
> +
> + *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
> + * @src_clk: Returned main timer value in nanoseconds unit
> + *
> + * Return: the device clock time on success, -errno otherwise.
> + */
> +int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk)
> +{
> + if (adapter->ptp == NULL)
> + return 0;
Should this not be an error, returning -EINVAL?
> + switch ((enum idpf_ptp_access)adapter->ptp->get_dev_clk_time_access) {
> + case IDPF_PTP_NONE:
> + return -EOPNOTSUPP;
Strictly speaking, you don't need this, it's handled by the default case.
> + case IDPF_PTP_MAILBOX:
> + return idpf_ptp_read_src_clk_reg_mailbox(adapter, src_clk);
> + case IDPF_PTP_DIRECT:
> + *src_clk = idpf_ptp_read_src_clk_reg_direct(adapter);
> + break;
Why have the function types for direct and mailbox different? Why not have
both just take src_clk as parameter and return 0 on success, so you can
have similar use of them. It would also possibly allow function pointer use
in future, if they are the same types.
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/net/intel/idpf/idpf_ptp.h b/drivers/net/intel/idpf/idpf_ptp.h
> new file mode 100644
> index 0000000000..68ac66ae91
> --- /dev/null
> +++ b/drivers/net/intel/idpf/idpf_ptp.h
> @@ -0,0 +1,227 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2025 Intel Corporation
> + */
> +
> +#ifndef _IDPF_PTP_H_
> +#define _IDPF_PTP_H_
> +
> +#include "idpf_osdep.h"
> +#include <rte_time.h>
> +#include "idpf_common_device.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 {
> + uint32_t exec_cmd_mask;
> + uint32_t 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
> + * @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
> + */
> +struct idpf_ptp_dev_clk_regs {
> + /* Main clock */
> + volatile uint32_t *dev_clk_ns_l;
> + volatile uint32_t *dev_clk_ns_h;
> +
> + /* PHY timer */
> + volatile uint32_t *phy_clk_ns_l;
> + volatile uint32_t *phy_clk_ns_h;
> +
> + /* System time */
> + volatile uint32_t *sys_time_ns_l;
> + volatile uint32_t *sys_time_ns_h;
> +
> + /* Main timer adjustments */
> + volatile uint32_t *incval_l;
> + volatile uint32_t *incval_h;
> + volatile uint32_t *shadj_l;
> + volatile uint32_t *shadj_h;
> +
> + /* PHY timer adjustments */
> + volatile uint32_t *phy_incval_l;
> + volatile uint32_t *phy_incval_h;
> + volatile uint32_t *phy_shadj_l;
> + volatile uint32_t *phy_shadj_h;
> +
> + /* Command */
> + volatile uint32_t *cmd;
> + volatile uint32_t *phy_cmd;
> + volatile uint32_t *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_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 {
> + uint16_t peer_mbx_q_id;
> + uint16_t peer_id;
> + 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 - 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
> + * @tstamp: the Tx tstamp value
> + * @idx: the index of the Tx tstamp
> + */
> +struct idpf_ptp_tx_tstamp {
> + uint64_t tstamp;
> + uint32_t tx_latch_reg_offset_l;
> + uint32_t tx_latch_reg_offset_h;
> + uint32_t 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
> + * @access: indicates an access to Tx timestamp
> + * @latches_index: the index of the latched Tx timestamps
> + * @tx_tstamp: array of Tx timestamp parameters
> + */
> +struct idpf_ptp_vport_tx_tstamp_caps {
> + uint32_t vport_id;
> + uint16_t num_entries;
> + uint16_t tstamp_ns_lo_bit;
> + uint16_t latched_idx;
> + bool access:1;
> + struct idpf_ptp_tx_tstamp tx_tstamp[];
> +};
> +
> +/**
> + * @struct idpf_ptp - PTP parameters
> + * @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
> + * @tx_tstamp_access: access type for the Tx timestamp value read
> + * @rsv: Reserved fields
> + * @secondary_mbx: parameters for using dedicated PTP mailbox
> + */
> +struct idpf_ptp {
> + uint64_t base_incval;
> + uint64_t max_adj;
> + struct idpf_ptp_cmd cmd;
> + struct idpf_ptp_dev_clk_regs dev_clk_regs;
> + uint32_t caps;
> + uint8_t get_dev_clk_time_access:2;
> + uint8_t get_cross_tstamp_access:2;
> + uint8_t set_dev_clk_time_access:2;
> + uint8_t adj_dev_clk_time_access:2;
> + uint8_t tx_tstamp_access:2;
> + uint8_t rsv:6;
> + struct idpf_ptp_secondary_mbx secondary_mbx;
> +};
> +
> +/**
> + * @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 {
> + uint64_t sys_time_ns;
> + uint64_t dev_clk_time_ns;
> +};
> +
> +int idpf_ptp_get_caps(struct idpf_adapter *adapter);
> +int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t *src_clk);
> +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, uint64_t time);
> +int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t incval);
> +int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, int64_t delta);
> +int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport);
> +int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
> +
> +/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
> +static inline uint64_t
> +idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
> + bool is_rx, uint32_t in_timestamp)
> +{
> + const uint64_t mask = 0xFFFFFFFF;
> + uint32_t phc_time_lo, delta;
> + uint64_t ns;
> +
> + if (flag != 0)
> + idpf_ptp_read_src_clk_reg(ad, &ad->time_hw);
> +
> + /* Extract the lower 32 bits of the PHC time */
> + phc_time_lo = (uint32_t)(ad->time_hw);
> +
> + /* Calculate the delta between the lower 32bits of the cached PHC
> + * time and the in_timestamp value.
> + */
> + delta = in_timestamp - phc_time_lo;
> +
> + if (delta > mask / 2) {
> + /* Reverse the delta calculation here */
> + delta = phc_time_lo - in_timestamp;
> + ns = ad->time_hw - delta;
> + } else {
> + if (is_rx)
> + ns = ad->time_hw - delta;
> + else
> + ns = ad->time_hw + delta;
> + }
> +
> + return ns;
> +}
> +#endif /* _IDPF_PTP_H_ */
> diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
> index a805d02ea2..5a4a3c2259 100644
> --- a/drivers/net/intel/idpf/meson.build
> +++ b/drivers/net/intel/idpf/meson.build
> @@ -18,6 +18,7 @@ sources += files(
>
> 'idpf_ethdev.c',
> 'idpf_rxtx.c',
> + 'idpf_ptp.c',
All lists in DPDK should be in alphabetical order, unless there is a reason
they can't be. Therefore, this new C file should be above idpf_rxtx.c in
the list.
> )
>
> if arch_subdir == 'x86' and dpdk_conf.get('RTE_IOVA_IN_MBUF') == 1
> --
> 2.47.1
>
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH v5 3/4] net/intel: add support for PTP
2026-01-23 0:04 ` [PATCH v5 3/4] net/intel: add support for PTP Soumyadeep Hore
@ 2026-02-03 15:37 ` Bruce Richardson
0 siblings, 0 replies; 57+ messages in thread
From: Bruce Richardson @ 2026-02-03 15:37 UTC (permalink / raw)
To: Soumyadeep Hore; +Cc: dev, rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
I know that checkpatch will complain that this affects common intel files
and suggest that "net/intel" is the prefix to use, but in this case, it's a
one-line change to common headers, everything else is in idpf so I think
net/idpf is a better reflection on the patch contents.
On Thu, Jan 22, 2026 at 07:04:48PM -0500, Soumyadeep Hore wrote:
> Add support for PTP that contains negotiation with CP to receive a
> set of supported functionalities through capabilities,
Consider rewording please - it's not PTP that contains the negotiation, for
example - it's the patch! :-)
Some other comments inline below.
/Bruce
> get/set/adjust the time of the main timer and Tx timestamping
> through secondary mailbox dedicated to PTP.
>
> Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
> ---
> drivers/net/intel/common/tx.h | 1 +
> drivers/net/intel/idpf/idpf_common_rxtx.c | 186 +++++++++------
> drivers/net/intel/idpf/idpf_common_rxtx.h | 10 +
> drivers/net/intel/idpf/idpf_ethdev.c | 270 ++++++++++++++++++++++
> 4 files changed, 396 insertions(+), 71 deletions(-)
>
> diff --git a/drivers/net/intel/common/tx.h b/drivers/net/intel/common/tx.h
> index 5af64a4cfe..617e184976 100644
> --- a/drivers/net/intel/common/tx.h
> +++ b/drivers/net/intel/common/tx.h
> @@ -109,6 +109,7 @@ struct ci_tx_queue {
> struct ci_tx_queue *complq;
> void **txqs; /*only valid for split queue mode*/
> uint32_t tx_start_qid;
> + uint32_t latch_idx; /* Tx timestamp latch index */
> uint16_t sw_nb_desc;
> uint16_t sw_tail;
> uint16_t rs_compl_count;
> diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.c b/drivers/net/intel/idpf/idpf_common_rxtx.c
> index a5d0795057..c8cd5b9d5b 100644
> --- a/drivers/net/intel/idpf/idpf_common_rxtx.c
> +++ b/drivers/net/intel/idpf/idpf_common_rxtx.c
> @@ -9,6 +9,7 @@
> #include "idpf_common_rxtx.h"
> #include "idpf_common_device.h"
> #include "../common/rx.h"
> +#include "idpf_ptp.h"
>
> int idpf_timestamp_dynfield_offset = -1;
> uint64_t idpf_timestamp_dynflag;
> @@ -435,58 +436,6 @@ idpf_qc_split_rxq_mbufs_alloc(struct idpf_rx_queue *rxq)
> return 0;
> }
>
> -#define IDPF_TIMESYNC_REG_WRAP_GUARD_BAND 10000
> -/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
> -static inline uint64_t
> -idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
> - uint32_t in_timestamp)
> -{
> -#ifdef RTE_ARCH_X86_64
> - struct idpf_hw *hw = &ad->hw;
> - const uint64_t mask = 0xFFFFFFFF;
> - uint32_t hi, lo, lo2, delta;
> - uint64_t ns;
> -
> - if (flag != 0) {
> - IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
> - IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_EXEC_CMD_M |
> - PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
> - lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
> - hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
> - /*
> - * On typical system, the delta between lo and lo2 is ~1000ns,
> - * so 10000 seems a large-enough but not overly-big guard band.
> - */
> - if (lo > (UINT32_MAX - IDPF_TIMESYNC_REG_WRAP_GUARD_BAND))
> - lo2 = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
> - else
> - lo2 = lo;
> -
> - if (lo2 < lo) {
> - lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
> - hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
> - }
> -
> - ad->time_hw = ((uint64_t)hi << 32) | lo;
> - }
> -
> - delta = (in_timestamp - (uint32_t)(ad->time_hw & mask));
> - if (delta > (mask / 2)) {
> - delta = ((uint32_t)(ad->time_hw & mask) - in_timestamp);
> - ns = ad->time_hw - delta;
> - } else {
> - ns = ad->time_hw + delta;
> - }
> -
> - return ns;
> -#else /* !RTE_ARCH_X86_64 */
> - RTE_SET_USED(ad);
> - RTE_SET_USED(flag);
> - RTE_SET_USED(in_timestamp);
> - return 0;
> -#endif /* RTE_ARCH_X86_64 */
> -}
> -
> #define IDPF_RX_FLEX_DESC_ADV_STATUS0_XSUM_S \
> (RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S) | \
> RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S) | \
> @@ -655,8 +604,12 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
> rx_desc_ring = rxq->rx_ring;
> ptype_tbl = rxq->adapter->ptype_tbl;
>
> - if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
> - rxq->hw_register_set = 1;
> + if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
> + uint64_t sw_cur_time = rte_get_timer_cycles() /
> + (rte_get_timer_hz() / 1000);
Reading the timer can be a slow operation. Therefore I suggest that you
also check if hw_register_set == 1 as part of the condition, and skip the
read in that case as unnecessary. Not sure if there are other global
conditions that also can be checked here too, e.g. the
idpf_timestamp_dynflag?
> + if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
> + rxq->hw_register_set = 1;
> + }
>
> while (nb_rx < nb_pkts) {
> rx_desc = &rx_desc_ring[rx_id];
> @@ -732,20 +685,38 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
> ptype_tbl[(rte_le_to_cpu_16(rx_desc->ptype_err_fflags0) &
> VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M) >>
> VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_S];
> +
> + if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
> + rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
> +
> status_err0_qw1 = rte_le_to_cpu_16(rx_desc->status_err0_qw1);
> pkt_flags = idpf_splitq_rx_csum_offload(status_err0_qw1);
> pkt_flags |= idpf_splitq_rx_rss_offload(rxm, rx_desc);
> if (idpf_timestamp_dynflag > 0 &&
> (rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP)) {
> /* timestamp */
> - ts_ns = idpf_tstamp_convert_32b_64b(ad,
> - rxq->hw_register_set,
> - rte_le_to_cpu_32(rx_desc->ts_high));
> - rxq->hw_register_set = 0;
> + rxq->time_high = rte_le_to_cpu_32(rx_desc->ts_high);
> + if (unlikely(rxq->hw_register_set)) {
> + ts_ns = idpf_tstamp_convert_32b_64b(ad,
> + rxq->hw_register_set, true,
> + rxq->time_high);
> + rxq->hw_time_low = (uint32_t)ts_ns;
> + rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
> + rxq->hw_register_set = 0;
> + } else {
> + if (rxq->time_high < rxq->hw_time_low)
> + rxq->hw_time_high += 1;
> + ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
> + rxq->hw_time_low = rxq->time_high;
> + }
> + rxq->hw_time_update = rte_get_timer_cycles() /
> + (rte_get_timer_hz() / 1000);
> *RTE_MBUF_DYNFIELD(rxm,
> idpf_timestamp_dynfield_offset,
> rte_mbuf_timestamp_t *) = ts_ns;
> first_seg->ol_flags |= idpf_timestamp_dynflag;
> + if (rx_desc->ts_low & VIRTCHNL2_RX_FLEX_TSTAMP_VALID)
> + rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
> }
>
> first_seg->ol_flags |= pkt_flags;
> @@ -839,10 +810,34 @@ idpf_split_tx_free(struct ci_tx_queue *cq)
> static inline uint16_t
> idpf_calc_context_desc(uint64_t flags)
> {
> - if ((flags & RTE_MBUF_F_TX_TCP_SEG) != 0)
> - return 1;
> + static uint64_t mask = RTE_MBUF_F_TX_TCP_SEG |
> + RTE_MBUF_F_TX_IEEE1588_TMST;
>
> - return 0;
> + return (flags & mask) ? 1 : 0;
> +}
> +
> +/**
> + * 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 inline void
> +idpf_tx_set_tstamp_desc(volatile union idpf_flex_tx_ctx_desc *ctx_desc,
> + uint32_t idx)
> +{
> + ctx_desc->tsyn.qw1.cmd_dtype =
> + rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_CMD_M,
> + IDPF_TX_CTX_DESC_TSYN));
> + ctx_desc->tsyn.qw1.cmd_dtype |=
> + rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_DTYPE_M,
> + IDPF_TX_DESC_DTYPE_CTX));
> + ctx_desc->tsyn.qw1.tsyn_reg_l =
> + rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_L_M,
> + idx));
> + ctx_desc->tsyn.qw1.tsyn_reg_h =
> + rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_H_M,
> + idx >> 2));
> }
>
> /* set TSO context descriptor
> @@ -948,6 +943,9 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
> idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
> ctx_desc);
>
> + if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
> + idpf_tx_set_tstamp_desc(ctx_desc, txq->latch_idx);
> +
> tx_id++;
> if (tx_id == txq->nb_tx_desc)
> tx_id = 0;
> @@ -1104,8 +1102,12 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
> rx_ring = rxq->rx_ring;
> ptype_tbl = rxq->adapter->ptype_tbl;
>
> - if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
> - rxq->hw_register_set = 1;
> + if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
> + uint64_t sw_cur_time = rte_get_timer_cycles() /
> + (rte_get_timer_hz() / 1000);
> + if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
> + rxq->hw_register_set = 1;
> + }
>
> while (nb_rx < nb_pkts) {
> rxdp = &rx_ring[rx_id];
> @@ -1167,17 +1169,33 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
>
> rxm->ol_flags |= pkt_flags;
>
> + if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
> + rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
> +
> if (idpf_timestamp_dynflag > 0 &&
> (rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
> /* timestamp */
> - ts_ns = idpf_tstamp_convert_32b_64b(ad,
> - rxq->hw_register_set,
> - rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
> - rxq->hw_register_set = 0;
> + rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
> + if (unlikely(rxq->hw_register_set)) {
> + ts_ns = idpf_tstamp_convert_32b_64b(ad,
> + rxq->hw_register_set, true,
> + rxq->time_high);
> + rxq->hw_time_low = (uint32_t)ts_ns;
> + rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
> + rxq->hw_register_set = 0;
> + } else {
> + if (rxq->time_high < rxq->hw_time_low)
> + rxq->hw_time_high += 1;
> + ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
> + rxq->hw_time_low = rxq->time_high;
> + }
> + rxq->hw_time_update = rte_get_timer_cycles() /
> + (rte_get_timer_hz() / 1000);
> *RTE_MBUF_DYNFIELD(rxm,
> idpf_timestamp_dynfield_offset,
> rte_mbuf_timestamp_t *) = ts_ns;
> rxm->ol_flags |= idpf_timestamp_dynflag;
> + rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
> }
>
> rx_pkts[nb_rx++] = rxm;
> @@ -1218,6 +1236,13 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
> if (unlikely(!rxq) || unlikely(!rxq->q_started))
> return nb_rx;
>
> + if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
> + uint64_t sw_cur_time = rte_get_timer_cycles() /
> + (rte_get_timer_hz() / 1000);
> + if (unlikely(sw_cur_time - rxq->hw_time_update > 4))
> + rxq->hw_register_set = 1;
> + }
> +
> while (nb_rx < nb_pkts) {
> rxdp = &rx_ring[rx_id];
> rx_status0 = rte_le_to_cpu_16(rxdp->flex_nic_wb.status_error0);
> @@ -1298,17 +1323,33 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
> ptype_tbl[(uint8_t)(rte_cpu_to_le_16(rxd.flex_nic_wb.ptype_flex_flags0) &
> VIRTCHNL2_RX_FLEX_DESC_PTYPE_M)];
>
> + if ((first_seg->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
> + first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
> +
> if (idpf_timestamp_dynflag > 0 &&
> (rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
> /* timestamp */
> - ts_ns = idpf_tstamp_convert_32b_64b(ad,
> - rxq->hw_register_set,
> - rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
> - rxq->hw_register_set = 0;
> + rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
> + if (unlikely(rxq->hw_register_set)) {
> + ts_ns = idpf_tstamp_convert_32b_64b(ad,
> + rxq->hw_register_set, true,
> + rxq->time_high);
> + rxq->hw_time_low = (uint32_t)ts_ns;
> + rxq->hw_time_high = (uint32_t)(ts_ns >> 32);
> + rxq->hw_register_set = 0;
> + } else {
> + if (rxq->time_high < rxq->hw_time_low)
> + rxq->hw_time_high += 1;
> + ts_ns = (uint64_t)rxq->hw_time_high << 32 | rxq->time_high;
> + rxq->hw_time_low = rxq->time_high;
> + }
> + rxq->hw_time_update = rte_get_timer_cycles() /
> + (rte_get_timer_hz() / 1000);
Is this line in the correct place? Should it not be the block where the
hw_register_set = 0 assignment is?
Is it documented in the code comments somewhere how this scheme for
updating timestamps and converting 32bit to 64bit values works?
> *RTE_MBUF_DYNFIELD(rxm,
> idpf_timestamp_dynfield_offset,
> rte_mbuf_timestamp_t *) = ts_ns;
> first_seg->ol_flags |= idpf_timestamp_dynflag;
> + first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
> }
>
> first_seg->ol_flags |= pkt_flags;
> @@ -1474,6 +1515,9 @@ idpf_dp_singleq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
> idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
> ctx_txd);
>
> + if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
> + idpf_tx_set_tstamp_desc(ctx_txd, txq->latch_idx);
> +
> txe->last_id = tx_last;
> tx_id = txe->next_id;
> txe = txn;
> diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.h b/drivers/net/intel/idpf/idpf_common_rxtx.h
> index 3bc3323af4..6098d336a4 100644
> --- a/drivers/net/intel/idpf/idpf_common_rxtx.h
> +++ b/drivers/net/intel/idpf/idpf_common_rxtx.h
> @@ -13,6 +13,12 @@
> #include "../common/tx.h"
> #include "../common/rx.h"
>
> +#define FIELD_PREP(_mask, _val) \
> + (__extension__ ({ \
> + typeof(_mask) _x = (_mask); \
> + ((typeof(_x))(_val) << rte_bsf32(_x)) & (_x); \
> + }))
> +
> #define IDPF_RX_MAX_BURST 32
>
> #define IDPF_RX_OFFLOAD_IPV4_CKSUM RTE_BIT64(1)
> @@ -155,7 +161,11 @@ struct idpf_rx_queue {
> struct idpf_rx_queue *bufq2;
>
> uint64_t offloads;
> + uint64_t hw_time_update; /* Last time HW timestamp was updated */
> uint32_t hw_register_set;
> + uint32_t time_high; /* high 32 bits of hardware timestamp register */
> + uint32_t hw_time_high; /* high 32 bits of timestamp */
> + uint32_t hw_time_low; /* low 32 bits of timestamp */
> };
>
> /* Offload features */
> diff --git a/drivers/net/intel/idpf/idpf_ethdev.c b/drivers/net/intel/idpf/idpf_ethdev.c
> index 5d786fbba6..5e57a45775 100644
> --- a/drivers/net/intel/idpf/idpf_ethdev.c
> +++ b/drivers/net/intel/idpf/idpf_ethdev.c
> @@ -14,6 +14,7 @@
> #include "idpf_ethdev.h"
> #include "idpf_rxtx.h"
> #include "../common/tx.h"
> +#include "idpf_ptp.h"
>
> #define IDPF_TX_SINGLE_Q "tx_single"
> #define IDPF_RX_SINGLE_Q "rx_single"
> @@ -841,6 +842,267 @@ idpf_dev_close(struct rte_eth_dev *dev)
> return 0;
> }
>
> +static int
> +idpf_timesync_enable(struct rte_eth_dev *dev)
> +{
> + struct idpf_vport *vport = dev->data->dev_private;
> + struct idpf_adapter *adapter = vport->adapter;
> + struct timespec sys_ts;
> + uint64_t ns;
> + int ret, q_id;
> + if (dev->data->dev_started && !(dev->data->dev_conf.rxmode.offloads &
> + RTE_ETH_RX_OFFLOAD_TIMESTAMP)) {
> + PMD_DRV_LOG(ERR, "Rx timestamp offload not configured");
> + return -1;
> + }
> +
> + adapter->ptp = rte_zmalloc(NULL, sizeof(struct idpf_ptp), 0);
> + if (adapter->ptp == NULL) {
> + PMD_DRV_LOG(ERR, "Failed to allocate memory for PTP");
> + return -ENOMEM;
> + }
> +
> + ret = idpf_ptp_get_caps(adapter);
> + if (ret) {
> + PMD_DRV_LOG(ERR, "Failed to get PTP capabilities, err=%d", ret);
> + goto fail_ptp;
> + }
> +
> + /* Write the default increment time value if the clock adjustments are enabled. */
> + if (adapter->ptp->adj_dev_clk_time_access != IDPF_PTP_NONE) {
> + ret = idpf_ptp_adj_dev_clk_fine(adapter, adapter->ptp->base_incval);
> + if (ret) {
> + PMD_DRV_LOG(ERR, "PTP set incval failed, err=%d", ret);
> + goto fail_ptp;
> + }
> + }
> +
> + /* Do not initialize the PTP if the device clock time cannot be read. */
> + if (adapter->ptp->get_dev_clk_time_access == IDPF_PTP_NONE) {
> + PMD_DRV_LOG(ERR, "Getting device clock time is not supported");
> + ret = -EIO;
> + goto fail_ptp;
> + }
> +
> + /* Set the device clock time to system time. */
> + if (adapter->ptp->set_dev_clk_time_access != IDPF_PTP_NONE) {
> + clock_gettime(CLOCK_REALTIME, &sys_ts);
> + ns = rte_timespec_to_ns(&sys_ts);
> + ret = idpf_ptp_set_dev_clk_time(adapter, ns);
> + if (ret) {
> + PMD_DRV_LOG(ERR, "PTP set clock time failed, err=%d", ret);
> + goto fail_ptp;
> + }
> + }
> +
> + ret = idpf_ptp_get_vport_tstamps_caps(vport);
> + if (ret) {
> + PMD_DRV_LOG(ERR, "Failed to get vport timestamp capabilities, err=%d", ret);
> + goto fail_ptp;
> + }
> +
> + for (q_id = 0; q_id < dev->data->nb_tx_queues; q_id++) {
> + struct ci_tx_queue *txq = dev->data->tx_queues[q_id];
> + txq->latch_idx = vport->tx_tstamp_caps->tx_tstamp[q_id].idx;
> + }
> +
> + 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;
> +
> + return 0;
> +
> +fail_ptp:
> + rte_free(adapter->ptp);
> + adapter->ptp = NULL;
> + return ret;
> +}
> +
> +static int
> +idpf_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
> + struct timespec *timestamp,
> + uint32_t flags)
> +{
> + struct idpf_rx_queue *rxq;
> + struct idpf_vport *vport = dev->data->dev_private;
> + struct idpf_adapter *adapter = vport->adapter;
> + uint64_t ts_ns;
> +
> + rxq = dev->data->rx_queues[flags];
> +
> + ts_ns = idpf_tstamp_convert_32b_64b(adapter, 1, true, rxq->time_high);
> + *timestamp = rte_ns_to_timespec(ts_ns);
> +
> + return 0;
> +}
> +
> +static int
> +idpf_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
> + struct timespec *timestamp)
> +{
> + struct idpf_vport *vport = dev->data->dev_private;
> + uint16_t latch_idx;
> + uint64_t ts_ns, tstamp;
> + int ret;
> +
> + ret = idpf_ptp_get_tx_tstamp(vport);
> + if (ret) {
> + PMD_DRV_LOG(ERR, "Failed to get TX timestamp");
> + return ret;
> + }
> +
> + latch_idx = vport->tx_tstamp_caps->latched_idx;
> + tstamp = vport->tx_tstamp_caps->tx_tstamp[latch_idx].tstamp;
> + ts_ns = idpf_tstamp_convert_32b_64b(vport->adapter, 0, false, tstamp);
> +
> + /* Convert to timespec */
> + *timestamp = rte_ns_to_timespec(ts_ns);
> +
> + vport->tx_tstamp_caps->latched_idx = -1;
> +
> + return 0;
> +}
> +
> +static int
> +idpf_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
> +{
> + struct idpf_vport *vport = dev->data->dev_private;
> + struct idpf_adapter *adapter = vport->adapter;
> + struct idpf_ptp *ptp = adapter->ptp;
> + uint64_t time, ns;
> + int ret;
> +
> + if (ptp->adj_dev_clk_time_access != IDPF_PTP_MAILBOX) {
> + PMD_DRV_LOG(ERR, "Adjusting device clock time is not supported");
> + return -ENOTSUP;
> + }
> +
> + if (delta > INT32_MAX || delta < INT32_MIN) {
> + ret = idpf_ptp_read_src_clk_reg(adapter, &time);
> + if (ret) {
> + PMD_DRV_LOG(ERR, "PTP read clock time failed, err %d", ret);
> + return ret;
> + }
> +
> + ns = time + delta;
> +
> + ret = idpf_ptp_set_dev_clk_time(adapter, ns);
> + if (ret)
> + PMD_DRV_LOG(ERR, "PTP set clock time failed, err %d", ret);
> +
> + return ret;
> + }
> +
> + ret = idpf_ptp_adj_dev_clk_time(adapter, delta);
> + if (ret)
> + PMD_DRV_LOG(ERR, "PTP adjusting clock failed, err %d", ret);
> +
> + return ret;
> +}
> +
> +static int
> +idpf_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
> +{
> + struct idpf_vport *vport = dev->data->dev_private;
> + struct idpf_adapter *adapter = vport->adapter;
> + struct idpf_ptp *ptp = adapter->ptp;
> + int64_t incval, diff = 0;
> + bool negative = false;
> + uint64_t div, rem;
> + uint64_t divisor = 1000000ULL << 16;
> + int shift;
> + int ret;
> +
> + incval = ptp->base_incval;
> +
> + if (ppm < 0) {
> + negative = true;
> + ppm = -ppm;
> + }
> +
> + /* can incval * ppm overflow ? */
> + if (rte_log2_u64(incval) + rte_log2_u64(ppm) > 62) {
> + rem = ppm % divisor;
> + div = ppm / divisor;
> + diff = div * incval;
> + ppm = rem;
> +
> + shift = rte_log2_u64(incval) + rte_log2_u64(ppm) - 62;
> + if (shift > 0) {
> + /* drop precision */
> + ppm >>= shift;
> + divisor >>= shift;
> + }
> + }
> +
> + if (divisor)
> + diff = diff + incval * ppm / divisor;
> +
> + if (negative)
> + incval -= diff;
> + else
> + incval += diff;
> +
> + ret = idpf_ptp_adj_dev_clk_fine(adapter, incval);
> + if (ret) {
> + PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
> + return ret;
> + }
> + return ret;
> +}
> +
> +static int
> +idpf_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
> +{
> + struct idpf_vport *vport = dev->data->dev_private;
> + struct idpf_adapter *adapter = vport->adapter;
> + uint64_t ns;
> + int ret;
> +
> + ns = rte_timespec_to_ns(ts);
> + ret = idpf_ptp_set_dev_clk_time(adapter, ns);
> + if (ret)
> + PMD_DRV_LOG(ERR, "PTP write time failed, err %d", ret);
> +
> + return ret;
> +}
> +
> +static int
> +idpf_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
> +{
> + struct idpf_vport *vport = dev->data->dev_private;
> + struct idpf_adapter *adapter = vport->adapter;
> + uint64_t time;
> + int ret;
> +
> + ret = idpf_ptp_read_src_clk_reg(adapter, &time);
> + if (ret)
> + PMD_DRV_LOG(ERR, "PTP read time failed, err %d", ret);
> + else
> + *ts = rte_ns_to_timespec(time);
> +
> + return ret;
> +}
> +
> +static int
> +idpf_timesync_disable(struct rte_eth_dev *dev)
> +{
> + struct idpf_vport *vport = dev->data->dev_private;
> + struct idpf_adapter *adapter = vport->adapter;
> +
> + if (vport->tx_tstamp_caps) {
> + rte_free(vport->tx_tstamp_caps);
> + vport->tx_tstamp_caps = NULL;
> + }
> +
> + if (adapter->ptp) {
> + rte_free(adapter->ptp);
> + adapter->ptp = NULL;
> + }
> +
> + return 0;
> +}
> +
> +
> static const struct eth_dev_ops idpf_eth_dev_ops = {
> .dev_configure = idpf_dev_configure,
> .dev_close = idpf_dev_close,
> @@ -867,6 +1129,14 @@ static const struct eth_dev_ops idpf_eth_dev_ops = {
> .xstats_get = idpf_dev_xstats_get,
> .xstats_get_names = idpf_dev_xstats_get_names,
> .xstats_reset = idpf_dev_xstats_reset,
> + .timesync_enable = idpf_timesync_enable,
> + .timesync_read_rx_timestamp = idpf_timesync_read_rx_timestamp,
> + .timesync_read_tx_timestamp = idpf_timesync_read_tx_timestamp,
> + .timesync_adjust_time = idpf_timesync_adjust_time,
> + .timesync_adjust_freq = idpf_timesync_adjust_freq,
> + .timesync_read_time = idpf_timesync_read_time,
> + .timesync_write_time = idpf_timesync_write_time,
> + .timesync_disable = idpf_timesync_disable,
> };
>
> static int
> --
> 2.47.1
>
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH v5 2/4] net/idpf: add PTP virtchnl2 support
2026-02-03 12:52 ` Bruce Richardson
@ 2026-02-14 7:10 ` Hore, Soumyadeep
0 siblings, 0 replies; 57+ messages in thread
From: Hore, Soumyadeep @ 2026-02-14 7:10 UTC (permalink / raw)
To: Richardson, Bruce
Cc: dev@dpdk.org, Kumar, Rajesh3, Singh, Aman Deep,
Subbarao, Manoj Kumar
[-- Attachment #1: Type: text/plain, Size: 42296 bytes --]
On Thu, Jan 22, 2026 at 07:04:47PM -0500, Soumyadeep Hore wrote:
> Add a new file - idpf_ptp - to handle PTP virtchnl messages.
> Keep the registers addresses in the PTP struct.
>
> Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
> ---
Some review feedback inline below. See also review feedback from AI review
that was posted by Stephen.
> drivers/net/intel/idpf/idpf_common_device.h | 4 +
> drivers/net/intel/idpf/idpf_common_virtchnl.c | 34 +-
> drivers/net/intel/idpf/idpf_ptp.c | 639 ++++++++++++++++++
> drivers/net/intel/idpf/idpf_ptp.h | 227 +++++++
> drivers/net/intel/idpf/meson.build | 1 +
> 5 files changed, 903 insertions(+), 2 deletions(-)
> create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
> create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
>
> diff --git a/drivers/net/intel/idpf/idpf_common_device.h b/drivers/net/intel/idpf/idpf_common_device.h
> index c32dcfbb12..3c84b96225 100644
> --- a/drivers/net/intel/idpf/idpf_common_device.h
> +++ b/drivers/net/intel/idpf/idpf_common_device.h
> @@ -90,6 +90,7 @@ struct idpf_adapter {
>
> /* For timestamp */
> uint64_t time_hw;
> + struct idpf_ptp *ptp;
>
> enum idpf_rx_func_type rx_func_type;
> };
> @@ -161,6 +162,9 @@ struct idpf_vport {
> /* Event from ipf */
> bool link_up;
> uint32_t link_speed;
> +
> + /* For PTP */
> + struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
> };
>
> /* Message type read in virtual channel from PF */
> diff --git a/drivers/net/intel/idpf/idpf_common_virtchnl.c b/drivers/net/intel/idpf/idpf_common_virtchnl.c
> index e927d7415a..a28bc9bee6 100644
> --- a/drivers/net/intel/idpf/idpf_common_virtchnl.c
> +++ b/drivers/net/intel/idpf/idpf_common_virtchnl.c
> @@ -4,6 +4,7 @@
>
> #include "idpf_common_virtchnl.h"
> #include "idpf_common_logs.h"
> +#include "idpf_ptp.h"
>
> #include <eal_export.h>
>
> @@ -38,6 +39,28 @@ idpf_vc_clean(struct idpf_adapter *adapter)
> return 0;
> }
>
> +/**
> + * idpf_ptp_is_mb_msg - Check if the message is PTP-related
> + * @op: virtchnl opcode
> + *
> + * Returns true if msg is PTP-related, false otherwise
> + */
> +static inline bool idpf_ptp_is_mb_msg(uint32_t op)
> +{
The name of this function seems backward to me. We are not returning if
it's a mailbox message, we are returning if it's PTP related, no? Therefore
would idpf_mb_msg_is_ptp, not be a better name?
> + switch (op) {
> + case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
> + 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:
> + case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> static int
> idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
> uint16_t msg_size, uint8_t *msg)
> @@ -71,8 +94,15 @@ idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
>
> memcpy(dma_mem->va, msg, msg_size);
>
> - ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
> - ctlq_msg->func_id = 0;
> + if (idpf_ptp_is_mb_msg(op) && adapter->ptp->secondary_mbx.valid) {
If the first of these is true and the second not, is that an error
condition and do we need to handle it?
No by default it should use primary mailbox for error conditions. Actually those are not error conditions but various cases where primary mail box is used for negotiation.
> + 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 {
> + ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
> + ctlq_msg->func_id = 0;
> + }
> +
> ctlq_msg->data_len = msg_size;
> ctlq_msg->cookie.mbx.chnl_opcode = op;
> ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
> diff --git a/drivers/net/intel/idpf/idpf_ptp.c b/drivers/net/intel/idpf/idpf_ptp.c
> new file mode 100644
> index 0000000000..b8f6afa518
> --- /dev/null
> +++ b/drivers/net/intel/idpf/idpf_ptp.c
> @@ -0,0 +1,639 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2025 Intel Corporation
> + */
> +
> +#include "idpf_ptp.h"
> +#include <base/virtchnl2.h>
> +#include "idpf_common_virtchnl.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.
> + */
> +static 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 = (uint8_t)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 = (uint8_t)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_MB;
> + ptp->set_dev_clk_time_access = (uint8_t)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 = (uint8_t)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 = (uint8_t)idpf_ptp_get_access(adapter,
> + direct,
> + mailbox);
> +}
This function could be much shorter if you just removed the variables "direct"
and "mailbox". Rather than doing
direct = VIRTCHNL2_CAP_PTP_TX_TSTAMPS;
mailbox = VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB;
ptp->tx_tstamp_access = (uint8_t)idpf_ptp_get_access(adapter,
direct,
mailbox);
You can shorten the 5 lines down to 3 as:
ptp->tx_tstamp_access = idpf_ptp_get_access(adapter,
VIRTCHNL2_CAP_PTP_TX_TSTAMPS,
VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
Furthermore, since all the defines used for mailbox are the same as those
used for direct, except for an _MB suffix, you could use a macro to shorten
thngs further if you want - maybe even fit each assignment on one line.
For example (untested, and also shortening adapter to ad to fit one line):
#define IDPF_PTP_ACCESS(a, d) (uint8_t)idpf_ptp_get_access(a, d, d##_MB)
static void
idpf_ptp_get_features_access(const struct idpf_adapter *ad)
{
struct idpf_ptp *ptp = ad->ptp;
ptp->get_dev_clk_time_access = IDPF_PTP_ACCESS(ad, VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME);
ptp->get_cross_tstamp_access = IDPF_PTP_ACCESS(ad, VIRTCHNL2_CAP_PTP_GET_CROSS_TIME);
ptp->set_dev_clk_time_access = IDPF_PTP_ACCESS(ad, VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME);
ptp->adj_dev_clk_time_access = IDPF_PTP_ACCESS(ad, VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK);
ptp->tx_stamp_access = IDPF_PTP_ACCESS(ad, VIRTCHNL2_CAP_PTP_TX_TSTAMPS);
}
Much shorter! :-)
> +/**
> + * 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_cross_time_reg_offsets cross_tstamp_offsets;
> + struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
> + struct virtchnl2_ptp_get_caps send_ptp_caps_msg = { };
Using {} is a GNU extension to C11, so its best to use {0} as initializer.
> + struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
> + struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
> + struct idpf_cmd_info args = { };
> + struct idpf_ptp_secondary_mbx *scnd_mbx;
> + struct idpf_ptp *ptp = adapter->ptp;
> + struct idpf_hw *hw = &adapter->hw;
> + enum idpf_ptp_access access_type;
> + int err;
> + u32 temp_offset;
> +
> + 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 |
> + VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME |
> + VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
> + VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK |
> + VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
> + VIRTCHNL2_CAP_PTP_TX_TSTAMPS |
> + VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
> +
> + args.ops = VIRTCHNL2_OP_PTP_GET_CAPS;
> + args.in_args = (uint8_t *)&send_ptp_caps_msg;
> + args.in_args_size = sizeof(send_ptp_caps_msg);
> +
> + args.out_buffer = adapter->mbx_resp;
> + args.out_size = sizeof(*recv_ptp_caps_msg);
> +
> + err = idpf_vc_cmd_execute(adapter, &args);
> + if (err < 0)
> + return err;
> +
> + recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
> + 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);
> +
> + /* 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);
> +
> + access_type = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
> + if (access_type != IDPF_PTP_DIRECT)
> + goto cross_tstamp;
> +
> + 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_PCI_REG_ADDR(hw,
> + temp_offset);
No need to wrap these lines. Use the full 100 chars.
(Also, when wrapping, double tab indent is sufficient, no need to put in 7
tabs + spaces to try and align braces in cases like this)
> + temp_offset = LE32_TO_CPU(clock_offsets.dev_clk_ns_h);
> + ptp->dev_clk_regs.dev_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
> + temp_offset);
> + temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_l);
> + ptp->dev_clk_regs.phy_clk_ns_l = IDPF_PCI_REG_ADDR(hw,
> + temp_offset);
> + temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_h);
> + ptp->dev_clk_regs.phy_clk_ns_h = IDPF_PCI_REG_ADDR(hw,
> + temp_offset);
> + temp_offset = LE32_TO_CPU(clock_offsets.cmd_sync_trigger);
> + ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
> +
> +cross_tstamp:
> + access_type = (enum idpf_ptp_access)ptp->get_cross_tstamp_access;
> + if (access_type != IDPF_PTP_DIRECT)
> + goto discipline_clock;
> +
> + 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_PCI_REG_ADDR(hw,
> + temp_offset);
Again, don't wrap. Also, this indentation used neither is a simple
double-indent, nor does it align to brace so please use one style
consistently.
> + temp_offset = LE32_TO_CPU(cross_tstamp_offsets.sys_time_ns_h);
> + ptp->dev_clk_regs.sys_time_ns_h = IDPF_PCI_REG_ADDR(hw,
> + temp_offset);
> + temp_offset = LE32_TO_CPU(cross_tstamp_offsets.cmd_sync_trigger);
> + ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
> +
> +discipline_clock:
> + access_type = (enum idpf_ptp_access)ptp->adj_dev_clk_time_access;
> + if (access_type != IDPF_PTP_DIRECT)
> + return err;
> +
> + 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_PCI_REG_ADDR(hw, temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_l);
> + ptp->dev_clk_regs.incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_h);
> + ptp->dev_clk_regs.incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_l);
> + ptp->dev_clk_regs.shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_h);
> + ptp->dev_clk_regs.shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
> +
> + /* PHY clock offsets */
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_cmd_type);
> + ptp->dev_clk_regs.phy_cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_l);
> + ptp->dev_clk_regs.phy_incval_l = IDPF_PCI_REG_ADDR(hw,
> + temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_h);
> + ptp->dev_clk_regs.phy_incval_h = IDPF_PCI_REG_ADDR(hw,
> + temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_l);
> + ptp->dev_clk_regs.phy_shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
> + temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_h);
> + ptp->dev_clk_regs.phy_shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
> +
> + return err;
> +}
> +
> +/**
> + * 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)
> +{
> + uint32_t 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 */
> + IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, shtime_enable);
> + IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, exec_cmd | shtime_enable);
> +}
> +
> +/**
> + * 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_cmd_info args = { };
As above, use {0} as initializer.
> + int err;
> + u64 dev_time;
> +
> + args.ops = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME;
> + args.in_args = (uint8_t *)&get_dev_clk_time_msg;
> + args.in_args_size = sizeof(get_dev_clk_time_msg);
> + args.out_buffer = adapter->mbx_resp;
> + args.out_size = sizeof(get_dev_clk_time_msg);
> +
> + err = idpf_vc_cmd_execute(adapter, &args);
> + if (err < 0)
> + return err;
> +
> + get_dev_clk_time_msg = *(struct virtchnl2_ptp_get_dev_clk_time *)args.out_buffer;
> + dev_time = LE64_TO_CPU(get_dev_clk_time_msg.dev_time_ns);
> + dev_clk_time->dev_clk_time_ns = dev_time;
> +
> + return err;
> +}
> +
> +/**
> + * 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_cmd_info args = { };
> + int err;
> +
> + args.ops = VIRTCHNL2_OP_PTP_GET_CROSS_TIME;
> + args.in_args = (uint8_t *)&cross_time_msg;
> + args.in_args_size = sizeof(cross_time_msg);
> + args.out_buffer = adapter->mbx_resp;
> + args.out_size = sizeof(cross_time_msg);
> +
> + err = idpf_vc_cmd_execute(adapter, &args);
> + if (err < 0)
> + return err;
> +
> + cross_time_msg = *(struct virtchnl2_ptp_get_cross_time *)args.out_buffer;
> + 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 err;
> +}
> +
> +/**
> + * 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 = { };
> + struct idpf_cmd_info args = { };
> + int err;
> +
> + set_dev_clk_time_msg.dev_time_ns = CPU_TO_LE64(time);
> +
> + args.ops = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME;
> + args.in_args = (uint8_t *)&set_dev_clk_time_msg;
> + args.in_args_size = sizeof(set_dev_clk_time_msg);
> + args.out_buffer = adapter->mbx_resp;
> + args.out_size = sizeof(set_dev_clk_time_msg);
> +
> + err = idpf_vc_cmd_execute(adapter, &args);
> + if (err < 0)
> + return err;
> +
> + return err;
> +}
> +
> +/**
> + * 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, int64_t delta)
> +{
> + struct virtchnl2_ptp_adj_dev_clk_time adj_dev_clk_time_msg = { };
> + struct idpf_cmd_info args = { };
> + int err;
> +
> + adj_dev_clk_time_msg.delta = CPU_TO_LE64(delta);
> +
> + args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME;
> + args.in_args = (uint8_t *)&adj_dev_clk_time_msg;
> + args.in_args_size = sizeof(adj_dev_clk_time_msg);
> + args.out_buffer = adapter->mbx_resp;
> + args.out_size = sizeof(adj_dev_clk_time_msg);
> +
> + err = idpf_vc_cmd_execute(adapter, &args);
> + if (err < 0)
> + return err;
> +
> + return err;
> +}
> +
> +/**
> + * 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 = { };
> + struct idpf_cmd_info args = { };
> + int err;
> +
> + adj_dev_clk_fine_msg.incval = CPU_TO_LE64(incval);
> +
> + args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE;
> + args.in_args = (uint8_t *)&adj_dev_clk_fine_msg;
> + args.in_args_size = sizeof(adj_dev_clk_fine_msg);
> + args.out_buffer = adapter->mbx_resp;
> + args.out_size = sizeof(adj_dev_clk_fine_msg);
> +
> + err = idpf_vc_cmd_execute(adapter, &args);
> + if (err < 0)
> + return err;
> +
> + return err;
> +}
> +
> +/**
> + * 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 virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
> + enum idpf_ptp_access tstamp_access, get_dev_clk_access;
> + struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
> + struct idpf_ptp *ptp = vport->adapter->ptp;
> + struct idpf_cmd_info args = { };
> + int err;
> + u16 num_latches, i;
> + u32 size;
> +
> + if (ptp == NULL)
> + return -EOPNOTSUPP;
> +
> + tstamp_access = (enum idpf_ptp_access)ptp->tx_tstamp_access;
> + get_dev_clk_access = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
> + if (tstamp_access == IDPF_PTP_NONE ||
> + get_dev_clk_access == IDPF_PTP_NONE)
> + return -EOPNOTSUPP;
> +
> + send_tx_tstamp_caps.vport_id = CPU_TO_LE32(vport->vport_id);
> +
> + args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS;
> + args.in_args = (uint8_t *)&send_tx_tstamp_caps;
> + args.in_args_size = sizeof(send_tx_tstamp_caps);
> + args.out_size = IDPF_CTLQ_MAX_BUF_LEN;
> + args.out_buffer = vport->adapter->mbx_resp;
> +
> + err = idpf_vc_cmd_execute(vport->adapter, &args);
> + if (err < 0)
> + return err;
> +
> + rcv_tx_tstamp_caps = (struct virtchnl2_ptp_get_vport_tx_tstamp_caps *)
> + args.out_buffer;
> + num_latches = LE16_TO_CPU(rcv_tx_tstamp_caps->num_latches);
> + size = sizeof(struct idpf_ptp_vport_tx_tstamp_caps) +
> + sizeof(struct idpf_ptp_tx_tstamp) * num_latches;
> + tstamp_caps = rte_zmalloc(NULL, size, 0);
> + if (tstamp_caps == NULL)
> + return -ENOMEM;
> +
> + tstamp_caps->access = true;
> + tstamp_caps->num_entries = num_latches;
> +
> + tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
> +
> + for (i = 0; i < tstamp_caps->num_entries; i++) {
> + __le32 offset_l, offset_h;
> +
> + tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i];
> +
> + if (tstamp_access == IDPF_PTP_DIRECT) {
> + offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
> + offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
> + tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_l = LE32_TO_CPU(offset_l);
> + tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_h = LE32_TO_CPU(offset_h);
> + }
> + tstamp_caps->tx_tstamp[i].idx = tx_tstamp_latch_caps.index;
> + }
> +
> + tstamp_caps->latched_idx = -1;
> + vport->tx_tstamp_caps = tstamp_caps;
> +
> + return err;
> +}
> +
> +/**
> + * 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
> + * @tx_tstamp: Tx timestamp structure to be fulfilled with the timestamp value
> + *
> + * Read the value of the Tx timestamp for a given latch received from the
> + * Control Plane.
> + *
> + * 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 *tx_tstamp)
> +{
> + struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
> + u8 tstamp_ns_lo_bit;
> +
> + tx_tstamp_caps = vport->tx_tstamp_caps;
> + tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
> +
> + tx_tstamp->tstamp = LE64_TO_CPU(tstamp_latch->tstamp);
> + tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
> +
> + return 0;
> +}
> +
> +/**
> + * 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 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 *ptp_tx_tstamp;
> + struct idpf_cmd_info args = { };
> + int size, msg_size;
> + u32 vport_id;
> + u16 num_latches, id;
> + int err;
> +
> + tx_tstamp_caps = vport->tx_tstamp_caps;
> + ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
> +
> + size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
> + sizeof(struct virtchnl2_ptp_tx_tstamp_latch) *
> + tx_tstamp_caps->num_entries;
> + send_tx_tstamp_msg = rte_zmalloc(NULL, size, 0);
> + if (send_tx_tstamp_msg == NULL)
> + return -ENOMEM;
> +
> + for (id = 0; id < tx_tstamp_caps->num_entries; id++,
> + ptp_tx_tstamp++)
> + send_tx_tstamp_msg->tstamp_latches[id].index =
> + ptp_tx_tstamp->idx;
Identation in this loop badly needs to be reworked.
> + send_tx_tstamp_msg->get_devtime_with_txtstmp = 1;
> +
> + msg_size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
> + sizeof(struct virtchnl2_ptp_tx_tstamp_latch) * id;
> + send_tx_tstamp_msg->vport_id = CPU_TO_LE32(vport->vport_id);
> + send_tx_tstamp_msg->num_latches = CPU_TO_LE16(id);
> +
> + args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP;
> + args.in_args = (uint8_t *)send_tx_tstamp_msg;
> + args.in_args_size = msg_size;
> + args.out_size = msg_size;
> + args.out_buffer = vport->adapter->mbx_resp;
> +
> + err = idpf_vc_cmd_execute(vport->adapter, &args);
> + rte_free(send_tx_tstamp_msg);
> + if (err < 0)
> + return err;
> +
> + recv_tx_tstamp_msg = (struct virtchnl2_ptp_get_vport_tx_tstamp_latches *)
> + args.out_buffer;
> + vport_id = LE32_TO_CPU(recv_tx_tstamp_msg->vport_id);
> + if (vport->vport_id != vport_id)
> + return -EINVAL;
> +
> + num_latches = LE16_TO_CPU(recv_tx_tstamp_msg->num_latches);
> +
> + ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
> + for (id = 0; id < num_latches; id++, ptp_tx_tstamp++) {
> + tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[id];
> +
> + if (!tstamp_latch.valid)
> + continue;
> +
> + err = idpf_ptp_get_tstamp_value(vport, &tstamp_latch,
> + ptp_tx_tstamp);
> + if (err == 0) {
> + tx_tstamp_caps->latched_idx = id;
> + vport->adapter->time_hw = recv_tx_tstamp_msg->device_time;
> + }
> + break;
> + }
> + return err;
> +}
> +
> +/**
> + * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer value
> + * @adapter: Driver specific private structure
> + *
> + * Return: the device clock time.
> + */
> +static u64 idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter)
> +{
> + struct idpf_ptp *ptp = adapter->ptp;
> + u32 hi, lo;
> +
> + idpf_ptp_enable_shtime(adapter);
> +
> + lo = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_l);
> + hi = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_h);
> +
> + return ((u64)hi << 32) | lo;
> +}
> +
> +/**
> + * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value through mailbox
> + * @adapter: Driver specific private structure
> + * @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,
> + u64 *src_clk)
> +{
> + struct idpf_ptp_dev_timers clk_time;
> + int err;
> +
> + err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
> + if (err)
> + return err;
> +
> + *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
> + * @src_clk: Returned main timer value in nanoseconds unit
> + *
> + * Return: the device clock time on success, -errno otherwise.
> + */
> +int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, u64 *src_clk)
> +{
> + if (adapter->ptp == NULL)
> + return 0;
Should this not be an error, returning -EINVAL?
> + switch ((enum idpf_ptp_access)adapter->ptp->get_dev_clk_time_access) {
> + case IDPF_PTP_NONE:
> + return -EOPNOTSUPP;
Strictly speaking, you don't need this, it's handled by the default case.
> + case IDPF_PTP_MAILBOX:
> + return idpf_ptp_read_src_clk_reg_mailbox(adapter, src_clk);
> + case IDPF_PTP_DIRECT:
> + *src_clk = idpf_ptp_read_src_clk_reg_direct(adapter);
> + break;
Why have the function types for direct and mailbox different? Why not have
both just take src_clk as parameter and return 0 on success, so you can
have similar use of them. It would also possibly allow function pointer use
in future, if they are the same types.
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/net/intel/idpf/idpf_ptp.h b/drivers/net/intel/idpf/idpf_ptp.h
> new file mode 100644
> index 0000000000..68ac66ae91
> --- /dev/null
> +++ b/drivers/net/intel/idpf/idpf_ptp.h
> @@ -0,0 +1,227 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2025 Intel Corporation
> + */
> +
> +#ifndef _IDPF_PTP_H_
> +#define _IDPF_PTP_H_
> +
> +#include "idpf_osdep.h"
> +#include <rte_time.h>
> +#include "idpf_common_device.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 {
> + uint32_t exec_cmd_mask;
> + uint32_t 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
> + * @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
> + */
> +struct idpf_ptp_dev_clk_regs {
> + /* Main clock */
> + volatile uint32_t *dev_clk_ns_l;
> + volatile uint32_t *dev_clk_ns_h;
> +
> + /* PHY timer */
> + volatile uint32_t *phy_clk_ns_l;
> + volatile uint32_t *phy_clk_ns_h;
> +
> + /* System time */
> + volatile uint32_t *sys_time_ns_l;
> + volatile uint32_t *sys_time_ns_h;
> +
> + /* Main timer adjustments */
> + volatile uint32_t *incval_l;
> + volatile uint32_t *incval_h;
> + volatile uint32_t *shadj_l;
> + volatile uint32_t *shadj_h;
> +
> + /* PHY timer adjustments */
> + volatile uint32_t *phy_incval_l;
> + volatile uint32_t *phy_incval_h;
> + volatile uint32_t *phy_shadj_l;
> + volatile uint32_t *phy_shadj_h;
> +
> + /* Command */
> + volatile uint32_t *cmd;
> + volatile uint32_t *phy_cmd;
> + volatile uint32_t *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_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 {
> + uint16_t peer_mbx_q_id;
> + uint16_t peer_id;
> + 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 - 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
> + * @tstamp: the Tx tstamp value
> + * @idx: the index of the Tx tstamp
> + */
> +struct idpf_ptp_tx_tstamp {
> + uint64_t tstamp;
> + uint32_t tx_latch_reg_offset_l;
> + uint32_t tx_latch_reg_offset_h;
> + uint32_t 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
> + * @access: indicates an access to Tx timestamp
> + * @latches_index: the index of the latched Tx timestamps
> + * @tx_tstamp: array of Tx timestamp parameters
> + */
> +struct idpf_ptp_vport_tx_tstamp_caps {
> + uint32_t vport_id;
> + uint16_t num_entries;
> + uint16_t tstamp_ns_lo_bit;
> + uint16_t latched_idx;
> + bool access:1;
> + struct idpf_ptp_tx_tstamp tx_tstamp[];
> +};
> +
> +/**
> + * @struct idpf_ptp - PTP parameters
> + * @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
> + * @tx_tstamp_access: access type for the Tx timestamp value read
> + * @rsv: Reserved fields
> + * @secondary_mbx: parameters for using dedicated PTP mailbox
> + */
> +struct idpf_ptp {
> + uint64_t base_incval;
> + uint64_t max_adj;
> + struct idpf_ptp_cmd cmd;
> + struct idpf_ptp_dev_clk_regs dev_clk_regs;
> + uint32_t caps;
> + uint8_t get_dev_clk_time_access:2;
> + uint8_t get_cross_tstamp_access:2;
> + uint8_t set_dev_clk_time_access:2;
> + uint8_t adj_dev_clk_time_access:2;
> + uint8_t tx_tstamp_access:2;
> + uint8_t rsv:6;
> + struct idpf_ptp_secondary_mbx secondary_mbx;
> +};
> +
> +/**
> + * @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 {
> + uint64_t sys_time_ns;
> + uint64_t dev_clk_time_ns;
> +};
> +
> +int idpf_ptp_get_caps(struct idpf_adapter *adapter);
> +int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t *src_clk);
> +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, uint64_t time);
> +int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t incval);
> +int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, int64_t delta);
> +int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport);
> +int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
> +
> +/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
> +static inline uint64_t
> +idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
> + bool is_rx, uint32_t in_timestamp)
> +{
> + const uint64_t mask = 0xFFFFFFFF;
> + uint32_t phc_time_lo, delta;
> + uint64_t ns;
> +
> + if (flag != 0)
> + idpf_ptp_read_src_clk_reg(ad, &ad->time_hw);
> +
> + /* Extract the lower 32 bits of the PHC time */
> + phc_time_lo = (uint32_t)(ad->time_hw);
> +
> + /* Calculate the delta between the lower 32bits of the cached PHC
> + * time and the in_timestamp value.
> + */
> + delta = in_timestamp - phc_time_lo;
> +
> + if (delta > mask / 2) {
> + /* Reverse the delta calculation here */
> + delta = phc_time_lo - in_timestamp;
> + ns = ad->time_hw - delta;
> + } else {
> + if (is_rx)
> + ns = ad->time_hw - delta;
> + else
> + ns = ad->time_hw + delta;
> + }
> +
> + return ns;
> +}
> +#endif /* _IDPF_PTP_H_ */
> diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
> index a805d02ea2..5a4a3c2259 100644
> --- a/drivers/net/intel/idpf/meson.build
> +++ b/drivers/net/intel/idpf/meson.build
> @@ -18,6 +18,7 @@ sources += files(
>
> 'idpf_ethdev.c',
> 'idpf_rxtx.c',
> + 'idpf_ptp.c',
All lists in DPDK should be in alphabetical order, unless there is a reason
they can't be. Therefore, this new C file should be above idpf_rxtx.c in
the list.
> )
>
> if arch_subdir == 'x86' and dpdk_conf.get('RTE_IOVA_IN_MBUF') == 1
> --
> 2.47.1
>
[-- Attachment #2: Type: text/html, Size: 72577 bytes --]
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH v6 0/4] Enable PTP feature for MEV
2026-01-23 0:04 ` [PATCH v5 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
2026-02-03 12:52 ` Bruce Richardson
@ 2026-02-14 19:49 ` Soumyadeep Hore
2026-02-14 19:49 ` [PATCH v6 1/4] net/idpf/base: add a new API for PTP support Soumyadeep Hore
` (3 more replies)
1 sibling, 4 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-02-14 19:49 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Enabling basic PTP feature in IDPF PMD using virtchnl messages.
---
v6:
- Addressed Bruce's comments
- Addressed AI comments
---
v5:
- Addressed AI comments
---
v4:
- Addressed Bruce's comments
---
v3:
- Fixed checkpatch warnings
- Addressed Bruce's comments
---
v2:
- Fixed essential checkpatch warnings
---
Milena Olech (1):
net/idpf/base: add a new API for PTP support
Soumyadeep Hore (3):
net/idpf: add PTP virtchnl2 support
net/idpf: add timesync support
doc/guides: add PTP IDPF documentation
doc/guides/nics/features/idpf.ini | 1 +
doc/guides/nics/idpf.rst | 15 +
doc/guides/rel_notes/release_26_03.rst | 4 +
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/base/virtchnl2.h | 324 +++++++----
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 121 +++--
drivers/net/intel/idpf/idpf_common_rxtx.h | 7 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 31 +-
drivers/net/intel/idpf/idpf_ethdev.c | 270 ++++++++++
drivers/net/intel/idpf/idpf_ptp.c | 504 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 130 +++++
drivers/net/intel/idpf/meson.build | 1 +
13 files changed, 1261 insertions(+), 152 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
--
2.47.1
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH v6 1/4] net/idpf/base: add a new API for PTP support
2026-02-14 19:49 ` [PATCH v6 0/4] Enable PTP feature for MEV Soumyadeep Hore
@ 2026-02-14 19:49 ` Soumyadeep Hore
2026-02-26 9:57 ` Kumar, Rajesh3
2026-02-14 19:49 ` [PATCH v6 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
` (2 subsequent siblings)
3 siblings, 1 reply; 57+ messages in thread
From: Soumyadeep Hore @ 2026-02-14 19:49 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao,
Milena Olech
From: Milena Olech <milena.olech@intel.com>
Add PTP capabilities to determine which PTP features are enabled -
get/set the time, cross timestamp, adjust the clock and timestamping.
Create appropriate structures and opcodes to support features introduced by
capabilities.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
---
drivers/net/intel/idpf/base/virtchnl2.h | 324 +++++++++++++++++-------
1 file changed, 235 insertions(+), 89 deletions(-)
diff --git a/drivers/net/intel/idpf/base/virtchnl2.h b/drivers/net/intel/idpf/base/virtchnl2.h
index 6cfb4f56fa..0b01daee30 100644
--- a/drivers/net/intel/idpf/base/virtchnl2.h
+++ b/drivers/net/intel/idpf/base/virtchnl2.h
@@ -118,8 +118,14 @@ enum virtchnl2_op {
VIRTCHNL2_OP_DEL_QUEUE_GROUPS = 539,
VIRTCHNL2_OP_GET_PORT_STATS = 540,
/* TimeSync opcodes */
- VIRTCHNL2_OP_GET_PTP_CAPS = 541,
- VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES = 542,
+ 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,
};
#define VIRTCHNL2_RDMA_INVALID_QUEUE_IDX 0xFFFF
@@ -1813,123 +1819,180 @@ VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_promisc_info);
* enum virtchnl2_ptp_caps - PTP capabilities
*/
enum virtchnl2_ptp_caps {
- VIRTCHNL2_PTP_CAP_LEGACY_CROSS_TIME = BIT(0),
- VIRTCHNL2_PTP_CAP_PTM = BIT(1),
- VIRTCHNL2_PTP_CAP_DEVICE_CLOCK_CONTROL = BIT(2),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_DIRECT = BIT(3),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_VIRTCHNL = BIT(4),
-};
-
-/**
- * struct virtchnl2_ptp_legacy_cross_time_reg - Legacy cross time registers
- * offsets.
+ 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_legacy_cross_time_reg {
- __le32 shadow_time_0;
- __le32 shadow_time_l;
- __le32 shadow_time_h;
- __le32 cmd_sync;
+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(16, virtchnl2_ptp_legacy_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_clk_reg_offsets);
/**
- * struct virtchnl2_ptp_ptm_cross_time_reg - PTM cross time registers 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_ptm_cross_time_reg {
- __le32 art_l;
- __le32 art_h;
- __le32 cmd_sync;
+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_ptm_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_cross_time_reg_offsets);
/**
- * struct virtchnl2_ptp_device_clock_control - Registers needed to control the
- * main clock.
+ * 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_device_clock_control {
- __le32 cmd;
- __le32 incval_l;
- __le32 incval_h;
- __le32 shadj_l;
- __le32 shadj_h;
- u8 pad[4];
+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(24, virtchnl2_ptp_device_clock_control);
+VIRTCHNL2_CHECK_STRUCT_LEN(40, virtchnl2_ptp_clk_adj_reg_offsets);
/**
- * struct virtchnl2_ptp_tx_tstamp_entry - PTP TX timestamp entry
- * @tx_latch_register_base: TX latch register base
- * @tx_latch_register_offset: TX latch register offset
- * @index: Index
- * @pad: Padding
+ * 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_entry {
- __le32 tx_latch_register_base;
- __le32 tx_latch_register_offset;
+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_entry);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch_caps);
/**
- * struct virtchnl2_ptp_tx_tstamp - Structure that defines tx tstamp entries
+ * struct virtchnl2_ptp_get_vport_tx_tstamp_caps - Structure that defines Tx
+ * tstamp entries
+ * @vport_id: Vport number
* @num_latches: Total number of latches
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
- * @ptp_tx_tstamp_entries: Aarray of TX timestamp entries
+ * @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_tx_tstamp {
+struct virtchnl2_ptp_get_vport_tx_tstamp_caps {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
- struct virtchnl2_ptp_tx_tstamp_entry ptp_tx_tstamp_entries[STRUCT_VAR_LEN];
+ u8 tstamp_ns_lo_bit;
+ u8 tstamp_ns_hi_bit;
+ u8 pad[8];
+
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp,
- ptp_tx_tstamp_entries);
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_caps,
+ tstamp_latches);
/**
- * struct virtchnl2_get_ptp_caps - Get PTP capabilities
- * @ptp_caps: PTP capability bitmap. See enum virtchnl2_ptp_caps.
- * @pad: Padding
- * @legacy_cross_time_reg: Legacy cross time register
- * @ptm_cross_time_reg: PTM cross time register
- * @device_clock_control: Device clock control
- * @tx_tstamp: TX timestamp
+ * 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
*
- * PV/VF sends this message to negotiate PTP capabilities. CP updates bitmap
+ * PF/VF sends this message to negotiate PTP capabilities. CP updates bitmap
* with supported features and fulfills appropriate structures.
- *
- * Associated with VIRTCHNL2_OP_GET_PTP_CAPS.
- */
-struct virtchnl2_get_ptp_caps {
- __le32 ptp_caps;
+ * 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_legacy_cross_time_reg legacy_cross_time_reg;
- struct virtchnl2_ptp_ptm_cross_time_reg ptm_cross_time_reg;
- struct virtchnl2_ptp_device_clock_control device_clock_control;
- struct virtchnl2_ptp_tx_tstamp tx_tstamp;
+ 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_VAR_LEN(88, virtchnl2_get_ptp_caps,
- tx_tstamp.ptp_tx_tstamp_entries);
+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_h: Timestamp high
- * @tstamp_l: Timestamp low
+ * @tstamp: Timestamp value
* @index: Index
* @valid: Timestamp validity
- * @pad: Padding
+ * @pad: Padding for future extensions
*/
struct virtchnl2_ptp_tx_tstamp_latch {
- __le32 tstamp_h;
- __le32 tstamp_l;
+ __le64 tstamp;
u8 index;
u8 valid;
u8 pad[6];
@@ -1938,26 +2001,97 @@ struct virtchnl2_ptp_tx_tstamp_latch {
VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch);
/**
- * struct virtchnl2_ptp_tx_tstamp_latches - PTP TX timestamp latches
+ * 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
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
+ * @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_GET_PTP_TX_TSTAMP_LATCHES.
+ * Associated with VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP.
*/
-struct virtchnl2_ptp_tx_tstamp_latches {
+struct virtchnl2_ptp_get_vport_tx_tstamp_latches {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
+ u8 get_devtime_with_txtstmp;
+ u8 pad[1];
+ u64 device_time;
struct virtchnl2_ptp_tx_tstamp_latch tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp_latches,
+
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_latches,
tstamp_latches);
+/* VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME
+ * @dev_time_ns: Device clock time value in nanoseconds
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the time from the main timer
+ */
+struct virtchnl2_ptp_get_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_dev_clk_time);
+
+/* 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
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the cross time
+ */
+struct virtchnl2_ptp_get_cross_time {
+ __le64 sys_time_ns;
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_get_cross_time);
+
+/* VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME
+ * @dev_time_ns: Device time value expressed in nanoseconds to set
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to set the time of the main timer
+ */
+struct virtchnl2_ptp_set_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_set_dev_clk_time);
+
+/* 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);
+
+/* 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);
+
static inline const char *virtchnl2_op_str(__le32 v_opcode)
{
switch (v_opcode) {
@@ -2025,10 +2159,22 @@ static inline const char *virtchnl2_op_str(__le32 v_opcode)
return "VIRTCHNL2_OP_DEL_QUEUE_GROUPS";
case VIRTCHNL2_OP_GET_PORT_STATS:
return "VIRTCHNL2_OP_GET_PORT_STATS";
- case VIRTCHNL2_OP_GET_PTP_CAPS:
- return "VIRTCHNL2_OP_GET_PTP_CAPS";
- case VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES:
- return "VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES";
+ case VIRTCHNL2_OP_PTP_GET_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_CAPS";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP";
+ case VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_CROSS_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_CROSS_TIME";
+ case VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS";
default:
return "Unsupported (update virtchnl2.h)";
}
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v6 2/4] net/idpf: add PTP virtchnl2 support
2026-02-14 19:49 ` [PATCH v6 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-02-14 19:49 ` [PATCH v6 1/4] net/idpf/base: add a new API for PTP support Soumyadeep Hore
@ 2026-02-14 19:49 ` Soumyadeep Hore
2026-02-26 10:15 ` Kumar, Rajesh3
2026-02-14 19:49 ` [PATCH v6 3/4] net/idpf: add timesync support Soumyadeep Hore
2026-02-14 19:49 ` [PATCH v6 4/4] doc/guides: add PTP IDPF documentation Soumyadeep Hore
3 siblings, 1 reply; 57+ messages in thread
From: Soumyadeep Hore @ 2026-02-14 19:49 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Add a new file - idpf_ptp - to handle PTP virtchnl messages.
Keep the registers addresses in the PTP struct.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 31 +-
drivers/net/intel/idpf/idpf_ptp.c | 504 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 130 +++++
drivers/net/intel/idpf/meson.build | 1 +
5 files changed, 668 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
diff --git a/drivers/net/intel/idpf/idpf_common_device.h b/drivers/net/intel/idpf/idpf_common_device.h
index 31915a03d4..e45795c18b 100644
--- a/drivers/net/intel/idpf/idpf_common_device.h
+++ b/drivers/net/intel/idpf/idpf_common_device.h
@@ -103,6 +103,7 @@ struct idpf_adapter {
/* For timestamp */
uint64_t time_hw;
+ struct idpf_ptp *ptp;
enum idpf_rx_func_type rx_func_type;
enum idpf_tx_func_type tx_func_type;
@@ -173,6 +174,9 @@ struct idpf_vport {
/* Event from ipf */
bool link_up;
uint32_t link_speed;
+
+ /* For PTP */
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
};
/* Message type read in virtual channel from PF */
diff --git a/drivers/net/intel/idpf/idpf_common_virtchnl.c b/drivers/net/intel/idpf/idpf_common_virtchnl.c
index e927d7415a..f8c487c537 100644
--- a/drivers/net/intel/idpf/idpf_common_virtchnl.c
+++ b/drivers/net/intel/idpf/idpf_common_virtchnl.c
@@ -4,6 +4,7 @@
#include "idpf_common_virtchnl.h"
#include "idpf_common_logs.h"
+#include "idpf_ptp.h"
#include <eal_export.h>
@@ -38,6 +39,24 @@ idpf_vc_clean(struct idpf_adapter *adapter)
return 0;
}
+ /* Helper to detect PTP-related mailbox messages */
+static inline bool
+idpf_mb_msg_is_ptp(uint32_t op)
+{
+ switch (op) {
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ 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:
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int
idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
uint16_t msg_size, uint8_t *msg)
@@ -71,8 +90,16 @@ idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
memcpy(dma_mem->va, msg, msg_size);
- ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
- ctlq_msg->func_id = 0;
+ if (adapter->ptp != NULL &&
+ idpf_mb_msg_is_ptp(op) &&
+ adapter->ptp->secondary_mbx.valid) {
+ 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 {
+ ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
+ ctlq_msg->func_id = 0;
+ }
ctlq_msg->data_len = msg_size;
ctlq_msg->cookie.mbx.chnl_opcode = op;
ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
diff --git a/drivers/net/intel/idpf/idpf_ptp.c b/drivers/net/intel/idpf/idpf_ptp.c
new file mode 100644
index 0000000000..aefd1309f7
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.c
@@ -0,0 +1,504 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#include "idpf_ptp.h"
+#include "idpf_common_virtchnl.h"
+#include "base/virtchnl2.h"
+
+static enum idpf_ptp_access
+idpf_ptp_get_access(const struct idpf_adapter *adapter, uint32_t direct,
+ uint32_t 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;
+}
+
+#define IDPF_PTP_ACCESS(adapter, cap) \
+ ((uint8_t)idpf_ptp_get_access((adapter), (cap), (cap##_MB)))
+
+static void
+idpf_ptp_get_features_access(const struct idpf_adapter *adapter)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+
+ ptp->get_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME);
+ ptp->get_cross_tstamp_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_GET_CROSS_TIME);
+ ptp->set_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME);
+ ptp->adj_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK);
+ ptp->tx_tstamp_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_TX_TSTAMPS);
+}
+
+int
+idpf_ptp_get_caps(struct idpf_adapter *adapter)
+{
+ struct virtchnl2_ptp_cross_time_reg_offsets cross_tstamp_offsets;
+ struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
+ struct virtchnl2_ptp_get_caps send_ptp_caps_msg = {0};
+ struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
+ struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
+ struct idpf_cmd_info args = {0};
+ struct idpf_ptp_secondary_mbx *scnd_mbx;
+ struct idpf_ptp *ptp = adapter->ptp;
+ struct idpf_hw *hw = &adapter->hw;
+ enum idpf_ptp_access access_type;
+ int err;
+ uint32_t temp_offset;
+
+ 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 |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CAPS;
+ args.in_args = (uint8_t *)&send_ptp_caps_msg;
+ args.in_args_size = sizeof(send_ptp_caps_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(*recv_ptp_caps_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
+ 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);
+ scnd_mbx->valid = scnd_mbx->peer_mbx_q_id != 0xffff;
+ if (scnd_mbx->valid)
+ scnd_mbx->peer_id = recv_ptp_caps_msg->peer_id;
+
+ idpf_ptp_get_features_access(adapter);
+
+ access_type = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.dev_clk_ns_h);
+ ptp->dev_clk_regs.dev_clk_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_l);
+ ptp->dev_clk_regs.phy_clk_ns_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_h);
+ ptp->dev_clk_regs.phy_clk_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ access_type = (enum idpf_ptp_access)ptp->get_cross_tstamp_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.sys_time_ns_h);
+ ptp->dev_clk_regs.sys_time_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ access_type = (enum idpf_ptp_access)ptp->adj_dev_clk_time_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ clk_adj_offsets = recv_ptp_caps_msg->clk_adj_offsets;
+
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_cmd_type);
+ ptp->dev_clk_regs.cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_l);
+ ptp->dev_clk_regs.incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_h);
+ ptp->dev_clk_regs.incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_l);
+ ptp->dev_clk_regs.shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_h);
+ ptp->dev_clk_regs.shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_cmd_type);
+ ptp->dev_clk_regs.phy_cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_l);
+ ptp->dev_clk_regs.phy_incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_h);
+ ptp->dev_clk_regs.phy_incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_l);
+ ptp->dev_clk_regs.phy_shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_h);
+ ptp->dev_clk_regs.phy_shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ return err;
+}
+
+static void
+idpf_ptp_enable_shtime(struct idpf_adapter *adapter)
+{
+ uint32_t shtime_enable, exec_cmd;
+
+ shtime_enable = adapter->ptp->cmd.shtime_enable_mask;
+ exec_cmd = adapter->ptp->cmd.exec_cmd_mask;
+
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, shtime_enable);
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync,
+ exec_cmd | shtime_enable);
+}
+
+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 = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+ uint64_t dev_time;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&get_dev_clk_time_msg;
+ args.in_args_size = sizeof(get_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(get_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ get_dev_clk_time_msg =
+ *(struct virtchnl2_ptp_get_dev_clk_time *)args.out_buffer;
+ dev_time = LE64_TO_CPU(get_dev_clk_time_msg.dev_time_ns);
+ dev_clk_time->dev_clk_time_ns = dev_time;
+
+ return err;
+}
+
+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 = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CROSS_TIME;
+ args.in_args = (uint8_t *)&cross_time_msg;
+ args.in_args_size = sizeof(cross_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(cross_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ cross_time_msg =
+ *(struct virtchnl2_ptp_get_cross_time *)args.out_buffer;
+ 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 err;
+}
+
+int
+idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, uint64_t time)
+{
+ struct virtchnl2_ptp_set_dev_clk_time set_dev_clk_time_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ set_dev_clk_time_msg.dev_time_ns = CPU_TO_LE64(time);
+
+ args.ops = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&set_dev_clk_time_msg;
+ args.in_args_size = sizeof(set_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(set_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, int64_t delta)
+{
+ struct virtchnl2_ptp_adj_dev_clk_time adj_dev_clk_time_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ adj_dev_clk_time_msg.delta = CPU_TO_LE64(delta);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&adj_dev_clk_time_msg;
+ args.in_args_size = sizeof(adj_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t incval)
+{
+ struct virtchnl2_ptp_adj_dev_clk_fine adj_dev_clk_fine_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ adj_dev_clk_fine_msg.incval = CPU_TO_LE64(incval);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE;
+ args.in_args = (uint8_t *)&adj_dev_clk_fine_msg;
+ args.in_args_size = sizeof(adj_dev_clk_fine_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_fine_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
+{
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps send_tx_tstamp_caps = {0};
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps *rcv_tx_tstamp_caps;
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
+ enum idpf_ptp_access tstamp_access, get_dev_clk_access;
+ struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
+ struct idpf_ptp *ptp = vport->adapter->ptp;
+ struct idpf_cmd_info args = {0};
+ int err;
+ uint16_t num_latches, i;
+ uint32_t size;
+
+ if (ptp == NULL)
+ return -EOPNOTSUPP;
+
+ tstamp_access = (enum idpf_ptp_access)ptp->tx_tstamp_access;
+ get_dev_clk_access = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (tstamp_access == IDPF_PTP_NONE ||
+ get_dev_clk_access == IDPF_PTP_NONE)
+ return -EOPNOTSUPP;
+
+ send_tx_tstamp_caps.vport_id = CPU_TO_LE32(vport->vport_id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS;
+ args.in_args = (uint8_t *)&send_tx_tstamp_caps;
+ args.in_args_size = sizeof(send_tx_tstamp_caps);
+ args.out_size = IDPF_CTLQ_MAX_BUF_LEN;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ if (err < 0)
+ return err;
+
+ rcv_tx_tstamp_caps =
+ (struct virtchnl2_ptp_get_vport_tx_tstamp_caps *)args.out_buffer;
+ num_latches = LE16_TO_CPU(rcv_tx_tstamp_caps->num_latches);
+ size = sizeof(struct idpf_ptp_vport_tx_tstamp_caps) +
+ sizeof(struct idpf_ptp_tx_tstamp) * num_latches;
+ tstamp_caps = rte_zmalloc(NULL, size, 0);
+ if (tstamp_caps == NULL)
+ return -ENOMEM;
+
+ tstamp_caps->access = true;
+ tstamp_caps->num_entries = num_latches;
+ tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ for (i = 0; i < tstamp_caps->num_entries; i++) {
+ __le32 offset_l, offset_h;
+
+ tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i];
+
+ if (tstamp_access == IDPF_PTP_DIRECT) {
+ offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
+ offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_l =
+ LE32_TO_CPU(offset_l);
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_h =
+ LE32_TO_CPU(offset_h);
+ }
+ tstamp_caps->tx_tstamp[i].idx = tx_tstamp_latch_caps.index;
+ }
+
+ tstamp_caps->latched_idx = -1;
+ vport->tx_tstamp_caps = tstamp_caps;
+
+ return err;
+}
+
+static int
+idpf_ptp_get_tstamp_value(struct idpf_vport *vport,
+ struct virtchnl2_ptp_tx_tstamp_latch *tstamp_latch,
+ struct idpf_ptp_tx_tstamp *tx_tstamp)
+{
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ uint8_t tstamp_ns_lo_bit;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ tx_tstamp->tstamp = LE64_TO_CPU(tstamp_latch->tstamp);
+ tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
+
+ return 0;
+}
+
+int
+idpf_ptp_get_tx_tstamp(struct idpf_vport *vport)
+{
+ struct virtchnl2_ptp_get_vport_tx_tstamp_latches *send_tx_tstamp_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 *ptp_tx_tstamp;
+ struct idpf_cmd_info args = {0};
+ int size, msg_size;
+ uint32_t vport_id;
+ uint16_t num_latches, id;
+ int err;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+
+ size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) *
+ tx_tstamp_caps->num_entries;
+ send_tx_tstamp_msg = rte_zmalloc(NULL, size, 0);
+ if (send_tx_tstamp_msg == NULL)
+ return -ENOMEM;
+
+ for (id = 0; id < tx_tstamp_caps->num_entries; id++, ptp_tx_tstamp++)
+ send_tx_tstamp_msg->tstamp_latches[id].index = ptp_tx_tstamp->idx;
+ send_tx_tstamp_msg->get_devtime_with_txtstmp = 1;
+
+ msg_size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) * id;
+ send_tx_tstamp_msg->vport_id = CPU_TO_LE32(vport->vport_id);
+ send_tx_tstamp_msg->num_latches = CPU_TO_LE16(id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP;
+ args.in_args = (uint8_t *)send_tx_tstamp_msg;
+ args.in_args_size = msg_size;
+ args.out_size = msg_size;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ rte_free(send_tx_tstamp_msg);
+ if (err < 0)
+ return err;
+
+ recv_tx_tstamp_msg =
+ (struct virtchnl2_ptp_get_vport_tx_tstamp_latches *)args.out_buffer;
+ vport_id = LE32_TO_CPU(recv_tx_tstamp_msg->vport_id);
+ if (vport->vport_id != vport_id)
+ return -EINVAL;
+
+ num_latches = LE16_TO_CPU(recv_tx_tstamp_msg->num_latches);
+
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+ for (id = 0; id < num_latches; id++, ptp_tx_tstamp++) {
+ tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[id];
+
+ if (!tstamp_latch.valid)
+ continue;
+
+ err = idpf_ptp_get_tstamp_value(vport, &tstamp_latch,
+ ptp_tx_tstamp);
+ if (err == 0) {
+ tx_tstamp_caps->latched_idx = id;
+ vport->adapter->time_hw = recv_tx_tstamp_msg->device_time;
+ }
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer value
+ * @adapter: Driver specific private structure
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int
+idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter, uint64_t *src_clk)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint32_t hi, lo;
+
+ idpf_ptp_enable_shtime(adapter);
+
+ lo = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_l);
+ hi = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_h);
+
+ *src_clk = ((uint64_t)hi << 32) | lo;
+ return 0;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value through mailbox
+ * @adapter: Driver specific private structure
+ * @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,
+ uint64_t *src_clk)
+{
+ struct idpf_ptp_dev_timers clk_time;
+ int err;
+
+ err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
+ if (err)
+ return err;
+
+ *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
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int
+idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t *src_clk)
+{
+ if (adapter->ptp == NULL)
+ return -EINVAL;
+
+ switch ((enum idpf_ptp_access)adapter->ptp->get_dev_clk_time_access) {
+ case IDPF_PTP_MAILBOX:
+ return idpf_ptp_read_src_clk_reg_mailbox(adapter, src_clk);
+ case IDPF_PTP_DIRECT:
+ return idpf_ptp_read_src_clk_reg_direct(adapter, src_clk);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
diff --git a/drivers/net/intel/idpf/idpf_ptp.h b/drivers/net/intel/idpf/idpf_ptp.h
new file mode 100644
index 0000000000..0c901440fb
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#ifndef _IDPF_PTP_H_
+#define _IDPF_PTP_H_
+
+#include "base/idpf_osdep.h"
+#include <rte_time.h>
+#include "idpf_common_device.h"
+
+struct idpf_ptp_cmd {
+ uint32_t exec_cmd_mask;
+ uint32_t shtime_enable_mask;
+};
+
+struct idpf_ptp_dev_clk_regs {
+ volatile uint32_t *dev_clk_ns_l;
+ volatile uint32_t *dev_clk_ns_h;
+ volatile uint32_t *phy_clk_ns_l;
+ volatile uint32_t *phy_clk_ns_h;
+ volatile uint32_t *sys_time_ns_l;
+ volatile uint32_t *sys_time_ns_h;
+ volatile uint32_t *incval_l;
+ volatile uint32_t *incval_h;
+ volatile uint32_t *shadj_l;
+ volatile uint32_t *shadj_h;
+ volatile uint32_t *phy_incval_l;
+ volatile uint32_t *phy_incval_h;
+ volatile uint32_t *phy_shadj_l;
+ volatile uint32_t *phy_shadj_h;
+ volatile uint32_t *cmd;
+ volatile uint32_t *phy_cmd;
+ volatile uint32_t *cmd_sync;
+};
+
+enum idpf_ptp_access {
+ IDPF_PTP_NONE = 0,
+ IDPF_PTP_DIRECT,
+ IDPF_PTP_MAILBOX,
+};
+
+struct idpf_ptp_secondary_mbx {
+ uint16_t peer_mbx_q_id;
+ uint16_t peer_id;
+ bool valid:1;
+};
+
+enum idpf_ptp_tx_tstamp_state {
+ IDPF_PTP_FREE,
+ IDPF_PTP_REQUEST,
+ IDPF_PTP_READ_VALUE,
+};
+
+struct idpf_ptp_tx_tstamp {
+ uint64_t tstamp;
+ uint32_t tx_latch_reg_offset_l;
+ uint32_t tx_latch_reg_offset_h;
+ uint32_t idx;
+};
+
+struct idpf_ptp_vport_tx_tstamp_caps {
+ uint32_t vport_id;
+ uint16_t num_entries;
+ uint16_t tstamp_ns_lo_bit;
+ uint16_t latched_idx;
+ bool access:1;
+ struct idpf_ptp_tx_tstamp tx_tstamp[];
+};
+
+struct idpf_ptp {
+ uint64_t base_incval;
+ uint64_t max_adj;
+ struct idpf_ptp_cmd cmd;
+ struct idpf_ptp_dev_clk_regs dev_clk_regs;
+ uint32_t caps;
+ uint8_t get_dev_clk_time_access:2;
+ uint8_t get_cross_tstamp_access:2;
+ uint8_t set_dev_clk_time_access:2;
+ uint8_t adj_dev_clk_time_access:2;
+ uint8_t tx_tstamp_access:2;
+ uint8_t rsv:6;
+ struct idpf_ptp_secondary_mbx secondary_mbx;
+};
+
+struct idpf_ptp_dev_timers {
+ uint64_t sys_time_ns;
+ uint64_t dev_clk_time_ns;
+};
+
+int idpf_ptp_get_caps(struct idpf_adapter *adapter);
+int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t *src_clk);
+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, uint64_t time);
+int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t incval);
+int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, int64_t delta);
+int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport);
+int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
+
+static inline uint64_t
+idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
+ bool is_rx, uint32_t in_timestamp)
+{
+ const uint64_t mask = 0xFFFFFFFFULL;
+ uint32_t phc_time_lo, delta;
+ uint64_t ns;
+
+ if (flag != 0)
+ idpf_ptp_read_src_clk_reg(ad, &ad->time_hw);
+
+ phc_time_lo = (uint32_t)(ad->time_hw);
+ delta = in_timestamp - phc_time_lo;
+
+ if (delta > mask / 2) {
+ delta = phc_time_lo - in_timestamp;
+ ns = ad->time_hw - delta;
+ } else {
+ if (is_rx)
+ ns = ad->time_hw - delta;
+ else
+ ns = ad->time_hw + delta;
+ }
+
+ return ns;
+}
+
+#endif /* _IDPF_PTP_H_ */
diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
index a805d02ea2..740c1a5985 100644
--- a/drivers/net/intel/idpf/meson.build
+++ b/drivers/net/intel/idpf/meson.build
@@ -17,6 +17,7 @@ sources += files(
'idpf_common_virtchnl.c',
'idpf_ethdev.c',
+ 'idpf_ptp.c',
'idpf_rxtx.c',
)
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v6 3/4] net/idpf: add timesync support
2026-02-14 19:49 ` [PATCH v6 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-02-14 19:49 ` [PATCH v6 1/4] net/idpf/base: add a new API for PTP support Soumyadeep Hore
2026-02-14 19:49 ` [PATCH v6 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
@ 2026-02-14 19:49 ` Soumyadeep Hore
2026-02-26 10:32 ` Kumar, Rajesh3
2026-02-14 19:49 ` [PATCH v6 4/4] doc/guides: add PTP IDPF documentation Soumyadeep Hore
3 siblings, 1 reply; 57+ messages in thread
From: Soumyadeep Hore @ 2026-02-14 19:49 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Add timesync support including:
- Capability negotiation with the Control Plane
to determine supported PTP features.
- Get/set/adjust operations on the main device
clock.
- Tx timestamping support using a PTP-dedicated
secondary mailbox.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 121 +++++-----
drivers/net/intel/idpf/idpf_common_rxtx.h | 7 +
drivers/net/intel/idpf/idpf_ethdev.c | 270 ++++++++++++++++++++++
4 files changed, 338 insertions(+), 61 deletions(-)
diff --git a/drivers/net/intel/common/tx.h b/drivers/net/intel/common/tx.h
index e295d83e3a..dc964f3dd1 100644
--- a/drivers/net/intel/common/tx.h
+++ b/drivers/net/intel/common/tx.h
@@ -115,6 +115,7 @@ struct ci_tx_queue {
struct ci_tx_queue *complq;
void **txqs; /*only valid for split queue mode*/
uint32_t tx_start_qid;
+ uint32_t latch_idx; /* Tx timestamp latch index */
uint16_t sw_nb_desc;
uint16_t sw_tail;
uint16_t rs_compl_count;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.c b/drivers/net/intel/idpf/idpf_common_rxtx.c
index 797ee515dd..11c9dfc4fc 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.c
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.c
@@ -9,6 +9,7 @@
#include "idpf_common_rxtx.h"
#include "idpf_common_device.h"
#include "../common/rx.h"
+#include "idpf_ptp.h"
int idpf_timestamp_dynfield_offset = -1;
uint64_t idpf_timestamp_dynflag;
@@ -435,58 +436,6 @@ idpf_qc_split_rxq_mbufs_alloc(struct idpf_rx_queue *rxq)
return 0;
}
-#define IDPF_TIMESYNC_REG_WRAP_GUARD_BAND 10000
-/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
-static inline uint64_t
-idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
- uint32_t in_timestamp)
-{
-#ifdef RTE_ARCH_X86_64
- struct idpf_hw *hw = &ad->hw;
- const uint64_t mask = 0xFFFFFFFF;
- uint32_t hi, lo, lo2, delta;
- uint64_t ns;
-
- if (flag != 0) {
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_EXEC_CMD_M |
- PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- /*
- * On typical system, the delta between lo and lo2 is ~1000ns,
- * so 10000 seems a large-enough but not overly-big guard band.
- */
- if (lo > (UINT32_MAX - IDPF_TIMESYNC_REG_WRAP_GUARD_BAND))
- lo2 = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- else
- lo2 = lo;
-
- if (lo2 < lo) {
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- }
-
- ad->time_hw = ((uint64_t)hi << 32) | lo;
- }
-
- delta = (in_timestamp - (uint32_t)(ad->time_hw & mask));
- if (delta > (mask / 2)) {
- delta = ((uint32_t)(ad->time_hw & mask) - in_timestamp);
- ns = ad->time_hw - delta;
- } else {
- ns = ad->time_hw + delta;
- }
-
- return ns;
-#else /* !RTE_ARCH_X86_64 */
- RTE_SET_USED(ad);
- RTE_SET_USED(flag);
- RTE_SET_USED(in_timestamp);
- return 0;
-#endif /* RTE_ARCH_X86_64 */
-}
-
#define IDPF_RX_FLEX_DESC_ADV_STATUS0_XSUM_S \
(RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S) | \
RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S) | \
@@ -732,20 +681,27 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(rte_le_to_cpu_16(rx_desc->ptype_err_fflags0) &
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M) >>
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_S];
+
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
status_err0_qw1 = rte_le_to_cpu_16(rx_desc->status_err0_qw1);
pkt_flags = idpf_splitq_rx_csum_offload(status_err0_qw1);
pkt_flags |= idpf_splitq_rx_rss_offload(rxm, rx_desc);
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP)) {
/* timestamp */
+ rxq->time_high = rte_le_to_cpu_32(rx_desc->ts_high);
ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rx_desc->ts_high));
+ rxq->hw_register_set, true,
+ rxq->time_high);
rxq->hw_register_set = 0;
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ if (rx_desc->ts_low & VIRTCHNL2_RX_FLEX_TSTAMP_VALID)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -839,10 +795,34 @@ idpf_split_tx_free(struct ci_tx_queue *cq)
static inline uint16_t
idpf_calc_context_desc(uint64_t flags)
{
- if ((flags & RTE_MBUF_F_TX_TCP_SEG) != 0)
- return 1;
+ static uint64_t mask = RTE_MBUF_F_TX_TCP_SEG |
+ RTE_MBUF_F_TX_IEEE1588_TMST;
- return 0;
+ return (flags & mask) ? 1 : 0;
+}
+
+/**
+ * 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 inline void
+idpf_tx_set_tstamp_desc(volatile union idpf_flex_tx_ctx_desc *ctx_desc,
+ uint32_t idx)
+{
+ ctx_desc->tsyn.qw1.cmd_dtype =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_CMD_M,
+ IDPF_TX_CTX_DESC_TSYN));
+ ctx_desc->tsyn.qw1.cmd_dtype |=
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_DTYPE_M,
+ IDPF_TX_DESC_DTYPE_CTX));
+ ctx_desc->tsyn.qw1.tsyn_reg_l =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_L_M,
+ idx));
+ ctx_desc->tsyn.qw1.tsyn_reg_h =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_H_M,
+ idx >> 2));
}
/* set TSO context descriptor
@@ -948,6 +928,9 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_desc);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_desc, txq->latch_idx);
+
tx_id++;
if (tx_id == txq->nb_tx_desc)
tx_id = 0;
@@ -1167,17 +1150,22 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rxm->ol_flags |= pkt_flags;
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
+ rxq->hw_register_set, true,
+ rxq->time_high);
rxq->hw_register_set = 0;
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
rxm->ol_flags |= idpf_timestamp_dynflag;
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
rx_pkts[nb_rx++] = rxm;
@@ -1218,6 +1206,9 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
if (unlikely(!rxq) || unlikely(!rxq->q_started))
return nb_rx;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
+ rxq->hw_register_set = 1;
+
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
rx_status0 = rte_le_to_cpu_16(rxdp->flex_nic_wb.status_error0);
@@ -1298,17 +1289,22 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(uint8_t)(rte_cpu_to_le_16(rxd.flex_nic_wb.ptype_flex_flags0) &
VIRTCHNL2_RX_FLEX_DESC_PTYPE_M)];
+ if ((first_seg->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
+ rxq->hw_register_set, true,
+ rxq->time_high);
rxq->hw_register_set = 0;
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -1474,6 +1470,9 @@ idpf_dp_singleq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_txd);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_txd, txq->latch_idx);
+
txe->last_id = tx_last;
tx_id = txe->next_id;
txe = txn;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.h b/drivers/net/intel/idpf/idpf_common_rxtx.h
index 7c6ff5d047..e1b8904181 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.h
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.h
@@ -13,6 +13,12 @@
#include "../common/tx.h"
#include "../common/rx.h"
+#define FIELD_PREP(_mask, _val) \
+ (__extension__ ({ \
+ typeof(_mask) _x = (_mask); \
+ ((typeof(_x))(_val) << rte_bsf32(_x)) & (_x); \
+ }))
+
#define IDPF_RX_MAX_BURST 32
#define IDPF_RX_OFFLOAD_IPV4_CKSUM RTE_BIT64(1)
@@ -167,6 +173,7 @@ struct idpf_rx_queue {
uint64_t offloads;
uint32_t hw_register_set;
+ uint32_t time_high; /* high 32 bits of hardware timestamp register */
};
/* Offload features */
diff --git a/drivers/net/intel/idpf/idpf_ethdev.c b/drivers/net/intel/idpf/idpf_ethdev.c
index 5d786fbba6..5e57a45775 100644
--- a/drivers/net/intel/idpf/idpf_ethdev.c
+++ b/drivers/net/intel/idpf/idpf_ethdev.c
@@ -14,6 +14,7 @@
#include "idpf_ethdev.h"
#include "idpf_rxtx.h"
#include "../common/tx.h"
+#include "idpf_ptp.h"
#define IDPF_TX_SINGLE_Q "tx_single"
#define IDPF_RX_SINGLE_Q "rx_single"
@@ -841,6 +842,267 @@ idpf_dev_close(struct rte_eth_dev *dev)
return 0;
}
+static int
+idpf_timesync_enable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct timespec sys_ts;
+ uint64_t ns;
+ int ret, q_id;
+ if (dev->data->dev_started && !(dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP)) {
+ PMD_DRV_LOG(ERR, "Rx timestamp offload not configured");
+ return -1;
+ }
+
+ adapter->ptp = rte_zmalloc(NULL, sizeof(struct idpf_ptp), 0);
+ if (adapter->ptp == NULL) {
+ PMD_DRV_LOG(ERR, "Failed to allocate memory for PTP");
+ return -ENOMEM;
+ }
+
+ ret = idpf_ptp_get_caps(adapter);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get PTP capabilities, err=%d", ret);
+ goto fail_ptp;
+ }
+
+ /* Write the default increment time value if the clock adjustments are enabled. */
+ if (adapter->ptp->adj_dev_clk_time_access != IDPF_PTP_NONE) {
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, adapter->ptp->base_incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set incval failed, err=%d", ret);
+ goto fail_ptp;
+ }
+ }
+
+ /* Do not initialize the PTP if the device clock time cannot be read. */
+ if (adapter->ptp->get_dev_clk_time_access == IDPF_PTP_NONE) {
+ PMD_DRV_LOG(ERR, "Getting device clock time is not supported");
+ ret = -EIO;
+ goto fail_ptp;
+ }
+
+ /* Set the device clock time to system time. */
+ if (adapter->ptp->set_dev_clk_time_access != IDPF_PTP_NONE) {
+ clock_gettime(CLOCK_REALTIME, &sys_ts);
+ ns = rte_timespec_to_ns(&sys_ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err=%d", ret);
+ goto fail_ptp;
+ }
+ }
+
+ ret = idpf_ptp_get_vport_tstamps_caps(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get vport timestamp capabilities, err=%d", ret);
+ goto fail_ptp;
+ }
+
+ for (q_id = 0; q_id < dev->data->nb_tx_queues; q_id++) {
+ struct ci_tx_queue *txq = dev->data->tx_queues[q_id];
+ txq->latch_idx = vport->tx_tstamp_caps->tx_tstamp[q_id].idx;
+ }
+
+ 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;
+
+ return 0;
+
+fail_ptp:
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ return ret;
+}
+
+static int
+idpf_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp,
+ uint32_t flags)
+{
+ struct idpf_rx_queue *rxq;
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ts_ns;
+
+ rxq = dev->data->rx_queues[flags];
+
+ ts_ns = idpf_tstamp_convert_32b_64b(adapter, 1, true, rxq->time_high);
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ return 0;
+}
+
+static int
+idpf_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ uint16_t latch_idx;
+ uint64_t ts_ns, tstamp;
+ int ret;
+
+ ret = idpf_ptp_get_tx_tstamp(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get TX timestamp");
+ return ret;
+ }
+
+ latch_idx = vport->tx_tstamp_caps->latched_idx;
+ tstamp = vport->tx_tstamp_caps->tx_tstamp[latch_idx].tstamp;
+ ts_ns = idpf_tstamp_convert_32b_64b(vport->adapter, 0, false, tstamp);
+
+ /* Convert to timespec */
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ vport->tx_tstamp_caps->latched_idx = -1;
+
+ return 0;
+}
+
+static int
+idpf_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint64_t time, ns;
+ int ret;
+
+ if (ptp->adj_dev_clk_time_access != IDPF_PTP_MAILBOX) {
+ PMD_DRV_LOG(ERR, "Adjusting device clock time is not supported");
+ return -ENOTSUP;
+ }
+
+ if (delta > INT32_MAX || delta < INT32_MIN) {
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP read clock time failed, err %d", ret);
+ return ret;
+ }
+
+ ns = time + delta;
+
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err %d", ret);
+
+ return ret;
+ }
+
+ ret = idpf_ptp_adj_dev_clk_time(adapter, delta);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP adjusting clock failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ int64_t incval, diff = 0;
+ bool negative = false;
+ uint64_t div, rem;
+ uint64_t divisor = 1000000ULL << 16;
+ int shift;
+ int ret;
+
+ incval = ptp->base_incval;
+
+ if (ppm < 0) {
+ negative = true;
+ ppm = -ppm;
+ }
+
+ /* can incval * ppm overflow ? */
+ if (rte_log2_u64(incval) + rte_log2_u64(ppm) > 62) {
+ rem = ppm % divisor;
+ div = ppm / divisor;
+ diff = div * incval;
+ ppm = rem;
+
+ shift = rte_log2_u64(incval) + rte_log2_u64(ppm) - 62;
+ if (shift > 0) {
+ /* drop precision */
+ ppm >>= shift;
+ divisor >>= shift;
+ }
+ }
+
+ if (divisor)
+ diff = diff + incval * ppm / divisor;
+
+ if (negative)
+ incval -= diff;
+ else
+ incval += diff;
+
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int
+idpf_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ns;
+ int ret;
+
+ ns = rte_timespec_to_ns(ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP write time failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t time;
+ int ret;
+
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP read time failed, err %d", ret);
+ else
+ *ts = rte_ns_to_timespec(time);
+
+ return ret;
+}
+
+static int
+idpf_timesync_disable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+
+ if (vport->tx_tstamp_caps) {
+ rte_free(vport->tx_tstamp_caps);
+ vport->tx_tstamp_caps = NULL;
+ }
+
+ if (adapter->ptp) {
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ }
+
+ return 0;
+}
+
+
static const struct eth_dev_ops idpf_eth_dev_ops = {
.dev_configure = idpf_dev_configure,
.dev_close = idpf_dev_close,
@@ -867,6 +1129,14 @@ static const struct eth_dev_ops idpf_eth_dev_ops = {
.xstats_get = idpf_dev_xstats_get,
.xstats_get_names = idpf_dev_xstats_get_names,
.xstats_reset = idpf_dev_xstats_reset,
+ .timesync_enable = idpf_timesync_enable,
+ .timesync_read_rx_timestamp = idpf_timesync_read_rx_timestamp,
+ .timesync_read_tx_timestamp = idpf_timesync_read_tx_timestamp,
+ .timesync_adjust_time = idpf_timesync_adjust_time,
+ .timesync_adjust_freq = idpf_timesync_adjust_freq,
+ .timesync_read_time = idpf_timesync_read_time,
+ .timesync_write_time = idpf_timesync_write_time,
+ .timesync_disable = idpf_timesync_disable,
};
static int
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v6 4/4] doc/guides: add PTP IDPF documentation
2026-02-14 19:49 ` [PATCH v6 0/4] Enable PTP feature for MEV Soumyadeep Hore
` (2 preceding siblings ...)
2026-02-14 19:49 ` [PATCH v6 3/4] net/idpf: add timesync support Soumyadeep Hore
@ 2026-02-14 19:49 ` Soumyadeep Hore
2026-02-26 10:39 ` Kumar, Rajesh3
2026-02-27 0:33 ` [PATCH v7 0/4] Enable PTP feature for MEV Soumyadeep Hore
3 siblings, 2 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-02-14 19:49 UTC (permalink / raw)
To: dev, bruce.richardson
Cc: rajesh3.kumar, aman.deep.singh, manoj.kumar.subbarao
Updates the IDPF Documentation with PTP feature.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
doc/guides/nics/features/idpf.ini | 1 +
doc/guides/nics/idpf.rst | 15 +++++++++++++++
doc/guides/rel_notes/release_26_03.rst | 4 ++++
3 files changed, 20 insertions(+)
diff --git a/doc/guides/nics/features/idpf.ini b/doc/guides/nics/features/idpf.ini
index 63b799289a..f546648924 100644
--- a/doc/guides/nics/features/idpf.ini
+++ b/doc/guides/nics/features/idpf.ini
@@ -19,6 +19,7 @@ RSS reta update = Y
L3 checksum offload = P
L4 checksum offload = P
Timestamp offload = P
+Timesync = Y
Packet type parsing = Y
Basic stats = Y
Linux = Y
diff --git a/doc/guides/nics/idpf.rst b/doc/guides/nics/idpf.rst
index b99cc18188..998c1a5dfa 100644
--- a/doc/guides/nics/idpf.rst
+++ b/doc/guides/nics/idpf.rst
@@ -109,3 +109,18 @@ The paths are chosen based on 2 conditions:
A value "P" means the offload feature is not supported by vector path.
If any not supported features are used, idpf vector PMD is disabled
and the scalar paths are chosen.
+
+ Time Synchronization
+ ~~~~~~~~~~~~~~~~~~~~
+
+ The system operator can run a PTP (Precision Time Protocol) client application
+ to synchronize the time on the network card in ACC
+ (and optionally the time on the system) to the PTP primary.
+
+ IDPF PMD supports PTP client applications that use the DPDK IEEE 1588 API
+ to communicate with the PTP primary clock.
+ Note that PTP client application needs to run on PF in ACC.
+
+ .. code-block:: console
+
+ examples/dpdk-ptpclient -l 0-3 -n 3 -a 0000:ec:00.1 -- -T 1 -p 0x1
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index afdf1af06c..16b5a0b0b1 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -63,6 +63,10 @@ New Features
* The timestamp value has been updated to make it usable.
+* **Updated Intel idpf ethernet driver.**
+
+ * Added support for time sync features.
+
* **Updated Intel iavf driver.**
* Added support for pre and post VF reset callbacks.
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* RE: [PATCH v6 1/4] net/idpf/base: add a new API for PTP support
2026-02-14 19:49 ` [PATCH v6 1/4] net/idpf/base: add a new API for PTP support Soumyadeep Hore
@ 2026-02-26 9:57 ` Kumar, Rajesh3
0 siblings, 0 replies; 57+ messages in thread
From: Kumar, Rajesh3 @ 2026-02-26 9:57 UTC (permalink / raw)
To: Hore, Soumyadeep, dev@dpdk.org, Richardson, Bruce
Cc: Singh, Aman Deep, Subbarao, Manoj Kumar, Olech, Milena
Acked-by: Rajesh Kumar <rajesh3.kumar@intel.com>
-----Original Message-----
From: Hore, Soumyadeep <soumyadeep.hore@intel.com>
Sent: 15 February 2026 01:19 AM
To: dev@dpdk.org; Richardson, Bruce <bruce.richardson@intel.com>
Cc: Kumar, Rajesh3 <rajesh3.kumar@intel.com>; Singh, Aman Deep <aman.deep.singh@intel.com>; Subbarao, Manoj Kumar <manoj.kumar.subbarao@intel.com>; Olech, Milena <milena.olech@intel.com>
Subject: [PATCH v6 1/4] net/idpf/base: add a new API for PTP support
From: Milena Olech <milena.olech@intel.com>
Add PTP capabilities to determine which PTP features are enabled - get/set the time, cross timestamp, adjust the clock and timestamping.
Create appropriate structures and opcodes to support features introduced by capabilities.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
---
drivers/net/intel/idpf/base/virtchnl2.h | 324 +++++++++++++++++-------
1 file changed, 235 insertions(+), 89 deletions(-)
diff --git a/drivers/net/intel/idpf/base/virtchnl2.h b/drivers/net/intel/idpf/base/virtchnl2.h
index 6cfb4f56fa..0b01daee30 100644
--- a/drivers/net/intel/idpf/base/virtchnl2.h
+++ b/drivers/net/intel/idpf/base/virtchnl2.h
@@ -118,8 +118,14 @@ enum virtchnl2_op {
VIRTCHNL2_OP_DEL_QUEUE_GROUPS = 539,
VIRTCHNL2_OP_GET_PORT_STATS = 540,
/* TimeSync opcodes */
- VIRTCHNL2_OP_GET_PTP_CAPS = 541,
- VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES = 542,
+ 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,
};
#define VIRTCHNL2_RDMA_INVALID_QUEUE_IDX 0xFFFF
@@ -1813,123 +1819,180 @@ VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_promisc_info);
* enum virtchnl2_ptp_caps - PTP capabilities
*/
enum virtchnl2_ptp_caps {
- VIRTCHNL2_PTP_CAP_LEGACY_CROSS_TIME = BIT(0),
- VIRTCHNL2_PTP_CAP_PTM = BIT(1),
- VIRTCHNL2_PTP_CAP_DEVICE_CLOCK_CONTROL = BIT(2),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_DIRECT = BIT(3),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_VIRTCHNL = BIT(4),
-};
-
-/**
- * struct virtchnl2_ptp_legacy_cross_time_reg - Legacy cross time registers
- * offsets.
+ 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_legacy_cross_time_reg {
- __le32 shadow_time_0;
- __le32 shadow_time_l;
- __le32 shadow_time_h;
- __le32 cmd_sync;
+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(16, virtchnl2_ptp_legacy_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_clk_reg_offsets);
/**
- * struct virtchnl2_ptp_ptm_cross_time_reg - PTM cross time registers 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_ptm_cross_time_reg {
- __le32 art_l;
- __le32 art_h;
- __le32 cmd_sync;
+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_ptm_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_cross_time_reg_offsets);
/**
- * struct virtchnl2_ptp_device_clock_control - Registers needed to control the
- * main clock.
+ * 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_device_clock_control {
- __le32 cmd;
- __le32 incval_l;
- __le32 incval_h;
- __le32 shadj_l;
- __le32 shadj_h;
- u8 pad[4];
+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(24, virtchnl2_ptp_device_clock_control);
+VIRTCHNL2_CHECK_STRUCT_LEN(40, virtchnl2_ptp_clk_adj_reg_offsets);
/**
- * struct virtchnl2_ptp_tx_tstamp_entry - PTP TX timestamp entry
- * @tx_latch_register_base: TX latch register base
- * @tx_latch_register_offset: TX latch register offset
- * @index: Index
- * @pad: Padding
+ * 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_entry {
- __le32 tx_latch_register_base;
- __le32 tx_latch_register_offset;
+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_entry);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch_caps);
/**
- * struct virtchnl2_ptp_tx_tstamp - Structure that defines tx tstamp entries
+ * struct virtchnl2_ptp_get_vport_tx_tstamp_caps - Structure that defines Tx
+ * tstamp entries
+ * @vport_id: Vport number
* @num_latches: Total number of latches
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
- * @ptp_tx_tstamp_entries: Aarray of TX timestamp entries
+ * @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_tx_tstamp {
+struct virtchnl2_ptp_get_vport_tx_tstamp_caps {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
- struct virtchnl2_ptp_tx_tstamp_entry ptp_tx_tstamp_entries[STRUCT_VAR_LEN];
+ u8 tstamp_ns_lo_bit;
+ u8 tstamp_ns_hi_bit;
+ u8 pad[8];
+
+ struct virtchnl2_ptp_tx_tstamp_latch_caps
+tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp,
- ptp_tx_tstamp_entries);
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_caps,
+ tstamp_latches);
/**
- * struct virtchnl2_get_ptp_caps - Get PTP capabilities
- * @ptp_caps: PTP capability bitmap. See enum virtchnl2_ptp_caps.
- * @pad: Padding
- * @legacy_cross_time_reg: Legacy cross time register
- * @ptm_cross_time_reg: PTM cross time register
- * @device_clock_control: Device clock control
- * @tx_tstamp: TX timestamp
+ * 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
*
- * PV/VF sends this message to negotiate PTP capabilities. CP updates bitmap
+ * PF/VF sends this message to negotiate PTP capabilities. CP updates
+ bitmap
* with supported features and fulfills appropriate structures.
- *
- * Associated with VIRTCHNL2_OP_GET_PTP_CAPS.
- */
-struct virtchnl2_get_ptp_caps {
- __le32 ptp_caps;
+ * 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_legacy_cross_time_reg legacy_cross_time_reg;
- struct virtchnl2_ptp_ptm_cross_time_reg ptm_cross_time_reg;
- struct virtchnl2_ptp_device_clock_control device_clock_control;
- struct virtchnl2_ptp_tx_tstamp tx_tstamp;
+ 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_VAR_LEN(88, virtchnl2_get_ptp_caps,
- tx_tstamp.ptp_tx_tstamp_entries);
+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_h: Timestamp high
- * @tstamp_l: Timestamp low
+ * @tstamp: Timestamp value
* @index: Index
* @valid: Timestamp validity
- * @pad: Padding
+ * @pad: Padding for future extensions
*/
struct virtchnl2_ptp_tx_tstamp_latch {
- __le32 tstamp_h;
- __le32 tstamp_l;
+ __le64 tstamp;
u8 index;
u8 valid;
u8 pad[6];
@@ -1938,26 +2001,97 @@ struct virtchnl2_ptp_tx_tstamp_latch { VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch);
/**
- * struct virtchnl2_ptp_tx_tstamp_latches - PTP TX timestamp latches
+ * 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
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
+ * @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_GET_PTP_TX_TSTAMP_LATCHES.
+ * Associated with VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP.
*/
-struct virtchnl2_ptp_tx_tstamp_latches {
+struct virtchnl2_ptp_get_vport_tx_tstamp_latches {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
+ u8 get_devtime_with_txtstmp;
+ u8 pad[1];
+ u64 device_time;
struct virtchnl2_ptp_tx_tstamp_latch tstamp_latches[STRUCT_VAR_LEN]; }; -VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp_latches,
+
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32,
+virtchnl2_ptp_get_vport_tx_tstamp_latches,
tstamp_latches);
+/* VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME
+ * @dev_time_ns: Device clock time value in nanoseconds
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the time from the main timer */
+struct virtchnl2_ptp_get_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_dev_clk_time);
+
+/* 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
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the cross time */ struct
+virtchnl2_ptp_get_cross_time {
+ __le64 sys_time_ns;
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_get_cross_time);
+
+/* VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME
+ * @dev_time_ns: Device time value expressed in nanoseconds to set
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to set the time of the main timer */
+struct virtchnl2_ptp_set_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_set_dev_clk_time);
+
+/* 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);
+
+/* 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);
+
static inline const char *virtchnl2_op_str(__le32 v_opcode) {
switch (v_opcode) {
@@ -2025,10 +2159,22 @@ static inline const char *virtchnl2_op_str(__le32 v_opcode)
return "VIRTCHNL2_OP_DEL_QUEUE_GROUPS";
case VIRTCHNL2_OP_GET_PORT_STATS:
return "VIRTCHNL2_OP_GET_PORT_STATS";
- case VIRTCHNL2_OP_GET_PTP_CAPS:
- return "VIRTCHNL2_OP_GET_PTP_CAPS";
- case VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES:
- return "VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES";
+ case VIRTCHNL2_OP_PTP_GET_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_CAPS";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP";
+ case VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_CROSS_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_CROSS_TIME";
+ case VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS";
default:
return "Unsupported (update virtchnl2.h)";
}
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* RE: [PATCH v6 2/4] net/idpf: add PTP virtchnl2 support
2026-02-14 19:49 ` [PATCH v6 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
@ 2026-02-26 10:15 ` Kumar, Rajesh3
2026-02-26 11:12 ` Hore, Soumyadeep
0 siblings, 1 reply; 57+ messages in thread
From: Kumar, Rajesh3 @ 2026-02-26 10:15 UTC (permalink / raw)
To: Hore, Soumyadeep, dev@dpdk.org, Richardson, Bruce
Cc: Singh, Aman Deep, Subbarao, Manoj Kumar
-----Original Message-----
From: Hore, Soumyadeep <soumyadeep.hore@intel.com>
Sent: 15 February 2026 01:19 AM
To: dev@dpdk.org; Richardson, Bruce <bruce.richardson@intel.com>
Cc: Kumar, Rajesh3 <rajesh3.kumar@intel.com>; Singh, Aman Deep <aman.deep.singh@intel.com>; Subbarao, Manoj Kumar <manoj.kumar.subbarao@intel.com>
Subject: [PATCH v6 2/4] net/idpf: add PTP virtchnl2 support
Add a new file - idpf_ptp - to handle PTP virtchnl messages.
Keep the registers addresses in the PTP struct.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 31 +-
drivers/net/intel/idpf/idpf_ptp.c | 504 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 130 +++++
drivers/net/intel/idpf/meson.build | 1 +
5 files changed, 668 insertions(+), 2 deletions(-) create mode 100644 drivers/net/intel/idpf/idpf_ptp.c create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
diff --git a/drivers/net/intel/idpf/idpf_common_device.h b/drivers/net/intel/idpf/idpf_common_device.h
index 31915a03d4..e45795c18b 100644
--- a/drivers/net/intel/idpf/idpf_common_device.h
+++ b/drivers/net/intel/idpf/idpf_common_device.h
@@ -103,6 +103,7 @@ struct idpf_adapter {
/* For timestamp */
uint64_t time_hw;
+ struct idpf_ptp *ptp;
enum idpf_rx_func_type rx_func_type;
enum idpf_tx_func_type tx_func_type;
@@ -173,6 +174,9 @@ struct idpf_vport {
/* Event from ipf */
bool link_up;
uint32_t link_speed;
+
+ /* For PTP */
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
};
/* Message type read in virtual channel from PF */ diff --git a/drivers/net/intel/idpf/idpf_common_virtchnl.c b/drivers/net/intel/idpf/idpf_common_virtchnl.c
index e927d7415a..f8c487c537 100644
--- a/drivers/net/intel/idpf/idpf_common_virtchnl.c
+++ b/drivers/net/intel/idpf/idpf_common_virtchnl.c
@@ -4,6 +4,7 @@
#include "idpf_common_virtchnl.h"
#include "idpf_common_logs.h"
+#include "idpf_ptp.h"
#include <eal_export.h>
@@ -38,6 +39,24 @@ idpf_vc_clean(struct idpf_adapter *adapter)
return 0;
}
+ /* Helper to detect PTP-related mailbox messages */ static inline bool
+idpf_mb_msg_is_ptp(uint32_t op) {
+ switch (op) {
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ 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:
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int
idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
uint16_t msg_size, uint8_t *msg)
@@ -71,8 +90,16 @@ idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
memcpy(dma_mem->va, msg, msg_size);
- ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
- ctlq_msg->func_id = 0;
+ if (adapter->ptp != NULL &&
+ idpf_mb_msg_is_ptp(op) &&
+ adapter->ptp->secondary_mbx.valid) {
+ 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 {
+ ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
+ ctlq_msg->func_id = 0;
+ }
ctlq_msg->data_len = msg_size;
ctlq_msg->cookie.mbx.chnl_opcode = op;
ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS; diff --git a/drivers/net/intel/idpf/idpf_ptp.c b/drivers/net/intel/idpf/idpf_ptp.c
new file mode 100644
index 0000000000..aefd1309f7
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.c
@@ -0,0 +1,504 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#include "idpf_ptp.h"
+#include "idpf_common_virtchnl.h"
+#include "base/virtchnl2.h"
+
+static enum idpf_ptp_access
+idpf_ptp_get_access(const struct idpf_adapter *adapter, uint32_t direct,
+ uint32_t 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;
+}
+
+#define IDPF_PTP_ACCESS(adapter, cap) \
+ ((uint8_t)idpf_ptp_get_access((adapter), (cap), (cap##_MB)))
+
+static void
+idpf_ptp_get_features_access(const struct idpf_adapter *adapter) {
+ struct idpf_ptp *ptp = adapter->ptp;
+
+ ptp->get_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME);
+ ptp->get_cross_tstamp_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_GET_CROSS_TIME);
+ ptp->set_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME);
+ ptp->adj_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK);
+ ptp->tx_tstamp_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_TX_TSTAMPS); }
+
+int
+idpf_ptp_get_caps(struct idpf_adapter *adapter) {
+ struct virtchnl2_ptp_cross_time_reg_offsets cross_tstamp_offsets;
+ struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
+ struct virtchnl2_ptp_get_caps send_ptp_caps_msg = {0};
+ struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
+ struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
+ struct idpf_cmd_info args = {0};
+ struct idpf_ptp_secondary_mbx *scnd_mbx;
+ struct idpf_ptp *ptp = adapter->ptp;
+ struct idpf_hw *hw = &adapter->hw;
+ enum idpf_ptp_access access_type;
+ int err;
+ uint32_t temp_offset;
+
+ 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 |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CAPS;
+ args.in_args = (uint8_t *)&send_ptp_caps_msg;
+ args.in_args_size = sizeof(send_ptp_caps_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(*recv_ptp_caps_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
+ 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);
+ scnd_mbx->valid = scnd_mbx->peer_mbx_q_id != 0xffff;
+ if (scnd_mbx->valid)
+ scnd_mbx->peer_id = recv_ptp_caps_msg->peer_id;
+
+ idpf_ptp_get_features_access(adapter);
+
+ access_type = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.dev_clk_ns_h);
+ ptp->dev_clk_regs.dev_clk_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_l);
+ ptp->dev_clk_regs.phy_clk_ns_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_h);
+ ptp->dev_clk_regs.phy_clk_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ access_type = (enum idpf_ptp_access)ptp->get_cross_tstamp_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.sys_time_ns_h);
+ ptp->dev_clk_regs.sys_time_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ access_type = (enum idpf_ptp_access)ptp->adj_dev_clk_time_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ clk_adj_offsets = recv_ptp_caps_msg->clk_adj_offsets;
+
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_cmd_type);
+ ptp->dev_clk_regs.cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_l);
+ ptp->dev_clk_regs.incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_h);
+ ptp->dev_clk_regs.incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_l);
+ ptp->dev_clk_regs.shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_h);
+ ptp->dev_clk_regs.shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_cmd_type);
+ ptp->dev_clk_regs.phy_cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_l);
+ ptp->dev_clk_regs.phy_incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_h);
+ ptp->dev_clk_regs.phy_incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_l);
+ ptp->dev_clk_regs.phy_shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_h);
+ ptp->dev_clk_regs.phy_shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ return err;
+}
+
+static void
+idpf_ptp_enable_shtime(struct idpf_adapter *adapter) {
+ uint32_t shtime_enable, exec_cmd;
+
+ shtime_enable = adapter->ptp->cmd.shtime_enable_mask;
+ exec_cmd = adapter->ptp->cmd.exec_cmd_mask;
+
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, shtime_enable);
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync,
+ exec_cmd | shtime_enable);
+}
+
+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 = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+ uint64_t dev_time;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&get_dev_clk_time_msg;
+ args.in_args_size = sizeof(get_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(get_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ get_dev_clk_time_msg =
+ *(struct virtchnl2_ptp_get_dev_clk_time *)args.out_buffer;
+ dev_time = LE64_TO_CPU(get_dev_clk_time_msg.dev_time_ns);
+ dev_clk_time->dev_clk_time_ns = dev_time;
+
+ return err;
+}
+
+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 = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CROSS_TIME;
+ args.in_args = (uint8_t *)&cross_time_msg;
+ args.in_args_size = sizeof(cross_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(cross_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ cross_time_msg =
+ *(struct virtchnl2_ptp_get_cross_time *)args.out_buffer;
+ 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 err;
+}
+
+int
+idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, uint64_t time)
+{
+ struct virtchnl2_ptp_set_dev_clk_time set_dev_clk_time_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ set_dev_clk_time_msg.dev_time_ns = CPU_TO_LE64(time);
+
+ args.ops = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&set_dev_clk_time_msg;
+ args.in_args_size = sizeof(set_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(set_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, int64_t delta)
+{
+ struct virtchnl2_ptp_adj_dev_clk_time adj_dev_clk_time_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ adj_dev_clk_time_msg.delta = CPU_TO_LE64(delta);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&adj_dev_clk_time_msg;
+ args.in_args_size = sizeof(adj_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t
+incval) {
+ struct virtchnl2_ptp_adj_dev_clk_fine adj_dev_clk_fine_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ adj_dev_clk_fine_msg.incval = CPU_TO_LE64(incval);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE;
+ args.in_args = (uint8_t *)&adj_dev_clk_fine_msg;
+ args.in_args_size = sizeof(adj_dev_clk_fine_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_fine_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport) {
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps send_tx_tstamp_caps = {0};
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps *rcv_tx_tstamp_caps;
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
+ enum idpf_ptp_access tstamp_access, get_dev_clk_access;
+ struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
+ struct idpf_ptp *ptp = vport->adapter->ptp;
+ struct idpf_cmd_info args = {0};
+ int err;
+ uint16_t num_latches, i;
Instead of using i, Can we use something like latch_itr ?
+ uint32_t size;
+
+ if (ptp == NULL)
+ return -EOPNOTSUPP;
+
+ tstamp_access = (enum idpf_ptp_access)ptp->tx_tstamp_access;
+ get_dev_clk_access = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (tstamp_access == IDPF_PTP_NONE ||
+ get_dev_clk_access == IDPF_PTP_NONE)
+ return -EOPNOTSUPP;
+
+ send_tx_tstamp_caps.vport_id = CPU_TO_LE32(vport->vport_id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS;
+ args.in_args = (uint8_t *)&send_tx_tstamp_caps;
+ args.in_args_size = sizeof(send_tx_tstamp_caps);
+ args.out_size = IDPF_CTLQ_MAX_BUF_LEN;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ if (err < 0)
+ return err;
+
+ rcv_tx_tstamp_caps =
+ (struct virtchnl2_ptp_get_vport_tx_tstamp_caps *)args.out_buffer;
+ num_latches = LE16_TO_CPU(rcv_tx_tstamp_caps->num_latches);
+ size = sizeof(struct idpf_ptp_vport_tx_tstamp_caps) +
+ sizeof(struct idpf_ptp_tx_tstamp) * num_latches;
+ tstamp_caps = rte_zmalloc(NULL, size, 0);
+ if (tstamp_caps == NULL)
+ return -ENOMEM;
+
+ tstamp_caps->access = true;
+ tstamp_caps->num_entries = num_latches;
+ tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ for (i = 0; i < tstamp_caps->num_entries; i++) {
+ __le32 offset_l, offset_h;
+
+ tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i];
+
+ if (tstamp_access == IDPF_PTP_DIRECT) {
+ offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
+ offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_l =
+ LE32_TO_CPU(offset_l);
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_h =
+ LE32_TO_CPU(offset_h);
+ }
+ tstamp_caps->tx_tstamp[i].idx = tx_tstamp_latch_caps.index;
+ }
+
+ tstamp_caps->latched_idx = -1;
+ vport->tx_tstamp_caps = tstamp_caps;
+
+ return err;
+}
+
+static int
+idpf_ptp_get_tstamp_value(struct idpf_vport *vport,
+ struct virtchnl2_ptp_tx_tstamp_latch *tstamp_latch,
+ struct idpf_ptp_tx_tstamp *tx_tstamp) {
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ uint8_t tstamp_ns_lo_bit;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ tx_tstamp->tstamp = LE64_TO_CPU(tstamp_latch->tstamp);
+ tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
+
+ return 0;
+}
+
+int
+idpf_ptp_get_tx_tstamp(struct idpf_vport *vport) {
+ struct virtchnl2_ptp_get_vport_tx_tstamp_latches *send_tx_tstamp_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 *ptp_tx_tstamp;
+ struct idpf_cmd_info args = {0};
+ int size, msg_size;
+ uint32_t vport_id;
+ uint16_t num_latches, id;
Instead of using id, Can we use something like latch_itr ?
+ int err;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+
+ size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) *
+ tx_tstamp_caps->num_entries;
+ send_tx_tstamp_msg = rte_zmalloc(NULL, size, 0);
+ if (send_tx_tstamp_msg == NULL)
+ return -ENOMEM;
+
+ for (id = 0; id < tx_tstamp_caps->num_entries; id++, ptp_tx_tstamp++)
+ send_tx_tstamp_msg->tstamp_latches[id].index = ptp_tx_tstamp->idx;
+ send_tx_tstamp_msg->get_devtime_with_txtstmp = 1;
+
+ msg_size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) * id;
+ send_tx_tstamp_msg->vport_id = CPU_TO_LE32(vport->vport_id);
+ send_tx_tstamp_msg->num_latches = CPU_TO_LE16(id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP;
+ args.in_args = (uint8_t *)send_tx_tstamp_msg;
+ args.in_args_size = msg_size;
+ args.out_size = msg_size;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ rte_free(send_tx_tstamp_msg);
+ if (err < 0)
+ return err;
+
+ recv_tx_tstamp_msg =
+ (struct virtchnl2_ptp_get_vport_tx_tstamp_latches *)args.out_buffer;
+ vport_id = LE32_TO_CPU(recv_tx_tstamp_msg->vport_id);
+ if (vport->vport_id != vport_id)
+ return -EINVAL;
+
+ num_latches = LE16_TO_CPU(recv_tx_tstamp_msg->num_latches);
+
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+ for (id = 0; id < num_latches; id++, ptp_tx_tstamp++) {
+ tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[id];
+
+ if (!tstamp_latch.valid)
+ continue;
+
+ err = idpf_ptp_get_tstamp_value(vport, &tstamp_latch,
+ ptp_tx_tstamp);
+ if (err == 0) {
+ tx_tstamp_caps->latched_idx = id;
+ vport->adapter->time_hw = recv_tx_tstamp_msg->device_time;
+ }
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer
+value
+ * @adapter: Driver specific private structure
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int
+idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter, uint64_t
+*src_clk) {
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint32_t hi, lo;
+
+ idpf_ptp_enable_shtime(adapter);
+
+ lo = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_l);
+ hi = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_h);
+
+ *src_clk = ((uint64_t)hi << 32) | lo;
+ return 0;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value
+through mailbox
+ * @adapter: Driver specific private structure
+ * @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,
+ uint64_t *src_clk)
+{
+ struct idpf_ptp_dev_timers clk_time;
+ int err;
+
+ err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
+ if (err)
+ return err;
+
+ *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
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int
+idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t
+*src_clk) {
+ if (adapter->ptp == NULL)
+ return -EINVAL;
+
+ switch ((enum idpf_ptp_access)adapter->ptp->get_dev_clk_time_access) {
+ case IDPF_PTP_MAILBOX:
+ return idpf_ptp_read_src_clk_reg_mailbox(adapter, src_clk);
+ case IDPF_PTP_DIRECT:
+ return idpf_ptp_read_src_clk_reg_direct(adapter, src_clk);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
diff --git a/drivers/net/intel/idpf/idpf_ptp.h b/drivers/net/intel/idpf/idpf_ptp.h
new file mode 100644
index 0000000000..0c901440fb
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#ifndef _IDPF_PTP_H_
+#define _IDPF_PTP_H_
+
+#include "base/idpf_osdep.h"
+#include <rte_time.h>
+#include "idpf_common_device.h"
+
+struct idpf_ptp_cmd {
+ uint32_t exec_cmd_mask;
+ uint32_t shtime_enable_mask;
+};
+
+struct idpf_ptp_dev_clk_regs {
+ volatile uint32_t *dev_clk_ns_l;
+ volatile uint32_t *dev_clk_ns_h;
+ volatile uint32_t *phy_clk_ns_l;
+ volatile uint32_t *phy_clk_ns_h;
+ volatile uint32_t *sys_time_ns_l;
+ volatile uint32_t *sys_time_ns_h;
+ volatile uint32_t *incval_l;
+ volatile uint32_t *incval_h;
+ volatile uint32_t *shadj_l;
+ volatile uint32_t *shadj_h;
+ volatile uint32_t *phy_incval_l;
+ volatile uint32_t *phy_incval_h;
+ volatile uint32_t *phy_shadj_l;
+ volatile uint32_t *phy_shadj_h;
+ volatile uint32_t *cmd;
+ volatile uint32_t *phy_cmd;
+ volatile uint32_t *cmd_sync;
+};
+
+enum idpf_ptp_access {
+ IDPF_PTP_NONE = 0,
+ IDPF_PTP_DIRECT,
+ IDPF_PTP_MAILBOX,
+};
+
+struct idpf_ptp_secondary_mbx {
+ uint16_t peer_mbx_q_id;
+ uint16_t peer_id;
+ bool valid:1;
+};
+
+enum idpf_ptp_tx_tstamp_state {
+ IDPF_PTP_FREE,
+ IDPF_PTP_REQUEST,
+ IDPF_PTP_READ_VALUE,
+};
+
+struct idpf_ptp_tx_tstamp {
+ uint64_t tstamp;
+ uint32_t tx_latch_reg_offset_l;
+ uint32_t tx_latch_reg_offset_h;
+ uint32_t idx;
+};
+
+struct idpf_ptp_vport_tx_tstamp_caps {
+ uint32_t vport_id;
+ uint16_t num_entries;
+ uint16_t tstamp_ns_lo_bit;
+ uint16_t latched_idx;
+ bool access:1;
+ struct idpf_ptp_tx_tstamp tx_tstamp[]; };
+
+struct idpf_ptp {
+ uint64_t base_incval;
+ uint64_t max_adj;
+ struct idpf_ptp_cmd cmd;
+ struct idpf_ptp_dev_clk_regs dev_clk_regs;
+ uint32_t caps;
+ uint8_t get_dev_clk_time_access:2;
+ uint8_t get_cross_tstamp_access:2;
+ uint8_t set_dev_clk_time_access:2;
+ uint8_t adj_dev_clk_time_access:2;
+ uint8_t tx_tstamp_access:2;
+ uint8_t rsv:6;
+ struct idpf_ptp_secondary_mbx secondary_mbx; };
+
+struct idpf_ptp_dev_timers {
+ uint64_t sys_time_ns;
+ uint64_t dev_clk_time_ns;
+};
+
+int idpf_ptp_get_caps(struct idpf_adapter *adapter); int
+idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t
+*src_clk); 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, uint64_t time);
+int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t
+incval); int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter,
+int64_t delta); int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport
+*vport); int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
+
+static inline uint64_t
+idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
+ bool is_rx, uint32_t in_timestamp) {
+ const uint64_t mask = 0xFFFFFFFFULL;
+ uint32_t phc_time_lo, delta;
+ uint64_t ns;
+
+ if (flag != 0)
+ idpf_ptp_read_src_clk_reg(ad, &ad->time_hw);
+
+ phc_time_lo = (uint32_t)(ad->time_hw);
+ delta = in_timestamp - phc_time_lo;
+
+ if (delta > mask / 2) {
+ delta = phc_time_lo - in_timestamp;
+ ns = ad->time_hw - delta;
+ } else {
+ if (is_rx)
+ ns = ad->time_hw - delta;
+ else
+ ns = ad->time_hw + delta;
+ }
+
+ return ns;
+}
+
+#endif /* _IDPF_PTP_H_ */
diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
index a805d02ea2..740c1a5985 100644
--- a/drivers/net/intel/idpf/meson.build
+++ b/drivers/net/intel/idpf/meson.build
@@ -17,6 +17,7 @@ sources += files(
'idpf_common_virtchnl.c',
'idpf_ethdev.c',
+ 'idpf_ptp.c',
'idpf_rxtx.c',
)
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* RE: [PATCH v6 3/4] net/idpf: add timesync support
2026-02-14 19:49 ` [PATCH v6 3/4] net/idpf: add timesync support Soumyadeep Hore
@ 2026-02-26 10:32 ` Kumar, Rajesh3
0 siblings, 0 replies; 57+ messages in thread
From: Kumar, Rajesh3 @ 2026-02-26 10:32 UTC (permalink / raw)
To: Hore, Soumyadeep, dev@dpdk.org, Richardson, Bruce
Cc: Singh, Aman Deep, Subbarao, Manoj Kumar
Acked-by: Rajesh Kumar <rajesh3.kumar@intel.com>
-----Original Message-----
From: Hore, Soumyadeep <soumyadeep.hore@intel.com>
Sent: 15 February 2026 01:19 AM
To: dev@dpdk.org; Richardson, Bruce <bruce.richardson@intel.com>
Cc: Kumar, Rajesh3 <rajesh3.kumar@intel.com>; Singh, Aman Deep <aman.deep.singh@intel.com>; Subbarao, Manoj Kumar <manoj.kumar.subbarao@intel.com>
Subject: [PATCH v6 3/4] net/idpf: add timesync support
Add timesync support including:
- Capability negotiation with the Control Plane to determine supported PTP features.
- Get/set/adjust operations on the main device clock.
- Tx timestamping support using a PTP-dedicated secondary mailbox.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 121 +++++-----
drivers/net/intel/idpf/idpf_common_rxtx.h | 7 +
drivers/net/intel/idpf/idpf_ethdev.c | 270 ++++++++++++++++++++++
4 files changed, 338 insertions(+), 61 deletions(-)
diff --git a/drivers/net/intel/common/tx.h b/drivers/net/intel/common/tx.h index e295d83e3a..dc964f3dd1 100644
--- a/drivers/net/intel/common/tx.h
+++ b/drivers/net/intel/common/tx.h
@@ -115,6 +115,7 @@ struct ci_tx_queue {
struct ci_tx_queue *complq;
void **txqs; /*only valid for split queue mode*/
uint32_t tx_start_qid;
+ uint32_t latch_idx; /* Tx timestamp latch index */
uint16_t sw_nb_desc;
uint16_t sw_tail;
uint16_t rs_compl_count;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.c b/drivers/net/intel/idpf/idpf_common_rxtx.c
index 797ee515dd..11c9dfc4fc 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.c
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.c
@@ -9,6 +9,7 @@
#include "idpf_common_rxtx.h"
#include "idpf_common_device.h"
#include "../common/rx.h"
+#include "idpf_ptp.h"
int idpf_timestamp_dynfield_offset = -1; uint64_t idpf_timestamp_dynflag; @@ -435,58 +436,6 @@ idpf_qc_split_rxq_mbufs_alloc(struct idpf_rx_queue *rxq)
return 0;
}
-#define IDPF_TIMESYNC_REG_WRAP_GUARD_BAND 10000
-/* Helper function to convert a 32b nanoseconds timestamp to 64b. */ -static inline uint64_t -idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
- uint32_t in_timestamp)
-{
-#ifdef RTE_ARCH_X86_64
- struct idpf_hw *hw = &ad->hw;
- const uint64_t mask = 0xFFFFFFFF;
- uint32_t hi, lo, lo2, delta;
- uint64_t ns;
-
- if (flag != 0) {
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_EXEC_CMD_M |
- PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- /*
- * On typical system, the delta between lo and lo2 is ~1000ns,
- * so 10000 seems a large-enough but not overly-big guard band.
- */
- if (lo > (UINT32_MAX - IDPF_TIMESYNC_REG_WRAP_GUARD_BAND))
- lo2 = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- else
- lo2 = lo;
-
- if (lo2 < lo) {
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- }
-
- ad->time_hw = ((uint64_t)hi << 32) | lo;
- }
-
- delta = (in_timestamp - (uint32_t)(ad->time_hw & mask));
- if (delta > (mask / 2)) {
- delta = ((uint32_t)(ad->time_hw & mask) - in_timestamp);
- ns = ad->time_hw - delta;
- } else {
- ns = ad->time_hw + delta;
- }
-
- return ns;
-#else /* !RTE_ARCH_X86_64 */
- RTE_SET_USED(ad);
- RTE_SET_USED(flag);
- RTE_SET_USED(in_timestamp);
- return 0;
-#endif /* RTE_ARCH_X86_64 */
-}
-
#define IDPF_RX_FLEX_DESC_ADV_STATUS0_XSUM_S \
(RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S) | \
RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S) | \
@@ -732,20 +681,27 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(rte_le_to_cpu_16(rx_desc->ptype_err_fflags0) &
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M) >>
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_S];
+
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
status_err0_qw1 = rte_le_to_cpu_16(rx_desc->status_err0_qw1);
pkt_flags = idpf_splitq_rx_csum_offload(status_err0_qw1);
pkt_flags |= idpf_splitq_rx_rss_offload(rxm, rx_desc);
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP)) {
/* timestamp */
+ rxq->time_high = rte_le_to_cpu_32(rx_desc->ts_high);
ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rx_desc->ts_high));
+ rxq->hw_register_set, true,
+ rxq->time_high);
rxq->hw_register_set = 0;
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ if (rx_desc->ts_low & VIRTCHNL2_RX_FLEX_TSTAMP_VALID)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -839,10 +795,34 @@ idpf_split_tx_free(struct ci_tx_queue *cq) static inline uint16_t idpf_calc_context_desc(uint64_t flags) {
- if ((flags & RTE_MBUF_F_TX_TCP_SEG) != 0)
- return 1;
+ static uint64_t mask = RTE_MBUF_F_TX_TCP_SEG |
+ RTE_MBUF_F_TX_IEEE1588_TMST;
- return 0;
+ return (flags & mask) ? 1 : 0;
+}
+
+/**
+ * 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 inline void
+idpf_tx_set_tstamp_desc(volatile union idpf_flex_tx_ctx_desc *ctx_desc,
+ uint32_t idx)
+{
+ ctx_desc->tsyn.qw1.cmd_dtype =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_CMD_M,
+ IDPF_TX_CTX_DESC_TSYN));
+ ctx_desc->tsyn.qw1.cmd_dtype |=
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_DTYPE_M,
+ IDPF_TX_DESC_DTYPE_CTX));
+ ctx_desc->tsyn.qw1.tsyn_reg_l =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_L_M,
+ idx));
+ ctx_desc->tsyn.qw1.tsyn_reg_h =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_H_M,
+ idx >> 2));
}
/* set TSO context descriptor
@@ -948,6 +928,9 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_desc);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_desc, txq->latch_idx);
+
tx_id++;
if (tx_id == txq->nb_tx_desc)
tx_id = 0;
@@ -1167,17 +1150,22 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rxm->ol_flags |= pkt_flags;
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
+ rxq->hw_register_set, true,
+ rxq->time_high);
rxq->hw_register_set = 0;
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
rxm->ol_flags |= idpf_timestamp_dynflag;
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
rx_pkts[nb_rx++] = rxm;
@@ -1218,6 +1206,9 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
if (unlikely(!rxq) || unlikely(!rxq->q_started))
return nb_rx;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
+ rxq->hw_register_set = 1;
+
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
rx_status0 = rte_le_to_cpu_16(rxdp->flex_nic_wb.status_error0);
@@ -1298,17 +1289,22 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(uint8_t)(rte_cpu_to_le_16(rxd.flex_nic_wb.ptype_flex_flags0) &
VIRTCHNL2_RX_FLEX_DESC_PTYPE_M)];
+ if ((first_seg->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
+ rxq->hw_register_set, true,
+ rxq->time_high);
rxq->hw_register_set = 0;
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -1474,6 +1470,9 @@ idpf_dp_singleq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_txd);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_txd, txq->latch_idx);
+
txe->last_id = tx_last;
tx_id = txe->next_id;
txe = txn;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.h b/drivers/net/intel/idpf/idpf_common_rxtx.h
index 7c6ff5d047..e1b8904181 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.h
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.h
@@ -13,6 +13,12 @@
#include "../common/tx.h"
#include "../common/rx.h"
+#define FIELD_PREP(_mask, _val) \
+ (__extension__ ({ \
+ typeof(_mask) _x = (_mask); \
+ ((typeof(_x))(_val) << rte_bsf32(_x)) & (_x); \
+ }))
+
#define IDPF_RX_MAX_BURST 32
#define IDPF_RX_OFFLOAD_IPV4_CKSUM RTE_BIT64(1)
@@ -167,6 +173,7 @@ struct idpf_rx_queue {
uint64_t offloads;
uint32_t hw_register_set;
+ uint32_t time_high; /* high 32 bits of hardware timestamp register */
};
/* Offload features */
diff --git a/drivers/net/intel/idpf/idpf_ethdev.c b/drivers/net/intel/idpf/idpf_ethdev.c
index 5d786fbba6..5e57a45775 100644
--- a/drivers/net/intel/idpf/idpf_ethdev.c
+++ b/drivers/net/intel/idpf/idpf_ethdev.c
@@ -14,6 +14,7 @@
#include "idpf_ethdev.h"
#include "idpf_rxtx.h"
#include "../common/tx.h"
+#include "idpf_ptp.h"
#define IDPF_TX_SINGLE_Q "tx_single"
#define IDPF_RX_SINGLE_Q "rx_single"
@@ -841,6 +842,267 @@ idpf_dev_close(struct rte_eth_dev *dev)
return 0;
}
+static int
+idpf_timesync_enable(struct rte_eth_dev *dev) {
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct timespec sys_ts;
+ uint64_t ns;
+ int ret, q_id;
+ if (dev->data->dev_started && !(dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP)) {
+ PMD_DRV_LOG(ERR, "Rx timestamp offload not configured");
+ return -1;
+ }
+
+ adapter->ptp = rte_zmalloc(NULL, sizeof(struct idpf_ptp), 0);
+ if (adapter->ptp == NULL) {
+ PMD_DRV_LOG(ERR, "Failed to allocate memory for PTP");
+ return -ENOMEM;
+ }
+
+ ret = idpf_ptp_get_caps(adapter);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get PTP capabilities, err=%d", ret);
+ goto fail_ptp;
+ }
+
+ /* Write the default increment time value if the clock adjustments are enabled. */
+ if (adapter->ptp->adj_dev_clk_time_access != IDPF_PTP_NONE) {
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, adapter->ptp->base_incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set incval failed, err=%d", ret);
+ goto fail_ptp;
+ }
+ }
+
+ /* Do not initialize the PTP if the device clock time cannot be read. */
+ if (adapter->ptp->get_dev_clk_time_access == IDPF_PTP_NONE) {
+ PMD_DRV_LOG(ERR, "Getting device clock time is not supported");
+ ret = -EIO;
+ goto fail_ptp;
+ }
+
+ /* Set the device clock time to system time. */
+ if (adapter->ptp->set_dev_clk_time_access != IDPF_PTP_NONE) {
+ clock_gettime(CLOCK_REALTIME, &sys_ts);
+ ns = rte_timespec_to_ns(&sys_ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err=%d", ret);
+ goto fail_ptp;
+ }
+ }
+
+ ret = idpf_ptp_get_vport_tstamps_caps(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get vport timestamp capabilities, err=%d", ret);
+ goto fail_ptp;
+ }
+
+ for (q_id = 0; q_id < dev->data->nb_tx_queues; q_id++) {
+ struct ci_tx_queue *txq = dev->data->tx_queues[q_id];
+ txq->latch_idx = vport->tx_tstamp_caps->tx_tstamp[q_id].idx;
+ }
+
+ 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;
+
+ return 0;
+
+fail_ptp:
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ return ret;
+}
+
+static int
+idpf_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp,
+ uint32_t flags)
+{
+ struct idpf_rx_queue *rxq;
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ts_ns;
+
+ rxq = dev->data->rx_queues[flags];
+
+ ts_ns = idpf_tstamp_convert_32b_64b(adapter, 1, true, rxq->time_high);
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ return 0;
+}
+
+static int
+idpf_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ uint16_t latch_idx;
+ uint64_t ts_ns, tstamp;
+ int ret;
+
+ ret = idpf_ptp_get_tx_tstamp(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get TX timestamp");
+ return ret;
+ }
+
+ latch_idx = vport->tx_tstamp_caps->latched_idx;
+ tstamp = vport->tx_tstamp_caps->tx_tstamp[latch_idx].tstamp;
+ ts_ns = idpf_tstamp_convert_32b_64b(vport->adapter, 0, false, tstamp);
+
+ /* Convert to timespec */
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ vport->tx_tstamp_caps->latched_idx = -1;
+
+ return 0;
+}
+
+static int
+idpf_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta) {
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint64_t time, ns;
+ int ret;
+
+ if (ptp->adj_dev_clk_time_access != IDPF_PTP_MAILBOX) {
+ PMD_DRV_LOG(ERR, "Adjusting device clock time is not supported");
+ return -ENOTSUP;
+ }
+
+ if (delta > INT32_MAX || delta < INT32_MIN) {
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP read clock time failed, err %d", ret);
+ return ret;
+ }
+
+ ns = time + delta;
+
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err %d", ret);
+
+ return ret;
+ }
+
+ ret = idpf_ptp_adj_dev_clk_time(adapter, delta);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP adjusting clock failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm) {
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ int64_t incval, diff = 0;
+ bool negative = false;
+ uint64_t div, rem;
+ uint64_t divisor = 1000000ULL << 16;
+ int shift;
+ int ret;
+
+ incval = ptp->base_incval;
+
+ if (ppm < 0) {
+ negative = true;
+ ppm = -ppm;
+ }
+
+ /* can incval * ppm overflow ? */
+ if (rte_log2_u64(incval) + rte_log2_u64(ppm) > 62) {
+ rem = ppm % divisor;
+ div = ppm / divisor;
+ diff = div * incval;
+ ppm = rem;
+
+ shift = rte_log2_u64(incval) + rte_log2_u64(ppm) - 62;
+ if (shift > 0) {
+ /* drop precision */
+ ppm >>= shift;
+ divisor >>= shift;
+ }
+ }
+
+ if (divisor)
+ diff = diff + incval * ppm / divisor;
+
+ if (negative)
+ incval -= diff;
+ else
+ incval += diff;
+
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int
+idpf_timesync_write_time(struct rte_eth_dev *dev, const struct timespec
+*ts) {
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ns;
+ int ret;
+
+ ns = rte_timespec_to_ns(ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP write time failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts) {
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t time;
+ int ret;
+
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP read time failed, err %d", ret);
+ else
+ *ts = rte_ns_to_timespec(time);
+
+ return ret;
+}
+
+static int
+idpf_timesync_disable(struct rte_eth_dev *dev) {
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+
+ if (vport->tx_tstamp_caps) {
+ rte_free(vport->tx_tstamp_caps);
+ vport->tx_tstamp_caps = NULL;
+ }
+
+ if (adapter->ptp) {
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ }
+
+ return 0;
+}
+
+
static const struct eth_dev_ops idpf_eth_dev_ops = {
.dev_configure = idpf_dev_configure,
.dev_close = idpf_dev_close,
@@ -867,6 +1129,14 @@ static const struct eth_dev_ops idpf_eth_dev_ops = {
.xstats_get = idpf_dev_xstats_get,
.xstats_get_names = idpf_dev_xstats_get_names,
.xstats_reset = idpf_dev_xstats_reset,
+ .timesync_enable = idpf_timesync_enable,
+ .timesync_read_rx_timestamp = idpf_timesync_read_rx_timestamp,
+ .timesync_read_tx_timestamp = idpf_timesync_read_tx_timestamp,
+ .timesync_adjust_time = idpf_timesync_adjust_time,
+ .timesync_adjust_freq = idpf_timesync_adjust_freq,
+ .timesync_read_time = idpf_timesync_read_time,
+ .timesync_write_time = idpf_timesync_write_time,
+ .timesync_disable = idpf_timesync_disable,
};
static int
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* RE: [PATCH v6 4/4] doc/guides: add PTP IDPF documentation
2026-02-14 19:49 ` [PATCH v6 4/4] doc/guides: add PTP IDPF documentation Soumyadeep Hore
@ 2026-02-26 10:39 ` Kumar, Rajesh3
2026-02-27 0:33 ` [PATCH v7 0/4] Enable PTP feature for MEV Soumyadeep Hore
1 sibling, 0 replies; 57+ messages in thread
From: Kumar, Rajesh3 @ 2026-02-26 10:39 UTC (permalink / raw)
To: Hore, Soumyadeep, dev@dpdk.org, Richardson, Bruce
Cc: Singh, Aman Deep, Subbarao, Manoj Kumar
Acked-by: Rajesh Kumar <rajesh3.kumar@intel.com>
-----Original Message-----
From: Hore, Soumyadeep <soumyadeep.hore@intel.com>
Sent: 15 February 2026 01:19 AM
To: dev@dpdk.org; Richardson, Bruce <bruce.richardson@intel.com>
Cc: Kumar, Rajesh3 <rajesh3.kumar@intel.com>; Singh, Aman Deep <aman.deep.singh@intel.com>; Subbarao, Manoj Kumar <manoj.kumar.subbarao@intel.com>
Subject: [PATCH v6 4/4] doc/guides: add PTP IDPF documentation
Updates the IDPF Documentation with PTP feature.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
doc/guides/nics/features/idpf.ini | 1 +
doc/guides/nics/idpf.rst | 15 +++++++++++++++
doc/guides/rel_notes/release_26_03.rst | 4 ++++
3 files changed, 20 insertions(+)
diff --git a/doc/guides/nics/features/idpf.ini b/doc/guides/nics/features/idpf.ini
index 63b799289a..f546648924 100644
--- a/doc/guides/nics/features/idpf.ini
+++ b/doc/guides/nics/features/idpf.ini
@@ -19,6 +19,7 @@ RSS reta update = Y
L3 checksum offload = P
L4 checksum offload = P
Timestamp offload = P
+Timesync = Y
Packet type parsing = Y
Basic stats = Y
Linux = Y
diff --git a/doc/guides/nics/idpf.rst b/doc/guides/nics/idpf.rst index b99cc18188..998c1a5dfa 100644
--- a/doc/guides/nics/idpf.rst
+++ b/doc/guides/nics/idpf.rst
@@ -109,3 +109,18 @@ The paths are chosen based on 2 conditions:
A value "P" means the offload feature is not supported by vector path.
If any not supported features are used, idpf vector PMD is disabled
and the scalar paths are chosen.
+
+ Time Synchronization
+ ~~~~~~~~~~~~~~~~~~~~
+
+ The system operator can run a PTP (Precision Time Protocol) client
+ application to synchronize the time on the network card in ACC (and
+ optionally the time on the system) to the PTP primary.
+
+ IDPF PMD supports PTP client applications that use the DPDK IEEE 1588
+ API to communicate with the PTP primary clock.
+ Note that PTP client application needs to run on PF in ACC.
+
+ .. code-block:: console
+
+ examples/dpdk-ptpclient -l 0-3 -n 3 -a 0000:ec:00.1 -- -T 1 -p 0x1
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index afdf1af06c..16b5a0b0b1 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -63,6 +63,10 @@ New Features
* The timestamp value has been updated to make it usable.
+* **Updated Intel idpf ethernet driver.**
+
+ * Added support for time sync features.
+
* **Updated Intel iavf driver.**
* Added support for pre and post VF reset callbacks.
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* Re: [PATCH v6 2/4] net/idpf: add PTP virtchnl2 support
2026-02-26 10:15 ` Kumar, Rajesh3
@ 2026-02-26 11:12 ` Hore, Soumyadeep
0 siblings, 0 replies; 57+ messages in thread
From: Hore, Soumyadeep @ 2026-02-26 11:12 UTC (permalink / raw)
To: Kumar, Rajesh3, dev@dpdk.org, Richardson, Bruce
Cc: Singh, Aman Deep, Subbarao, Manoj Kumar
[-- Attachment #1: Type: text/plain, Size: 28851 bytes --]
----Original Message-----
From: Hore, Soumyadeep <soumyadeep.hore@intel.com>
Sent: 15 February 2026 01:19 AM
To: dev@dpdk.org; Richardson, Bruce <bruce.richardson@intel.com>
Cc: Kumar, Rajesh3 <rajesh3.kumar@intel.com>; Singh, Aman Deep <aman.deep.singh@intel.com>; Subbarao, Manoj Kumar <manoj.kumar.subbarao@intel.com>
Subject: [PATCH v6 2/4] net/idpf: add PTP virtchnl2 support
Add a new file - idpf_ptp - to handle PTP virtchnl messages.
Keep the registers addresses in the PTP struct.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 31 +-
drivers/net/intel/idpf/idpf_ptp.c | 504 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 130 +++++
drivers/net/intel/idpf/meson.build | 1 +
5 files changed, 668 insertions(+), 2 deletions(-) create mode 100644 drivers/net/intel/idpf/idpf_ptp.c create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
diff --git a/drivers/net/intel/idpf/idpf_common_device.h b/drivers/net/intel/idpf/idpf_common_device.h
index 31915a03d4..e45795c18b 100644
--- a/drivers/net/intel/idpf/idpf_common_device.h
+++ b/drivers/net/intel/idpf/idpf_common_device.h
@@ -103,6 +103,7 @@ struct idpf_adapter {
/* For timestamp */
uint64_t time_hw;
+ struct idpf_ptp *ptp;
enum idpf_rx_func_type rx_func_type;
enum idpf_tx_func_type tx_func_type;
@@ -173,6 +174,9 @@ struct idpf_vport {
/* Event from ipf */
bool link_up;
uint32_t link_speed;
+
+ /* For PTP */
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
};
/* Message type read in virtual channel from PF */ diff --git a/drivers/net/intel/idpf/idpf_common_virtchnl.c b/drivers/net/intel/idpf/idpf_common_virtchnl.c
index e927d7415a..f8c487c537 100644
--- a/drivers/net/intel/idpf/idpf_common_virtchnl.c
+++ b/drivers/net/intel/idpf/idpf_common_virtchnl.c
@@ -4,6 +4,7 @@
#include "idpf_common_virtchnl.h"
#include "idpf_common_logs.h"
+#include "idpf_ptp.h"
#include <eal_export.h>
@@ -38,6 +39,24 @@ idpf_vc_clean(struct idpf_adapter *adapter)
return 0;
}
+ /* Helper to detect PTP-related mailbox messages */ static inline bool
+idpf_mb_msg_is_ptp(uint32_t op) {
+ switch (op) {
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ 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:
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int
idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
uint16_t msg_size, uint8_t *msg)
@@ -71,8 +90,16 @@ idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
memcpy(dma_mem->va, msg, msg_size);
- ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
- ctlq_msg->func_id = 0;
+ if (adapter->ptp != NULL &&
+ idpf_mb_msg_is_ptp(op) &&
+ adapter->ptp->secondary_mbx.valid) {
+ 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 {
+ ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
+ ctlq_msg->func_id = 0;
+ }
ctlq_msg->data_len = msg_size;
ctlq_msg->cookie.mbx.chnl_opcode = op;
ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS; diff --git a/drivers/net/intel/idpf/idpf_ptp.c b/drivers/net/intel/idpf/idpf_ptp.c
new file mode 100644
index 0000000000..aefd1309f7
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.c
@@ -0,0 +1,504 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#include "idpf_ptp.h"
+#include "idpf_common_virtchnl.h"
+#include "base/virtchnl2.h"
+
+static enum idpf_ptp_access
+idpf_ptp_get_access(const struct idpf_adapter *adapter, uint32_t direct,
+ uint32_t 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;
+}
+
+#define IDPF_PTP_ACCESS(adapter, cap) \
+ ((uint8_t)idpf_ptp_get_access((adapter), (cap), (cap##_MB)))
+
+static void
+idpf_ptp_get_features_access(const struct idpf_adapter *adapter) {
+ struct idpf_ptp *ptp = adapter->ptp;
+
+ ptp->get_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME);
+ ptp->get_cross_tstamp_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_GET_CROSS_TIME);
+ ptp->set_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME);
+ ptp->adj_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK);
+ ptp->tx_tstamp_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_TX_TSTAMPS); }
+
+int
+idpf_ptp_get_caps(struct idpf_adapter *adapter) {
+ struct virtchnl2_ptp_cross_time_reg_offsets cross_tstamp_offsets;
+ struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
+ struct virtchnl2_ptp_get_caps send_ptp_caps_msg = {0};
+ struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
+ struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
+ struct idpf_cmd_info args = {0};
+ struct idpf_ptp_secondary_mbx *scnd_mbx;
+ struct idpf_ptp *ptp = adapter->ptp;
+ struct idpf_hw *hw = &adapter->hw;
+ enum idpf_ptp_access access_type;
+ int err;
+ uint32_t temp_offset;
+
+ 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 |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CAPS;
+ args.in_args = (uint8_t *)&send_ptp_caps_msg;
+ args.in_args_size = sizeof(send_ptp_caps_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(*recv_ptp_caps_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
+ 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);
+ scnd_mbx->valid = scnd_mbx->peer_mbx_q_id != 0xffff;
+ if (scnd_mbx->valid)
+ scnd_mbx->peer_id = recv_ptp_caps_msg->peer_id;
+
+ idpf_ptp_get_features_access(adapter);
+
+ access_type = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.dev_clk_ns_h);
+ ptp->dev_clk_regs.dev_clk_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_l);
+ ptp->dev_clk_regs.phy_clk_ns_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_h);
+ ptp->dev_clk_regs.phy_clk_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ access_type = (enum idpf_ptp_access)ptp->get_cross_tstamp_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.sys_time_ns_h);
+ ptp->dev_clk_regs.sys_time_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ access_type = (enum idpf_ptp_access)ptp->adj_dev_clk_time_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ clk_adj_offsets = recv_ptp_caps_msg->clk_adj_offsets;
+
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_cmd_type);
+ ptp->dev_clk_regs.cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_l);
+ ptp->dev_clk_regs.incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_h);
+ ptp->dev_clk_regs.incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_l);
+ ptp->dev_clk_regs.shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_h);
+ ptp->dev_clk_regs.shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_cmd_type);
+ ptp->dev_clk_regs.phy_cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_l);
+ ptp->dev_clk_regs.phy_incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_h);
+ ptp->dev_clk_regs.phy_incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_l);
+ ptp->dev_clk_regs.phy_shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_h);
+ ptp->dev_clk_regs.phy_shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ return err;
+}
+
+static void
+idpf_ptp_enable_shtime(struct idpf_adapter *adapter) {
+ uint32_t shtime_enable, exec_cmd;
+
+ shtime_enable = adapter->ptp->cmd.shtime_enable_mask;
+ exec_cmd = adapter->ptp->cmd.exec_cmd_mask;
+
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, shtime_enable);
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync,
+ exec_cmd | shtime_enable);
+}
+
+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 = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+ uint64_t dev_time;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&get_dev_clk_time_msg;
+ args.in_args_size = sizeof(get_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(get_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ get_dev_clk_time_msg =
+ *(struct virtchnl2_ptp_get_dev_clk_time *)args.out_buffer;
+ dev_time = LE64_TO_CPU(get_dev_clk_time_msg.dev_time_ns);
+ dev_clk_time->dev_clk_time_ns = dev_time;
+
+ return err;
+}
+
+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 = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CROSS_TIME;
+ args.in_args = (uint8_t *)&cross_time_msg;
+ args.in_args_size = sizeof(cross_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(cross_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ cross_time_msg =
+ *(struct virtchnl2_ptp_get_cross_time *)args.out_buffer;
+ 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 err;
+}
+
+int
+idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, uint64_t time)
+{
+ struct virtchnl2_ptp_set_dev_clk_time set_dev_clk_time_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ set_dev_clk_time_msg.dev_time_ns = CPU_TO_LE64(time);
+
+ args.ops = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&set_dev_clk_time_msg;
+ args.in_args_size = sizeof(set_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(set_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, int64_t delta)
+{
+ struct virtchnl2_ptp_adj_dev_clk_time adj_dev_clk_time_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ adj_dev_clk_time_msg.delta = CPU_TO_LE64(delta);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&adj_dev_clk_time_msg;
+ args.in_args_size = sizeof(adj_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t
+incval) {
+ struct virtchnl2_ptp_adj_dev_clk_fine adj_dev_clk_fine_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ adj_dev_clk_fine_msg.incval = CPU_TO_LE64(incval);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE;
+ args.in_args = (uint8_t *)&adj_dev_clk_fine_msg;
+ args.in_args_size = sizeof(adj_dev_clk_fine_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_fine_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport) {
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps send_tx_tstamp_caps = {0};
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps *rcv_tx_tstamp_caps;
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
+ enum idpf_ptp_access tstamp_access, get_dev_clk_access;
+ struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
+ struct idpf_ptp *ptp = vport->adapter->ptp;
+ struct idpf_cmd_info args = {0};
+ int err;
+ uint16_t num_latches, i;
Instead of using i, Can we use something like latch_itr ?
+ uint32_t size;
+
+ if (ptp == NULL)
+ return -EOPNOTSUPP;
+
+ tstamp_access = (enum idpf_ptp_access)ptp->tx_tstamp_access;
+ get_dev_clk_access = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (tstamp_access == IDPF_PTP_NONE ||
+ get_dev_clk_access == IDPF_PTP_NONE)
+ return -EOPNOTSUPP;
+
+ send_tx_tstamp_caps.vport_id = CPU_TO_LE32(vport->vport_id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS;
+ args.in_args = (uint8_t *)&send_tx_tstamp_caps;
+ args.in_args_size = sizeof(send_tx_tstamp_caps);
+ args.out_size = IDPF_CTLQ_MAX_BUF_LEN;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ if (err < 0)
+ return err;
+
+ rcv_tx_tstamp_caps =
+ (struct virtchnl2_ptp_get_vport_tx_tstamp_caps *)args.out_buffer;
+ num_latches = LE16_TO_CPU(rcv_tx_tstamp_caps->num_latches);
+ size = sizeof(struct idpf_ptp_vport_tx_tstamp_caps) +
+ sizeof(struct idpf_ptp_tx_tstamp) * num_latches;
+ tstamp_caps = rte_zmalloc(NULL, size, 0);
+ if (tstamp_caps == NULL)
+ return -ENOMEM;
+
+ tstamp_caps->access = true;
+ tstamp_caps->num_entries = num_latches;
+ tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ for (i = 0; i < tstamp_caps->num_entries; i++) {
+ __le32 offset_l, offset_h;
+
+ tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[i];
+
+ if (tstamp_access == IDPF_PTP_DIRECT) {
+ offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
+ offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_l =
+ LE32_TO_CPU(offset_l);
+ tstamp_caps->tx_tstamp[i].tx_latch_reg_offset_h =
+ LE32_TO_CPU(offset_h);
+ }
+ tstamp_caps->tx_tstamp[i].idx = tx_tstamp_latch_caps.index;
+ }
+
+ tstamp_caps->latched_idx = -1;
+ vport->tx_tstamp_caps = tstamp_caps;
+
+ return err;
+}
+
+static int
+idpf_ptp_get_tstamp_value(struct idpf_vport *vport,
+ struct virtchnl2_ptp_tx_tstamp_latch *tstamp_latch,
+ struct idpf_ptp_tx_tstamp *tx_tstamp) {
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ uint8_t tstamp_ns_lo_bit;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ tx_tstamp->tstamp = LE64_TO_CPU(tstamp_latch->tstamp);
+ tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
+
+ return 0;
+}
+
+int
+idpf_ptp_get_tx_tstamp(struct idpf_vport *vport) {
+ struct virtchnl2_ptp_get_vport_tx_tstamp_latches *send_tx_tstamp_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 *ptp_tx_tstamp;
+ struct idpf_cmd_info args = {0};
+ int size, msg_size;
+ uint32_t vport_id;
+ uint16_t num_latches, id;
Instead of using id, Can we use something like latch_itr ?
I am keeping it uniform and cheanging it to idx across the code as followed in previous developments
+ int err;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+
+ size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) *
+ tx_tstamp_caps->num_entries;
+ send_tx_tstamp_msg = rte_zmalloc(NULL, size, 0);
+ if (send_tx_tstamp_msg == NULL)
+ return -ENOMEM;
+
+ for (id = 0; id < tx_tstamp_caps->num_entries; id++, ptp_tx_tstamp++)
+ send_tx_tstamp_msg->tstamp_latches[id].index = ptp_tx_tstamp->idx;
+ send_tx_tstamp_msg->get_devtime_with_txtstmp = 1;
+
+ msg_size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) * id;
+ send_tx_tstamp_msg->vport_id = CPU_TO_LE32(vport->vport_id);
+ send_tx_tstamp_msg->num_latches = CPU_TO_LE16(id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP;
+ args.in_args = (uint8_t *)send_tx_tstamp_msg;
+ args.in_args_size = msg_size;
+ args.out_size = msg_size;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ rte_free(send_tx_tstamp_msg);
+ if (err < 0)
+ return err;
+
+ recv_tx_tstamp_msg =
+ (struct virtchnl2_ptp_get_vport_tx_tstamp_latches *)args.out_buffer;
+ vport_id = LE32_TO_CPU(recv_tx_tstamp_msg->vport_id);
+ if (vport->vport_id != vport_id)
+ return -EINVAL;
+
+ num_latches = LE16_TO_CPU(recv_tx_tstamp_msg->num_latches);
+
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+ for (id = 0; id < num_latches; id++, ptp_tx_tstamp++) {
+ tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[id];
+
+ if (!tstamp_latch.valid)
+ continue;
+
+ err = idpf_ptp_get_tstamp_value(vport, &tstamp_latch,
+ ptp_tx_tstamp);
+ if (err == 0) {
+ tx_tstamp_caps->latched_idx = id;
+ vport->adapter->time_hw = recv_tx_tstamp_msg->device_time;
+ }
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer
+value
+ * @adapter: Driver specific private structure
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int
+idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter, uint64_t
+*src_clk) {
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint32_t hi, lo;
+
+ idpf_ptp_enable_shtime(adapter);
+
+ lo = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_l);
+ hi = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_h);
+
+ *src_clk = ((uint64_t)hi << 32) | lo;
+ return 0;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value
+through mailbox
+ * @adapter: Driver specific private structure
+ * @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,
+ uint64_t *src_clk)
+{
+ struct idpf_ptp_dev_timers clk_time;
+ int err;
+
+ err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
+ if (err)
+ return err;
+
+ *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
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int
+idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t
+*src_clk) {
+ if (adapter->ptp == NULL)
+ return -EINVAL;
+
+ switch ((enum idpf_ptp_access)adapter->ptp->get_dev_clk_time_access) {
+ case IDPF_PTP_MAILBOX:
+ return idpf_ptp_read_src_clk_reg_mailbox(adapter, src_clk);
+ case IDPF_PTP_DIRECT:
+ return idpf_ptp_read_src_clk_reg_direct(adapter, src_clk);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
diff --git a/drivers/net/intel/idpf/idpf_ptp.h b/drivers/net/intel/idpf/idpf_ptp.h
new file mode 100644
index 0000000000..0c901440fb
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#ifndef _IDPF_PTP_H_
+#define _IDPF_PTP_H_
+
+#include "base/idpf_osdep.h"
+#include <rte_time.h>
+#include "idpf_common_device.h"
+
+struct idpf_ptp_cmd {
+ uint32_t exec_cmd_mask;
+ uint32_t shtime_enable_mask;
+};
+
+struct idpf_ptp_dev_clk_regs {
+ volatile uint32_t *dev_clk_ns_l;
+ volatile uint32_t *dev_clk_ns_h;
+ volatile uint32_t *phy_clk_ns_l;
+ volatile uint32_t *phy_clk_ns_h;
+ volatile uint32_t *sys_time_ns_l;
+ volatile uint32_t *sys_time_ns_h;
+ volatile uint32_t *incval_l;
+ volatile uint32_t *incval_h;
+ volatile uint32_t *shadj_l;
+ volatile uint32_t *shadj_h;
+ volatile uint32_t *phy_incval_l;
+ volatile uint32_t *phy_incval_h;
+ volatile uint32_t *phy_shadj_l;
+ volatile uint32_t *phy_shadj_h;
+ volatile uint32_t *cmd;
+ volatile uint32_t *phy_cmd;
+ volatile uint32_t *cmd_sync;
+};
+
+enum idpf_ptp_access {
+ IDPF_PTP_NONE = 0,
+ IDPF_PTP_DIRECT,
+ IDPF_PTP_MAILBOX,
+};
+
+struct idpf_ptp_secondary_mbx {
+ uint16_t peer_mbx_q_id;
+ uint16_t peer_id;
+ bool valid:1;
+};
+
+enum idpf_ptp_tx_tstamp_state {
+ IDPF_PTP_FREE,
+ IDPF_PTP_REQUEST,
+ IDPF_PTP_READ_VALUE,
+};
+
+struct idpf_ptp_tx_tstamp {
+ uint64_t tstamp;
+ uint32_t tx_latch_reg_offset_l;
+ uint32_t tx_latch_reg_offset_h;
+ uint32_t idx;
+};
+
+struct idpf_ptp_vport_tx_tstamp_caps {
+ uint32_t vport_id;
+ uint16_t num_entries;
+ uint16_t tstamp_ns_lo_bit;
+ uint16_t latched_idx;
+ bool access:1;
+ struct idpf_ptp_tx_tstamp tx_tstamp[]; };
+
+struct idpf_ptp {
+ uint64_t base_incval;
+ uint64_t max_adj;
+ struct idpf_ptp_cmd cmd;
+ struct idpf_ptp_dev_clk_regs dev_clk_regs;
+ uint32_t caps;
+ uint8_t get_dev_clk_time_access:2;
+ uint8_t get_cross_tstamp_access:2;
+ uint8_t set_dev_clk_time_access:2;
+ uint8_t adj_dev_clk_time_access:2;
+ uint8_t tx_tstamp_access:2;
+ uint8_t rsv:6;
+ struct idpf_ptp_secondary_mbx secondary_mbx; };
+
+struct idpf_ptp_dev_timers {
+ uint64_t sys_time_ns;
+ uint64_t dev_clk_time_ns;
+};
+
+int idpf_ptp_get_caps(struct idpf_adapter *adapter); int
+idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t
+*src_clk); 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, uint64_t time);
+int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t
+incval); int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter,
+int64_t delta); int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport
+*vport); int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
+
+static inline uint64_t
+idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
+ bool is_rx, uint32_t in_timestamp) {
+ const uint64_t mask = 0xFFFFFFFFULL;
+ uint32_t phc_time_lo, delta;
+ uint64_t ns;
+
+ if (flag != 0)
+ idpf_ptp_read_src_clk_reg(ad, &ad->time_hw);
+
+ phc_time_lo = (uint32_t)(ad->time_hw);
+ delta = in_timestamp - phc_time_lo;
+
+ if (delta > mask / 2) {
+ delta = phc_time_lo - in_timestamp;
+ ns = ad->time_hw - delta;
+ } else {
+ if (is_rx)
+ ns = ad->time_hw - delta;
+ else
+ ns = ad->time_hw + delta;
+ }
+
+ return ns;
+}
+
+#endif /* _IDPF_PTP_H_ */
diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
index a805d02ea2..740c1a5985 100644
--- a/drivers/net/intel/idpf/meson.build
+++ b/drivers/net/intel/idpf/meson.build
@@ -17,6 +17,7 @@ sources += files(
'idpf_common_virtchnl.c',
'idpf_ethdev.c',
+ 'idpf_ptp.c',
'idpf_rxtx.c',
)
--
2.47.1
[-- Attachment #2: Type: text/html, Size: 54039 bytes --]
^ permalink raw reply related [flat|nested] 57+ messages in thread
* RE: [PATCH v7 2/4] net/idpf: add PTP virtchnl2 support
2026-02-27 0:33 ` [PATCH v7 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
@ 2026-02-26 12:00 ` Kumar, Rajesh3
0 siblings, 0 replies; 57+ messages in thread
From: Kumar, Rajesh3 @ 2026-02-26 12:00 UTC (permalink / raw)
To: Hore, Soumyadeep, dev@dpdk.org, Richardson, Bruce,
Singh, Aman Deep, Subbarao, Manoj Kumar
Acked-by: Rajesh Kumar <rajesh3.kumar@intel.com>
-----Original Message-----
From: Hore, Soumyadeep <soumyadeep.hore@intel.com>
Sent: 27 February 2026 06:03 AM
To: dev@dpdk.org; Richardson, Bruce <bruce.richardson@intel.com>; Kumar, Rajesh3 <rajesh3.kumar@intel.com>; Singh, Aman Deep <aman.deep.singh@intel.com>; Subbarao, Manoj Kumar <manoj.kumar.subbarao@intel.com>
Subject: [PATCH v7 2/4] net/idpf: add PTP virtchnl2 support
Add a new file - idpf_ptp - to handle PTP virtchnl messages.
Keep the registers addresses in the PTP struct.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 31 +-
drivers/net/intel/idpf/idpf_ptp.c | 504 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 130 +++++
drivers/net/intel/idpf/meson.build | 1 +
5 files changed, 668 insertions(+), 2 deletions(-) create mode 100644 drivers/net/intel/idpf/idpf_ptp.c create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
diff --git a/drivers/net/intel/idpf/idpf_common_device.h b/drivers/net/intel/idpf/idpf_common_device.h
index 31915a03d4..e45795c18b 100644
--- a/drivers/net/intel/idpf/idpf_common_device.h
+++ b/drivers/net/intel/idpf/idpf_common_device.h
@@ -103,6 +103,7 @@ struct idpf_adapter {
/* For timestamp */
uint64_t time_hw;
+ struct idpf_ptp *ptp;
enum idpf_rx_func_type rx_func_type;
enum idpf_tx_func_type tx_func_type;
@@ -173,6 +174,9 @@ struct idpf_vport {
/* Event from ipf */
bool link_up;
uint32_t link_speed;
+
+ /* For PTP */
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
};
/* Message type read in virtual channel from PF */ diff --git a/drivers/net/intel/idpf/idpf_common_virtchnl.c b/drivers/net/intel/idpf/idpf_common_virtchnl.c
index e927d7415a..f8c487c537 100644
--- a/drivers/net/intel/idpf/idpf_common_virtchnl.c
+++ b/drivers/net/intel/idpf/idpf_common_virtchnl.c
@@ -4,6 +4,7 @@
#include "idpf_common_virtchnl.h"
#include "idpf_common_logs.h"
+#include "idpf_ptp.h"
#include <eal_export.h>
@@ -38,6 +39,24 @@ idpf_vc_clean(struct idpf_adapter *adapter)
return 0;
}
+ /* Helper to detect PTP-related mailbox messages */ static inline bool
+idpf_mb_msg_is_ptp(uint32_t op) {
+ switch (op) {
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ 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:
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int
idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
uint16_t msg_size, uint8_t *msg)
@@ -71,8 +90,16 @@ idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
memcpy(dma_mem->va, msg, msg_size);
- ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
- ctlq_msg->func_id = 0;
+ if (adapter->ptp != NULL &&
+ idpf_mb_msg_is_ptp(op) &&
+ adapter->ptp->secondary_mbx.valid) {
+ 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 {
+ ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
+ ctlq_msg->func_id = 0;
+ }
ctlq_msg->data_len = msg_size;
ctlq_msg->cookie.mbx.chnl_opcode = op;
ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS; diff --git a/drivers/net/intel/idpf/idpf_ptp.c b/drivers/net/intel/idpf/idpf_ptp.c
new file mode 100644
index 0000000000..5c3fcb90e4
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.c
@@ -0,0 +1,504 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#include "idpf_ptp.h"
+#include "idpf_common_virtchnl.h"
+#include "base/virtchnl2.h"
+
+static enum idpf_ptp_access
+idpf_ptp_get_access(const struct idpf_adapter *adapter, uint32_t direct,
+ uint32_t 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;
+}
+
+#define IDPF_PTP_ACCESS(adapter, cap) \
+ ((uint8_t)idpf_ptp_get_access((adapter), (cap), (cap##_MB)))
+
+static void
+idpf_ptp_get_features_access(const struct idpf_adapter *adapter) {
+ struct idpf_ptp *ptp = adapter->ptp;
+
+ ptp->get_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME);
+ ptp->get_cross_tstamp_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_GET_CROSS_TIME);
+ ptp->set_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME);
+ ptp->adj_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK);
+ ptp->tx_tstamp_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_TX_TSTAMPS); }
+
+int
+idpf_ptp_get_caps(struct idpf_adapter *adapter) {
+ struct virtchnl2_ptp_cross_time_reg_offsets cross_tstamp_offsets;
+ struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
+ struct virtchnl2_ptp_get_caps send_ptp_caps_msg = {0};
+ struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
+ struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
+ struct idpf_cmd_info args = {0};
+ struct idpf_ptp_secondary_mbx *scnd_mbx;
+ struct idpf_ptp *ptp = adapter->ptp;
+ struct idpf_hw *hw = &adapter->hw;
+ enum idpf_ptp_access access_type;
+ int err;
+ uint32_t temp_offset;
+
+ 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 |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CAPS;
+ args.in_args = (uint8_t *)&send_ptp_caps_msg;
+ args.in_args_size = sizeof(send_ptp_caps_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(*recv_ptp_caps_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
+ 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);
+ scnd_mbx->valid = scnd_mbx->peer_mbx_q_id != 0xffff;
+ if (scnd_mbx->valid)
+ scnd_mbx->peer_id = recv_ptp_caps_msg->peer_id;
+
+ idpf_ptp_get_features_access(adapter);
+
+ access_type = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.dev_clk_ns_h);
+ ptp->dev_clk_regs.dev_clk_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_l);
+ ptp->dev_clk_regs.phy_clk_ns_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_h);
+ ptp->dev_clk_regs.phy_clk_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ access_type = (enum idpf_ptp_access)ptp->get_cross_tstamp_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.sys_time_ns_h);
+ ptp->dev_clk_regs.sys_time_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ access_type = (enum idpf_ptp_access)ptp->adj_dev_clk_time_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ clk_adj_offsets = recv_ptp_caps_msg->clk_adj_offsets;
+
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_cmd_type);
+ ptp->dev_clk_regs.cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_l);
+ ptp->dev_clk_regs.incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_h);
+ ptp->dev_clk_regs.incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_l);
+ ptp->dev_clk_regs.shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_h);
+ ptp->dev_clk_regs.shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_cmd_type);
+ ptp->dev_clk_regs.phy_cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_l);
+ ptp->dev_clk_regs.phy_incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_h);
+ ptp->dev_clk_regs.phy_incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_l);
+ ptp->dev_clk_regs.phy_shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_h);
+ ptp->dev_clk_regs.phy_shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ return err;
+}
+
+static void
+idpf_ptp_enable_shtime(struct idpf_adapter *adapter) {
+ uint32_t shtime_enable, exec_cmd;
+
+ shtime_enable = adapter->ptp->cmd.shtime_enable_mask;
+ exec_cmd = adapter->ptp->cmd.exec_cmd_mask;
+
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, shtime_enable);
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync,
+ exec_cmd | shtime_enable);
+}
+
+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 = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+ uint64_t dev_time;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&get_dev_clk_time_msg;
+ args.in_args_size = sizeof(get_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(get_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ get_dev_clk_time_msg =
+ *(struct virtchnl2_ptp_get_dev_clk_time *)args.out_buffer;
+ dev_time = LE64_TO_CPU(get_dev_clk_time_msg.dev_time_ns);
+ dev_clk_time->dev_clk_time_ns = dev_time;
+
+ return err;
+}
+
+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 = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CROSS_TIME;
+ args.in_args = (uint8_t *)&cross_time_msg;
+ args.in_args_size = sizeof(cross_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(cross_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ cross_time_msg =
+ *(struct virtchnl2_ptp_get_cross_time *)args.out_buffer;
+ 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 err;
+}
+
+int
+idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, uint64_t time)
+{
+ struct virtchnl2_ptp_set_dev_clk_time set_dev_clk_time_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ set_dev_clk_time_msg.dev_time_ns = CPU_TO_LE64(time);
+
+ args.ops = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&set_dev_clk_time_msg;
+ args.in_args_size = sizeof(set_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(set_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, int64_t delta)
+{
+ struct virtchnl2_ptp_adj_dev_clk_time adj_dev_clk_time_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ adj_dev_clk_time_msg.delta = CPU_TO_LE64(delta);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&adj_dev_clk_time_msg;
+ args.in_args_size = sizeof(adj_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t
+incval) {
+ struct virtchnl2_ptp_adj_dev_clk_fine adj_dev_clk_fine_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ adj_dev_clk_fine_msg.incval = CPU_TO_LE64(incval);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE;
+ args.in_args = (uint8_t *)&adj_dev_clk_fine_msg;
+ args.in_args_size = sizeof(adj_dev_clk_fine_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_fine_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport) {
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps send_tx_tstamp_caps = {0};
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps *rcv_tx_tstamp_caps;
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
+ enum idpf_ptp_access tstamp_access, get_dev_clk_access;
+ struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
+ struct idpf_ptp *ptp = vport->adapter->ptp;
+ struct idpf_cmd_info args = {0};
+ int err;
+ uint16_t num_latches, id;
+ uint32_t size;
+
+ if (ptp == NULL)
+ return -EOPNOTSUPP;
+
+ tstamp_access = (enum idpf_ptp_access)ptp->tx_tstamp_access;
+ get_dev_clk_access = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (tstamp_access == IDPF_PTP_NONE ||
+ get_dev_clk_access == IDPF_PTP_NONE)
+ return -EOPNOTSUPP;
+
+ send_tx_tstamp_caps.vport_id = CPU_TO_LE32(vport->vport_id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS;
+ args.in_args = (uint8_t *)&send_tx_tstamp_caps;
+ args.in_args_size = sizeof(send_tx_tstamp_caps);
+ args.out_size = IDPF_CTLQ_MAX_BUF_LEN;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ if (err < 0)
+ return err;
+
+ rcv_tx_tstamp_caps =
+ (struct virtchnl2_ptp_get_vport_tx_tstamp_caps *)args.out_buffer;
+ num_latches = LE16_TO_CPU(rcv_tx_tstamp_caps->num_latches);
+ size = sizeof(struct idpf_ptp_vport_tx_tstamp_caps) +
+ sizeof(struct idpf_ptp_tx_tstamp) * num_latches;
+ tstamp_caps = rte_zmalloc(NULL, size, 0);
+ if (tstamp_caps == NULL)
+ return -ENOMEM;
+
+ tstamp_caps->access = true;
+ tstamp_caps->num_entries = num_latches;
+ tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ for (id = 0; id < tstamp_caps->num_entries; id++) {
+ __le32 offset_l, offset_h;
+
+ tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[id];
+
+ if (tstamp_access == IDPF_PTP_DIRECT) {
+ offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
+ offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
+ tstamp_caps->tx_tstamp[id].tx_latch_reg_offset_l =
+ LE32_TO_CPU(offset_l);
+ tstamp_caps->tx_tstamp[id].tx_latch_reg_offset_h =
+ LE32_TO_CPU(offset_h);
+ }
+ tstamp_caps->tx_tstamp[id].idx = tx_tstamp_latch_caps.index;
+ }
+
+ tstamp_caps->latched_idx = -1;
+ vport->tx_tstamp_caps = tstamp_caps;
+
+ return err;
+}
+
+static int
+idpf_ptp_get_tstamp_value(struct idpf_vport *vport,
+ struct virtchnl2_ptp_tx_tstamp_latch *tstamp_latch,
+ struct idpf_ptp_tx_tstamp *tx_tstamp) {
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ uint8_t tstamp_ns_lo_bit;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ tx_tstamp->tstamp = LE64_TO_CPU(tstamp_latch->tstamp);
+ tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
+
+ return 0;
+}
+
+int
+idpf_ptp_get_tx_tstamp(struct idpf_vport *vport) {
+ struct virtchnl2_ptp_get_vport_tx_tstamp_latches *send_tx_tstamp_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 *ptp_tx_tstamp;
+ struct idpf_cmd_info args = {0};
+ int size, msg_size;
+ uint32_t vport_id;
+ uint16_t num_latches, id;
+ int err;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+
+ size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) *
+ tx_tstamp_caps->num_entries;
+ send_tx_tstamp_msg = rte_zmalloc(NULL, size, 0);
+ if (send_tx_tstamp_msg == NULL)
+ return -ENOMEM;
+
+ for (id = 0; id < tx_tstamp_caps->num_entries; id++, ptp_tx_tstamp++)
+ send_tx_tstamp_msg->tstamp_latches[id].index = ptp_tx_tstamp->idx;
+ send_tx_tstamp_msg->get_devtime_with_txtstmp = 1;
+
+ msg_size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) * id;
+ send_tx_tstamp_msg->vport_id = CPU_TO_LE32(vport->vport_id);
+ send_tx_tstamp_msg->num_latches = CPU_TO_LE16(id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP;
+ args.in_args = (uint8_t *)send_tx_tstamp_msg;
+ args.in_args_size = msg_size;
+ args.out_size = msg_size;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ rte_free(send_tx_tstamp_msg);
+ if (err < 0)
+ return err;
+
+ recv_tx_tstamp_msg =
+ (struct virtchnl2_ptp_get_vport_tx_tstamp_latches *)args.out_buffer;
+ vport_id = LE32_TO_CPU(recv_tx_tstamp_msg->vport_id);
+ if (vport->vport_id != vport_id)
+ return -EINVAL;
+
+ num_latches = LE16_TO_CPU(recv_tx_tstamp_msg->num_latches);
+
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+ for (id = 0; id < num_latches; id++, ptp_tx_tstamp++) {
+ tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[id];
+
+ if (!tstamp_latch.valid)
+ continue;
+
+ err = idpf_ptp_get_tstamp_value(vport, &tstamp_latch,
+ ptp_tx_tstamp);
+ if (err == 0) {
+ tx_tstamp_caps->latched_idx = id;
+ vport->adapter->time_hw = recv_tx_tstamp_msg->device_time;
+ }
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer
+value
+ * @adapter: Driver specific private structure
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int
+idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter, uint64_t
+*src_clk) {
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint32_t hi, lo;
+
+ idpf_ptp_enable_shtime(adapter);
+
+ lo = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_l);
+ hi = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_h);
+
+ *src_clk = ((uint64_t)hi << 32) | lo;
+ return 0;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value
+through mailbox
+ * @adapter: Driver specific private structure
+ * @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,
+ uint64_t *src_clk)
+{
+ struct idpf_ptp_dev_timers clk_time;
+ int err;
+
+ err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
+ if (err)
+ return err;
+
+ *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
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int
+idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t
+*src_clk) {
+ if (adapter->ptp == NULL)
+ return -EINVAL;
+
+ switch ((enum idpf_ptp_access)adapter->ptp->get_dev_clk_time_access) {
+ case IDPF_PTP_MAILBOX:
+ return idpf_ptp_read_src_clk_reg_mailbox(adapter, src_clk);
+ case IDPF_PTP_DIRECT:
+ return idpf_ptp_read_src_clk_reg_direct(adapter, src_clk);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
diff --git a/drivers/net/intel/idpf/idpf_ptp.h b/drivers/net/intel/idpf/idpf_ptp.h
new file mode 100644
index 0000000000..0c901440fb
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#ifndef _IDPF_PTP_H_
+#define _IDPF_PTP_H_
+
+#include "base/idpf_osdep.h"
+#include <rte_time.h>
+#include "idpf_common_device.h"
+
+struct idpf_ptp_cmd {
+ uint32_t exec_cmd_mask;
+ uint32_t shtime_enable_mask;
+};
+
+struct idpf_ptp_dev_clk_regs {
+ volatile uint32_t *dev_clk_ns_l;
+ volatile uint32_t *dev_clk_ns_h;
+ volatile uint32_t *phy_clk_ns_l;
+ volatile uint32_t *phy_clk_ns_h;
+ volatile uint32_t *sys_time_ns_l;
+ volatile uint32_t *sys_time_ns_h;
+ volatile uint32_t *incval_l;
+ volatile uint32_t *incval_h;
+ volatile uint32_t *shadj_l;
+ volatile uint32_t *shadj_h;
+ volatile uint32_t *phy_incval_l;
+ volatile uint32_t *phy_incval_h;
+ volatile uint32_t *phy_shadj_l;
+ volatile uint32_t *phy_shadj_h;
+ volatile uint32_t *cmd;
+ volatile uint32_t *phy_cmd;
+ volatile uint32_t *cmd_sync;
+};
+
+enum idpf_ptp_access {
+ IDPF_PTP_NONE = 0,
+ IDPF_PTP_DIRECT,
+ IDPF_PTP_MAILBOX,
+};
+
+struct idpf_ptp_secondary_mbx {
+ uint16_t peer_mbx_q_id;
+ uint16_t peer_id;
+ bool valid:1;
+};
+
+enum idpf_ptp_tx_tstamp_state {
+ IDPF_PTP_FREE,
+ IDPF_PTP_REQUEST,
+ IDPF_PTP_READ_VALUE,
+};
+
+struct idpf_ptp_tx_tstamp {
+ uint64_t tstamp;
+ uint32_t tx_latch_reg_offset_l;
+ uint32_t tx_latch_reg_offset_h;
+ uint32_t idx;
+};
+
+struct idpf_ptp_vport_tx_tstamp_caps {
+ uint32_t vport_id;
+ uint16_t num_entries;
+ uint16_t tstamp_ns_lo_bit;
+ uint16_t latched_idx;
+ bool access:1;
+ struct idpf_ptp_tx_tstamp tx_tstamp[]; };
+
+struct idpf_ptp {
+ uint64_t base_incval;
+ uint64_t max_adj;
+ struct idpf_ptp_cmd cmd;
+ struct idpf_ptp_dev_clk_regs dev_clk_regs;
+ uint32_t caps;
+ uint8_t get_dev_clk_time_access:2;
+ uint8_t get_cross_tstamp_access:2;
+ uint8_t set_dev_clk_time_access:2;
+ uint8_t adj_dev_clk_time_access:2;
+ uint8_t tx_tstamp_access:2;
+ uint8_t rsv:6;
+ struct idpf_ptp_secondary_mbx secondary_mbx; };
+
+struct idpf_ptp_dev_timers {
+ uint64_t sys_time_ns;
+ uint64_t dev_clk_time_ns;
+};
+
+int idpf_ptp_get_caps(struct idpf_adapter *adapter); int
+idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t
+*src_clk); 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, uint64_t time);
+int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t
+incval); int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter,
+int64_t delta); int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport
+*vport); int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
+
+static inline uint64_t
+idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
+ bool is_rx, uint32_t in_timestamp) {
+ const uint64_t mask = 0xFFFFFFFFULL;
+ uint32_t phc_time_lo, delta;
+ uint64_t ns;
+
+ if (flag != 0)
+ idpf_ptp_read_src_clk_reg(ad, &ad->time_hw);
+
+ phc_time_lo = (uint32_t)(ad->time_hw);
+ delta = in_timestamp - phc_time_lo;
+
+ if (delta > mask / 2) {
+ delta = phc_time_lo - in_timestamp;
+ ns = ad->time_hw - delta;
+ } else {
+ if (is_rx)
+ ns = ad->time_hw - delta;
+ else
+ ns = ad->time_hw + delta;
+ }
+
+ return ns;
+}
+
+#endif /* _IDPF_PTP_H_ */
diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
index a805d02ea2..740c1a5985 100644
--- a/drivers/net/intel/idpf/meson.build
+++ b/drivers/net/intel/idpf/meson.build
@@ -17,6 +17,7 @@ sources += files(
'idpf_common_virtchnl.c',
'idpf_ethdev.c',
+ 'idpf_ptp.c',
'idpf_rxtx.c',
)
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v7 0/4] Enable PTP feature for MEV
2026-02-14 19:49 ` [PATCH v6 4/4] doc/guides: add PTP IDPF documentation Soumyadeep Hore
2026-02-26 10:39 ` Kumar, Rajesh3
@ 2026-02-27 0:33 ` Soumyadeep Hore
2026-02-27 0:33 ` [PATCH v7 1/4] net/idpf/base: add a new API for PTP support Soumyadeep Hore
` (3 more replies)
1 sibling, 4 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-02-27 0:33 UTC (permalink / raw)
To: dev, bruce.richardson, rajesh3.kumar, aman.deep.singh,
manoj.kumar.subbarao
Enabling basic PTP feature in IDPF PMD using virtchnl messages.
---
v7:
- Addressed Rajesh's comments
---
v6:
- Addressed Bruce's comments
- Addressed AI comments
---
v5:
- Addressed AI comments
---
v4:
- Addressed Bruce's comments
---
v3:
- Fixed checkpatch warnings
- Addressed Bruce's comments
---
v2:
- Fixed essential checkpatch warnings
---
Milena Olech (1):
net/idpf/base: add a new API for PTP support
Soumyadeep Hore (3):
net/idpf: add PTP virtchnl2 support
net/idpf: add timesync support
doc/guides: add PTP IDPF documentation
doc/guides/nics/features/idpf.ini | 1 +
doc/guides/nics/idpf.rst | 15 +
doc/guides/rel_notes/release_26_03.rst | 4 +
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/base/virtchnl2.h | 324 +++++++----
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 121 +++--
drivers/net/intel/idpf/idpf_common_rxtx.h | 7 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 31 +-
drivers/net/intel/idpf/idpf_ethdev.c | 270 ++++++++++
drivers/net/intel/idpf/idpf_ptp.c | 504 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 130 +++++
drivers/net/intel/idpf/meson.build | 1 +
13 files changed, 1261 insertions(+), 152 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
--
2.47.1
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH v7 1/4] net/idpf/base: add a new API for PTP support
2026-02-27 0:33 ` [PATCH v7 0/4] Enable PTP feature for MEV Soumyadeep Hore
@ 2026-02-27 0:33 ` Soumyadeep Hore
2026-02-27 0:33 ` [PATCH v7 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
` (2 subsequent siblings)
3 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-02-27 0:33 UTC (permalink / raw)
To: dev, bruce.richardson, rajesh3.kumar, aman.deep.singh,
manoj.kumar.subbarao
Cc: Milena Olech
From: Milena Olech <milena.olech@intel.com>
Add PTP capabilities to determine which PTP features are enabled -
get/set the time, cross timestamp, adjust the clock and timestamping.
Create appropriate structures and opcodes to support features introduced by
capabilities.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Acked-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
drivers/net/intel/idpf/base/virtchnl2.h | 324 +++++++++++++++++-------
1 file changed, 235 insertions(+), 89 deletions(-)
diff --git a/drivers/net/intel/idpf/base/virtchnl2.h b/drivers/net/intel/idpf/base/virtchnl2.h
index 6cfb4f56fa..0b01daee30 100644
--- a/drivers/net/intel/idpf/base/virtchnl2.h
+++ b/drivers/net/intel/idpf/base/virtchnl2.h
@@ -118,8 +118,14 @@ enum virtchnl2_op {
VIRTCHNL2_OP_DEL_QUEUE_GROUPS = 539,
VIRTCHNL2_OP_GET_PORT_STATS = 540,
/* TimeSync opcodes */
- VIRTCHNL2_OP_GET_PTP_CAPS = 541,
- VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES = 542,
+ 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,
};
#define VIRTCHNL2_RDMA_INVALID_QUEUE_IDX 0xFFFF
@@ -1813,123 +1819,180 @@ VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_promisc_info);
* enum virtchnl2_ptp_caps - PTP capabilities
*/
enum virtchnl2_ptp_caps {
- VIRTCHNL2_PTP_CAP_LEGACY_CROSS_TIME = BIT(0),
- VIRTCHNL2_PTP_CAP_PTM = BIT(1),
- VIRTCHNL2_PTP_CAP_DEVICE_CLOCK_CONTROL = BIT(2),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_DIRECT = BIT(3),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_VIRTCHNL = BIT(4),
-};
-
-/**
- * struct virtchnl2_ptp_legacy_cross_time_reg - Legacy cross time registers
- * offsets.
+ 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_legacy_cross_time_reg {
- __le32 shadow_time_0;
- __le32 shadow_time_l;
- __le32 shadow_time_h;
- __le32 cmd_sync;
+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(16, virtchnl2_ptp_legacy_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_clk_reg_offsets);
/**
- * struct virtchnl2_ptp_ptm_cross_time_reg - PTM cross time registers 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_ptm_cross_time_reg {
- __le32 art_l;
- __le32 art_h;
- __le32 cmd_sync;
+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_ptm_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_cross_time_reg_offsets);
/**
- * struct virtchnl2_ptp_device_clock_control - Registers needed to control the
- * main clock.
+ * 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_device_clock_control {
- __le32 cmd;
- __le32 incval_l;
- __le32 incval_h;
- __le32 shadj_l;
- __le32 shadj_h;
- u8 pad[4];
+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(24, virtchnl2_ptp_device_clock_control);
+VIRTCHNL2_CHECK_STRUCT_LEN(40, virtchnl2_ptp_clk_adj_reg_offsets);
/**
- * struct virtchnl2_ptp_tx_tstamp_entry - PTP TX timestamp entry
- * @tx_latch_register_base: TX latch register base
- * @tx_latch_register_offset: TX latch register offset
- * @index: Index
- * @pad: Padding
+ * 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_entry {
- __le32 tx_latch_register_base;
- __le32 tx_latch_register_offset;
+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_entry);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch_caps);
/**
- * struct virtchnl2_ptp_tx_tstamp - Structure that defines tx tstamp entries
+ * struct virtchnl2_ptp_get_vport_tx_tstamp_caps - Structure that defines Tx
+ * tstamp entries
+ * @vport_id: Vport number
* @num_latches: Total number of latches
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
- * @ptp_tx_tstamp_entries: Aarray of TX timestamp entries
+ * @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_tx_tstamp {
+struct virtchnl2_ptp_get_vport_tx_tstamp_caps {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
- struct virtchnl2_ptp_tx_tstamp_entry ptp_tx_tstamp_entries[STRUCT_VAR_LEN];
+ u8 tstamp_ns_lo_bit;
+ u8 tstamp_ns_hi_bit;
+ u8 pad[8];
+
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp,
- ptp_tx_tstamp_entries);
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_caps,
+ tstamp_latches);
/**
- * struct virtchnl2_get_ptp_caps - Get PTP capabilities
- * @ptp_caps: PTP capability bitmap. See enum virtchnl2_ptp_caps.
- * @pad: Padding
- * @legacy_cross_time_reg: Legacy cross time register
- * @ptm_cross_time_reg: PTM cross time register
- * @device_clock_control: Device clock control
- * @tx_tstamp: TX timestamp
+ * 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
*
- * PV/VF sends this message to negotiate PTP capabilities. CP updates bitmap
+ * PF/VF sends this message to negotiate PTP capabilities. CP updates bitmap
* with supported features and fulfills appropriate structures.
- *
- * Associated with VIRTCHNL2_OP_GET_PTP_CAPS.
- */
-struct virtchnl2_get_ptp_caps {
- __le32 ptp_caps;
+ * 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_legacy_cross_time_reg legacy_cross_time_reg;
- struct virtchnl2_ptp_ptm_cross_time_reg ptm_cross_time_reg;
- struct virtchnl2_ptp_device_clock_control device_clock_control;
- struct virtchnl2_ptp_tx_tstamp tx_tstamp;
+ 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_VAR_LEN(88, virtchnl2_get_ptp_caps,
- tx_tstamp.ptp_tx_tstamp_entries);
+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_h: Timestamp high
- * @tstamp_l: Timestamp low
+ * @tstamp: Timestamp value
* @index: Index
* @valid: Timestamp validity
- * @pad: Padding
+ * @pad: Padding for future extensions
*/
struct virtchnl2_ptp_tx_tstamp_latch {
- __le32 tstamp_h;
- __le32 tstamp_l;
+ __le64 tstamp;
u8 index;
u8 valid;
u8 pad[6];
@@ -1938,26 +2001,97 @@ struct virtchnl2_ptp_tx_tstamp_latch {
VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch);
/**
- * struct virtchnl2_ptp_tx_tstamp_latches - PTP TX timestamp latches
+ * 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
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
+ * @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_GET_PTP_TX_TSTAMP_LATCHES.
+ * Associated with VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP.
*/
-struct virtchnl2_ptp_tx_tstamp_latches {
+struct virtchnl2_ptp_get_vport_tx_tstamp_latches {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
+ u8 get_devtime_with_txtstmp;
+ u8 pad[1];
+ u64 device_time;
struct virtchnl2_ptp_tx_tstamp_latch tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp_latches,
+
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_latches,
tstamp_latches);
+/* VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME
+ * @dev_time_ns: Device clock time value in nanoseconds
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the time from the main timer
+ */
+struct virtchnl2_ptp_get_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_dev_clk_time);
+
+/* 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
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the cross time
+ */
+struct virtchnl2_ptp_get_cross_time {
+ __le64 sys_time_ns;
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_get_cross_time);
+
+/* VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME
+ * @dev_time_ns: Device time value expressed in nanoseconds to set
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to set the time of the main timer
+ */
+struct virtchnl2_ptp_set_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_set_dev_clk_time);
+
+/* 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);
+
+/* 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);
+
static inline const char *virtchnl2_op_str(__le32 v_opcode)
{
switch (v_opcode) {
@@ -2025,10 +2159,22 @@ static inline const char *virtchnl2_op_str(__le32 v_opcode)
return "VIRTCHNL2_OP_DEL_QUEUE_GROUPS";
case VIRTCHNL2_OP_GET_PORT_STATS:
return "VIRTCHNL2_OP_GET_PORT_STATS";
- case VIRTCHNL2_OP_GET_PTP_CAPS:
- return "VIRTCHNL2_OP_GET_PTP_CAPS";
- case VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES:
- return "VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES";
+ case VIRTCHNL2_OP_PTP_GET_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_CAPS";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP";
+ case VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_CROSS_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_CROSS_TIME";
+ case VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS";
default:
return "Unsupported (update virtchnl2.h)";
}
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v7 2/4] net/idpf: add PTP virtchnl2 support
2026-02-27 0:33 ` [PATCH v7 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-02-27 0:33 ` [PATCH v7 1/4] net/idpf/base: add a new API for PTP support Soumyadeep Hore
@ 2026-02-27 0:33 ` Soumyadeep Hore
2026-02-26 12:00 ` Kumar, Rajesh3
2026-02-27 0:33 ` [PATCH v7 3/4] net/idpf: add timesync support Soumyadeep Hore
2026-02-27 0:33 ` [PATCH v7 4/4] doc/guides: add PTP IDPF documentation Soumyadeep Hore
3 siblings, 1 reply; 57+ messages in thread
From: Soumyadeep Hore @ 2026-02-27 0:33 UTC (permalink / raw)
To: dev, bruce.richardson, rajesh3.kumar, aman.deep.singh,
manoj.kumar.subbarao
Add a new file - idpf_ptp - to handle PTP virtchnl messages.
Keep the registers addresses in the PTP struct.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 31 +-
drivers/net/intel/idpf/idpf_ptp.c | 504 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 130 +++++
drivers/net/intel/idpf/meson.build | 1 +
5 files changed, 668 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
diff --git a/drivers/net/intel/idpf/idpf_common_device.h b/drivers/net/intel/idpf/idpf_common_device.h
index 31915a03d4..e45795c18b 100644
--- a/drivers/net/intel/idpf/idpf_common_device.h
+++ b/drivers/net/intel/idpf/idpf_common_device.h
@@ -103,6 +103,7 @@ struct idpf_adapter {
/* For timestamp */
uint64_t time_hw;
+ struct idpf_ptp *ptp;
enum idpf_rx_func_type rx_func_type;
enum idpf_tx_func_type tx_func_type;
@@ -173,6 +174,9 @@ struct idpf_vport {
/* Event from ipf */
bool link_up;
uint32_t link_speed;
+
+ /* For PTP */
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
};
/* Message type read in virtual channel from PF */
diff --git a/drivers/net/intel/idpf/idpf_common_virtchnl.c b/drivers/net/intel/idpf/idpf_common_virtchnl.c
index e927d7415a..f8c487c537 100644
--- a/drivers/net/intel/idpf/idpf_common_virtchnl.c
+++ b/drivers/net/intel/idpf/idpf_common_virtchnl.c
@@ -4,6 +4,7 @@
#include "idpf_common_virtchnl.h"
#include "idpf_common_logs.h"
+#include "idpf_ptp.h"
#include <eal_export.h>
@@ -38,6 +39,24 @@ idpf_vc_clean(struct idpf_adapter *adapter)
return 0;
}
+ /* Helper to detect PTP-related mailbox messages */
+static inline bool
+idpf_mb_msg_is_ptp(uint32_t op)
+{
+ switch (op) {
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ 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:
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int
idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
uint16_t msg_size, uint8_t *msg)
@@ -71,8 +90,16 @@ idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
memcpy(dma_mem->va, msg, msg_size);
- ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
- ctlq_msg->func_id = 0;
+ if (adapter->ptp != NULL &&
+ idpf_mb_msg_is_ptp(op) &&
+ adapter->ptp->secondary_mbx.valid) {
+ 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 {
+ ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
+ ctlq_msg->func_id = 0;
+ }
ctlq_msg->data_len = msg_size;
ctlq_msg->cookie.mbx.chnl_opcode = op;
ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
diff --git a/drivers/net/intel/idpf/idpf_ptp.c b/drivers/net/intel/idpf/idpf_ptp.c
new file mode 100644
index 0000000000..5c3fcb90e4
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.c
@@ -0,0 +1,504 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#include "idpf_ptp.h"
+#include "idpf_common_virtchnl.h"
+#include "base/virtchnl2.h"
+
+static enum idpf_ptp_access
+idpf_ptp_get_access(const struct idpf_adapter *adapter, uint32_t direct,
+ uint32_t 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;
+}
+
+#define IDPF_PTP_ACCESS(adapter, cap) \
+ ((uint8_t)idpf_ptp_get_access((adapter), (cap), (cap##_MB)))
+
+static void
+idpf_ptp_get_features_access(const struct idpf_adapter *adapter)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+
+ ptp->get_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME);
+ ptp->get_cross_tstamp_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_GET_CROSS_TIME);
+ ptp->set_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME);
+ ptp->adj_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK);
+ ptp->tx_tstamp_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_TX_TSTAMPS);
+}
+
+int
+idpf_ptp_get_caps(struct idpf_adapter *adapter)
+{
+ struct virtchnl2_ptp_cross_time_reg_offsets cross_tstamp_offsets;
+ struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
+ struct virtchnl2_ptp_get_caps send_ptp_caps_msg = {0};
+ struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
+ struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
+ struct idpf_cmd_info args = {0};
+ struct idpf_ptp_secondary_mbx *scnd_mbx;
+ struct idpf_ptp *ptp = adapter->ptp;
+ struct idpf_hw *hw = &adapter->hw;
+ enum idpf_ptp_access access_type;
+ int err;
+ uint32_t temp_offset;
+
+ 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 |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CAPS;
+ args.in_args = (uint8_t *)&send_ptp_caps_msg;
+ args.in_args_size = sizeof(send_ptp_caps_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(*recv_ptp_caps_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
+ 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);
+ scnd_mbx->valid = scnd_mbx->peer_mbx_q_id != 0xffff;
+ if (scnd_mbx->valid)
+ scnd_mbx->peer_id = recv_ptp_caps_msg->peer_id;
+
+ idpf_ptp_get_features_access(adapter);
+
+ access_type = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.dev_clk_ns_h);
+ ptp->dev_clk_regs.dev_clk_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_l);
+ ptp->dev_clk_regs.phy_clk_ns_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_h);
+ ptp->dev_clk_regs.phy_clk_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ access_type = (enum idpf_ptp_access)ptp->get_cross_tstamp_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.sys_time_ns_h);
+ ptp->dev_clk_regs.sys_time_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ access_type = (enum idpf_ptp_access)ptp->adj_dev_clk_time_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ clk_adj_offsets = recv_ptp_caps_msg->clk_adj_offsets;
+
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_cmd_type);
+ ptp->dev_clk_regs.cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_l);
+ ptp->dev_clk_regs.incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_h);
+ ptp->dev_clk_regs.incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_l);
+ ptp->dev_clk_regs.shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_h);
+ ptp->dev_clk_regs.shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_cmd_type);
+ ptp->dev_clk_regs.phy_cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_l);
+ ptp->dev_clk_regs.phy_incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_h);
+ ptp->dev_clk_regs.phy_incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_l);
+ ptp->dev_clk_regs.phy_shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_h);
+ ptp->dev_clk_regs.phy_shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ return err;
+}
+
+static void
+idpf_ptp_enable_shtime(struct idpf_adapter *adapter)
+{
+ uint32_t shtime_enable, exec_cmd;
+
+ shtime_enable = adapter->ptp->cmd.shtime_enable_mask;
+ exec_cmd = adapter->ptp->cmd.exec_cmd_mask;
+
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, shtime_enable);
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync,
+ exec_cmd | shtime_enable);
+}
+
+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 = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+ uint64_t dev_time;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&get_dev_clk_time_msg;
+ args.in_args_size = sizeof(get_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(get_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ get_dev_clk_time_msg =
+ *(struct virtchnl2_ptp_get_dev_clk_time *)args.out_buffer;
+ dev_time = LE64_TO_CPU(get_dev_clk_time_msg.dev_time_ns);
+ dev_clk_time->dev_clk_time_ns = dev_time;
+
+ return err;
+}
+
+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 = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CROSS_TIME;
+ args.in_args = (uint8_t *)&cross_time_msg;
+ args.in_args_size = sizeof(cross_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(cross_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ cross_time_msg =
+ *(struct virtchnl2_ptp_get_cross_time *)args.out_buffer;
+ 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 err;
+}
+
+int
+idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, uint64_t time)
+{
+ struct virtchnl2_ptp_set_dev_clk_time set_dev_clk_time_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ set_dev_clk_time_msg.dev_time_ns = CPU_TO_LE64(time);
+
+ args.ops = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&set_dev_clk_time_msg;
+ args.in_args_size = sizeof(set_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(set_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, int64_t delta)
+{
+ struct virtchnl2_ptp_adj_dev_clk_time adj_dev_clk_time_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ adj_dev_clk_time_msg.delta = CPU_TO_LE64(delta);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&adj_dev_clk_time_msg;
+ args.in_args_size = sizeof(adj_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t incval)
+{
+ struct virtchnl2_ptp_adj_dev_clk_fine adj_dev_clk_fine_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ adj_dev_clk_fine_msg.incval = CPU_TO_LE64(incval);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE;
+ args.in_args = (uint8_t *)&adj_dev_clk_fine_msg;
+ args.in_args_size = sizeof(adj_dev_clk_fine_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_fine_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
+{
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps send_tx_tstamp_caps = {0};
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps *rcv_tx_tstamp_caps;
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
+ enum idpf_ptp_access tstamp_access, get_dev_clk_access;
+ struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
+ struct idpf_ptp *ptp = vport->adapter->ptp;
+ struct idpf_cmd_info args = {0};
+ int err;
+ uint16_t num_latches, id;
+ uint32_t size;
+
+ if (ptp == NULL)
+ return -EOPNOTSUPP;
+
+ tstamp_access = (enum idpf_ptp_access)ptp->tx_tstamp_access;
+ get_dev_clk_access = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (tstamp_access == IDPF_PTP_NONE ||
+ get_dev_clk_access == IDPF_PTP_NONE)
+ return -EOPNOTSUPP;
+
+ send_tx_tstamp_caps.vport_id = CPU_TO_LE32(vport->vport_id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS;
+ args.in_args = (uint8_t *)&send_tx_tstamp_caps;
+ args.in_args_size = sizeof(send_tx_tstamp_caps);
+ args.out_size = IDPF_CTLQ_MAX_BUF_LEN;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ if (err < 0)
+ return err;
+
+ rcv_tx_tstamp_caps =
+ (struct virtchnl2_ptp_get_vport_tx_tstamp_caps *)args.out_buffer;
+ num_latches = LE16_TO_CPU(rcv_tx_tstamp_caps->num_latches);
+ size = sizeof(struct idpf_ptp_vport_tx_tstamp_caps) +
+ sizeof(struct idpf_ptp_tx_tstamp) * num_latches;
+ tstamp_caps = rte_zmalloc(NULL, size, 0);
+ if (tstamp_caps == NULL)
+ return -ENOMEM;
+
+ tstamp_caps->access = true;
+ tstamp_caps->num_entries = num_latches;
+ tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ for (id = 0; id < tstamp_caps->num_entries; id++) {
+ __le32 offset_l, offset_h;
+
+ tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[id];
+
+ if (tstamp_access == IDPF_PTP_DIRECT) {
+ offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
+ offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
+ tstamp_caps->tx_tstamp[id].tx_latch_reg_offset_l =
+ LE32_TO_CPU(offset_l);
+ tstamp_caps->tx_tstamp[id].tx_latch_reg_offset_h =
+ LE32_TO_CPU(offset_h);
+ }
+ tstamp_caps->tx_tstamp[id].idx = tx_tstamp_latch_caps.index;
+ }
+
+ tstamp_caps->latched_idx = -1;
+ vport->tx_tstamp_caps = tstamp_caps;
+
+ return err;
+}
+
+static int
+idpf_ptp_get_tstamp_value(struct idpf_vport *vport,
+ struct virtchnl2_ptp_tx_tstamp_latch *tstamp_latch,
+ struct idpf_ptp_tx_tstamp *tx_tstamp)
+{
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ uint8_t tstamp_ns_lo_bit;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ tx_tstamp->tstamp = LE64_TO_CPU(tstamp_latch->tstamp);
+ tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
+
+ return 0;
+}
+
+int
+idpf_ptp_get_tx_tstamp(struct idpf_vport *vport)
+{
+ struct virtchnl2_ptp_get_vport_tx_tstamp_latches *send_tx_tstamp_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 *ptp_tx_tstamp;
+ struct idpf_cmd_info args = {0};
+ int size, msg_size;
+ uint32_t vport_id;
+ uint16_t num_latches, id;
+ int err;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+
+ size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) *
+ tx_tstamp_caps->num_entries;
+ send_tx_tstamp_msg = rte_zmalloc(NULL, size, 0);
+ if (send_tx_tstamp_msg == NULL)
+ return -ENOMEM;
+
+ for (id = 0; id < tx_tstamp_caps->num_entries; id++, ptp_tx_tstamp++)
+ send_tx_tstamp_msg->tstamp_latches[id].index = ptp_tx_tstamp->idx;
+ send_tx_tstamp_msg->get_devtime_with_txtstmp = 1;
+
+ msg_size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) * id;
+ send_tx_tstamp_msg->vport_id = CPU_TO_LE32(vport->vport_id);
+ send_tx_tstamp_msg->num_latches = CPU_TO_LE16(id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP;
+ args.in_args = (uint8_t *)send_tx_tstamp_msg;
+ args.in_args_size = msg_size;
+ args.out_size = msg_size;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ rte_free(send_tx_tstamp_msg);
+ if (err < 0)
+ return err;
+
+ recv_tx_tstamp_msg =
+ (struct virtchnl2_ptp_get_vport_tx_tstamp_latches *)args.out_buffer;
+ vport_id = LE32_TO_CPU(recv_tx_tstamp_msg->vport_id);
+ if (vport->vport_id != vport_id)
+ return -EINVAL;
+
+ num_latches = LE16_TO_CPU(recv_tx_tstamp_msg->num_latches);
+
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+ for (id = 0; id < num_latches; id++, ptp_tx_tstamp++) {
+ tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[id];
+
+ if (!tstamp_latch.valid)
+ continue;
+
+ err = idpf_ptp_get_tstamp_value(vport, &tstamp_latch,
+ ptp_tx_tstamp);
+ if (err == 0) {
+ tx_tstamp_caps->latched_idx = id;
+ vport->adapter->time_hw = recv_tx_tstamp_msg->device_time;
+ }
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer value
+ * @adapter: Driver specific private structure
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int
+idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter, uint64_t *src_clk)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint32_t hi, lo;
+
+ idpf_ptp_enable_shtime(adapter);
+
+ lo = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_l);
+ hi = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_h);
+
+ *src_clk = ((uint64_t)hi << 32) | lo;
+ return 0;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value through mailbox
+ * @adapter: Driver specific private structure
+ * @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,
+ uint64_t *src_clk)
+{
+ struct idpf_ptp_dev_timers clk_time;
+ int err;
+
+ err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
+ if (err)
+ return err;
+
+ *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
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int
+idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t *src_clk)
+{
+ if (adapter->ptp == NULL)
+ return -EINVAL;
+
+ switch ((enum idpf_ptp_access)adapter->ptp->get_dev_clk_time_access) {
+ case IDPF_PTP_MAILBOX:
+ return idpf_ptp_read_src_clk_reg_mailbox(adapter, src_clk);
+ case IDPF_PTP_DIRECT:
+ return idpf_ptp_read_src_clk_reg_direct(adapter, src_clk);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
diff --git a/drivers/net/intel/idpf/idpf_ptp.h b/drivers/net/intel/idpf/idpf_ptp.h
new file mode 100644
index 0000000000..0c901440fb
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#ifndef _IDPF_PTP_H_
+#define _IDPF_PTP_H_
+
+#include "base/idpf_osdep.h"
+#include <rte_time.h>
+#include "idpf_common_device.h"
+
+struct idpf_ptp_cmd {
+ uint32_t exec_cmd_mask;
+ uint32_t shtime_enable_mask;
+};
+
+struct idpf_ptp_dev_clk_regs {
+ volatile uint32_t *dev_clk_ns_l;
+ volatile uint32_t *dev_clk_ns_h;
+ volatile uint32_t *phy_clk_ns_l;
+ volatile uint32_t *phy_clk_ns_h;
+ volatile uint32_t *sys_time_ns_l;
+ volatile uint32_t *sys_time_ns_h;
+ volatile uint32_t *incval_l;
+ volatile uint32_t *incval_h;
+ volatile uint32_t *shadj_l;
+ volatile uint32_t *shadj_h;
+ volatile uint32_t *phy_incval_l;
+ volatile uint32_t *phy_incval_h;
+ volatile uint32_t *phy_shadj_l;
+ volatile uint32_t *phy_shadj_h;
+ volatile uint32_t *cmd;
+ volatile uint32_t *phy_cmd;
+ volatile uint32_t *cmd_sync;
+};
+
+enum idpf_ptp_access {
+ IDPF_PTP_NONE = 0,
+ IDPF_PTP_DIRECT,
+ IDPF_PTP_MAILBOX,
+};
+
+struct idpf_ptp_secondary_mbx {
+ uint16_t peer_mbx_q_id;
+ uint16_t peer_id;
+ bool valid:1;
+};
+
+enum idpf_ptp_tx_tstamp_state {
+ IDPF_PTP_FREE,
+ IDPF_PTP_REQUEST,
+ IDPF_PTP_READ_VALUE,
+};
+
+struct idpf_ptp_tx_tstamp {
+ uint64_t tstamp;
+ uint32_t tx_latch_reg_offset_l;
+ uint32_t tx_latch_reg_offset_h;
+ uint32_t idx;
+};
+
+struct idpf_ptp_vport_tx_tstamp_caps {
+ uint32_t vport_id;
+ uint16_t num_entries;
+ uint16_t tstamp_ns_lo_bit;
+ uint16_t latched_idx;
+ bool access:1;
+ struct idpf_ptp_tx_tstamp tx_tstamp[];
+};
+
+struct idpf_ptp {
+ uint64_t base_incval;
+ uint64_t max_adj;
+ struct idpf_ptp_cmd cmd;
+ struct idpf_ptp_dev_clk_regs dev_clk_regs;
+ uint32_t caps;
+ uint8_t get_dev_clk_time_access:2;
+ uint8_t get_cross_tstamp_access:2;
+ uint8_t set_dev_clk_time_access:2;
+ uint8_t adj_dev_clk_time_access:2;
+ uint8_t tx_tstamp_access:2;
+ uint8_t rsv:6;
+ struct idpf_ptp_secondary_mbx secondary_mbx;
+};
+
+struct idpf_ptp_dev_timers {
+ uint64_t sys_time_ns;
+ uint64_t dev_clk_time_ns;
+};
+
+int idpf_ptp_get_caps(struct idpf_adapter *adapter);
+int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t *src_clk);
+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, uint64_t time);
+int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t incval);
+int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, int64_t delta);
+int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport);
+int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
+
+static inline uint64_t
+idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
+ bool is_rx, uint32_t in_timestamp)
+{
+ const uint64_t mask = 0xFFFFFFFFULL;
+ uint32_t phc_time_lo, delta;
+ uint64_t ns;
+
+ if (flag != 0)
+ idpf_ptp_read_src_clk_reg(ad, &ad->time_hw);
+
+ phc_time_lo = (uint32_t)(ad->time_hw);
+ delta = in_timestamp - phc_time_lo;
+
+ if (delta > mask / 2) {
+ delta = phc_time_lo - in_timestamp;
+ ns = ad->time_hw - delta;
+ } else {
+ if (is_rx)
+ ns = ad->time_hw - delta;
+ else
+ ns = ad->time_hw + delta;
+ }
+
+ return ns;
+}
+
+#endif /* _IDPF_PTP_H_ */
diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
index a805d02ea2..740c1a5985 100644
--- a/drivers/net/intel/idpf/meson.build
+++ b/drivers/net/intel/idpf/meson.build
@@ -17,6 +17,7 @@ sources += files(
'idpf_common_virtchnl.c',
'idpf_ethdev.c',
+ 'idpf_ptp.c',
'idpf_rxtx.c',
)
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v7 3/4] net/idpf: add timesync support
2026-02-27 0:33 ` [PATCH v7 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-02-27 0:33 ` [PATCH v7 1/4] net/idpf/base: add a new API for PTP support Soumyadeep Hore
2026-02-27 0:33 ` [PATCH v7 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
@ 2026-02-27 0:33 ` Soumyadeep Hore
2026-02-27 0:33 ` [PATCH v7 4/4] doc/guides: add PTP IDPF documentation Soumyadeep Hore
3 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-02-27 0:33 UTC (permalink / raw)
To: dev, bruce.richardson, rajesh3.kumar, aman.deep.singh,
manoj.kumar.subbarao
Add timesync support including:
- Capability negotiation with the Control Plane
to determine supported PTP features.
- Get/set/adjust operations on the main device
clock.
- Tx timestamping support using a PTP-dedicated
secondary mailbox.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Acked-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 121 +++++-----
drivers/net/intel/idpf/idpf_common_rxtx.h | 7 +
drivers/net/intel/idpf/idpf_ethdev.c | 270 ++++++++++++++++++++++
4 files changed, 338 insertions(+), 61 deletions(-)
diff --git a/drivers/net/intel/common/tx.h b/drivers/net/intel/common/tx.h
index e295d83e3a..dc964f3dd1 100644
--- a/drivers/net/intel/common/tx.h
+++ b/drivers/net/intel/common/tx.h
@@ -115,6 +115,7 @@ struct ci_tx_queue {
struct ci_tx_queue *complq;
void **txqs; /*only valid for split queue mode*/
uint32_t tx_start_qid;
+ uint32_t latch_idx; /* Tx timestamp latch index */
uint16_t sw_nb_desc;
uint16_t sw_tail;
uint16_t rs_compl_count;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.c b/drivers/net/intel/idpf/idpf_common_rxtx.c
index 797ee515dd..11c9dfc4fc 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.c
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.c
@@ -9,6 +9,7 @@
#include "idpf_common_rxtx.h"
#include "idpf_common_device.h"
#include "../common/rx.h"
+#include "idpf_ptp.h"
int idpf_timestamp_dynfield_offset = -1;
uint64_t idpf_timestamp_dynflag;
@@ -435,58 +436,6 @@ idpf_qc_split_rxq_mbufs_alloc(struct idpf_rx_queue *rxq)
return 0;
}
-#define IDPF_TIMESYNC_REG_WRAP_GUARD_BAND 10000
-/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
-static inline uint64_t
-idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
- uint32_t in_timestamp)
-{
-#ifdef RTE_ARCH_X86_64
- struct idpf_hw *hw = &ad->hw;
- const uint64_t mask = 0xFFFFFFFF;
- uint32_t hi, lo, lo2, delta;
- uint64_t ns;
-
- if (flag != 0) {
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_EXEC_CMD_M |
- PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- /*
- * On typical system, the delta between lo and lo2 is ~1000ns,
- * so 10000 seems a large-enough but not overly-big guard band.
- */
- if (lo > (UINT32_MAX - IDPF_TIMESYNC_REG_WRAP_GUARD_BAND))
- lo2 = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- else
- lo2 = lo;
-
- if (lo2 < lo) {
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- }
-
- ad->time_hw = ((uint64_t)hi << 32) | lo;
- }
-
- delta = (in_timestamp - (uint32_t)(ad->time_hw & mask));
- if (delta > (mask / 2)) {
- delta = ((uint32_t)(ad->time_hw & mask) - in_timestamp);
- ns = ad->time_hw - delta;
- } else {
- ns = ad->time_hw + delta;
- }
-
- return ns;
-#else /* !RTE_ARCH_X86_64 */
- RTE_SET_USED(ad);
- RTE_SET_USED(flag);
- RTE_SET_USED(in_timestamp);
- return 0;
-#endif /* RTE_ARCH_X86_64 */
-}
-
#define IDPF_RX_FLEX_DESC_ADV_STATUS0_XSUM_S \
(RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S) | \
RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S) | \
@@ -732,20 +681,27 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(rte_le_to_cpu_16(rx_desc->ptype_err_fflags0) &
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M) >>
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_S];
+
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
status_err0_qw1 = rte_le_to_cpu_16(rx_desc->status_err0_qw1);
pkt_flags = idpf_splitq_rx_csum_offload(status_err0_qw1);
pkt_flags |= idpf_splitq_rx_rss_offload(rxm, rx_desc);
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP)) {
/* timestamp */
+ rxq->time_high = rte_le_to_cpu_32(rx_desc->ts_high);
ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rx_desc->ts_high));
+ rxq->hw_register_set, true,
+ rxq->time_high);
rxq->hw_register_set = 0;
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ if (rx_desc->ts_low & VIRTCHNL2_RX_FLEX_TSTAMP_VALID)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -839,10 +795,34 @@ idpf_split_tx_free(struct ci_tx_queue *cq)
static inline uint16_t
idpf_calc_context_desc(uint64_t flags)
{
- if ((flags & RTE_MBUF_F_TX_TCP_SEG) != 0)
- return 1;
+ static uint64_t mask = RTE_MBUF_F_TX_TCP_SEG |
+ RTE_MBUF_F_TX_IEEE1588_TMST;
- return 0;
+ return (flags & mask) ? 1 : 0;
+}
+
+/**
+ * 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 inline void
+idpf_tx_set_tstamp_desc(volatile union idpf_flex_tx_ctx_desc *ctx_desc,
+ uint32_t idx)
+{
+ ctx_desc->tsyn.qw1.cmd_dtype =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_CMD_M,
+ IDPF_TX_CTX_DESC_TSYN));
+ ctx_desc->tsyn.qw1.cmd_dtype |=
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TXD_QW1_DTYPE_M,
+ IDPF_TX_DESC_DTYPE_CTX));
+ ctx_desc->tsyn.qw1.tsyn_reg_l =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_L_M,
+ idx));
+ ctx_desc->tsyn.qw1.tsyn_reg_h =
+ rte_cpu_to_le_16(FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_H_M,
+ idx >> 2));
}
/* set TSO context descriptor
@@ -948,6 +928,9 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_desc);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_desc, txq->latch_idx);
+
tx_id++;
if (tx_id == txq->nb_tx_desc)
tx_id = 0;
@@ -1167,17 +1150,22 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rxm->ol_flags |= pkt_flags;
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
+ rxq->hw_register_set, true,
+ rxq->time_high);
rxq->hw_register_set = 0;
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
rxm->ol_flags |= idpf_timestamp_dynflag;
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
rx_pkts[nb_rx++] = rxm;
@@ -1218,6 +1206,9 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
if (unlikely(!rxq) || unlikely(!rxq->q_started))
return nb_rx;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
+ rxq->hw_register_set = 1;
+
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
rx_status0 = rte_le_to_cpu_16(rxdp->flex_nic_wb.status_error0);
@@ -1298,17 +1289,22 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(uint8_t)(rte_cpu_to_le_16(rxd.flex_nic_wb.ptype_flex_flags0) &
VIRTCHNL2_RX_FLEX_DESC_PTYPE_M)];
+ if ((first_seg->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
+ rxq->hw_register_set, true,
+ rxq->time_high);
rxq->hw_register_set = 0;
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -1474,6 +1470,9 @@ idpf_dp_singleq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
idpf_set_splitq_tso_ctx(tx_pkt, tx_offload,
ctx_txd);
+ if ((ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST) != 0)
+ idpf_tx_set_tstamp_desc(ctx_txd, txq->latch_idx);
+
txe->last_id = tx_last;
tx_id = txe->next_id;
txe = txn;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.h b/drivers/net/intel/idpf/idpf_common_rxtx.h
index 7c6ff5d047..e1b8904181 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.h
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.h
@@ -13,6 +13,12 @@
#include "../common/tx.h"
#include "../common/rx.h"
+#define FIELD_PREP(_mask, _val) \
+ (__extension__ ({ \
+ typeof(_mask) _x = (_mask); \
+ ((typeof(_x))(_val) << rte_bsf32(_x)) & (_x); \
+ }))
+
#define IDPF_RX_MAX_BURST 32
#define IDPF_RX_OFFLOAD_IPV4_CKSUM RTE_BIT64(1)
@@ -167,6 +173,7 @@ struct idpf_rx_queue {
uint64_t offloads;
uint32_t hw_register_set;
+ uint32_t time_high; /* high 32 bits of hardware timestamp register */
};
/* Offload features */
diff --git a/drivers/net/intel/idpf/idpf_ethdev.c b/drivers/net/intel/idpf/idpf_ethdev.c
index 5d786fbba6..5e57a45775 100644
--- a/drivers/net/intel/idpf/idpf_ethdev.c
+++ b/drivers/net/intel/idpf/idpf_ethdev.c
@@ -14,6 +14,7 @@
#include "idpf_ethdev.h"
#include "idpf_rxtx.h"
#include "../common/tx.h"
+#include "idpf_ptp.h"
#define IDPF_TX_SINGLE_Q "tx_single"
#define IDPF_RX_SINGLE_Q "rx_single"
@@ -841,6 +842,267 @@ idpf_dev_close(struct rte_eth_dev *dev)
return 0;
}
+static int
+idpf_timesync_enable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct timespec sys_ts;
+ uint64_t ns;
+ int ret, q_id;
+ if (dev->data->dev_started && !(dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP)) {
+ PMD_DRV_LOG(ERR, "Rx timestamp offload not configured");
+ return -1;
+ }
+
+ adapter->ptp = rte_zmalloc(NULL, sizeof(struct idpf_ptp), 0);
+ if (adapter->ptp == NULL) {
+ PMD_DRV_LOG(ERR, "Failed to allocate memory for PTP");
+ return -ENOMEM;
+ }
+
+ ret = idpf_ptp_get_caps(adapter);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get PTP capabilities, err=%d", ret);
+ goto fail_ptp;
+ }
+
+ /* Write the default increment time value if the clock adjustments are enabled. */
+ if (adapter->ptp->adj_dev_clk_time_access != IDPF_PTP_NONE) {
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, adapter->ptp->base_incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set incval failed, err=%d", ret);
+ goto fail_ptp;
+ }
+ }
+
+ /* Do not initialize the PTP if the device clock time cannot be read. */
+ if (adapter->ptp->get_dev_clk_time_access == IDPF_PTP_NONE) {
+ PMD_DRV_LOG(ERR, "Getting device clock time is not supported");
+ ret = -EIO;
+ goto fail_ptp;
+ }
+
+ /* Set the device clock time to system time. */
+ if (adapter->ptp->set_dev_clk_time_access != IDPF_PTP_NONE) {
+ clock_gettime(CLOCK_REALTIME, &sys_ts);
+ ns = rte_timespec_to_ns(&sys_ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err=%d", ret);
+ goto fail_ptp;
+ }
+ }
+
+ ret = idpf_ptp_get_vport_tstamps_caps(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get vport timestamp capabilities, err=%d", ret);
+ goto fail_ptp;
+ }
+
+ for (q_id = 0; q_id < dev->data->nb_tx_queues; q_id++) {
+ struct ci_tx_queue *txq = dev->data->tx_queues[q_id];
+ txq->latch_idx = vport->tx_tstamp_caps->tx_tstamp[q_id].idx;
+ }
+
+ 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;
+
+ return 0;
+
+fail_ptp:
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ return ret;
+}
+
+static int
+idpf_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp,
+ uint32_t flags)
+{
+ struct idpf_rx_queue *rxq;
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ts_ns;
+
+ rxq = dev->data->rx_queues[flags];
+
+ ts_ns = idpf_tstamp_convert_32b_64b(adapter, 1, true, rxq->time_high);
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ return 0;
+}
+
+static int
+idpf_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ uint16_t latch_idx;
+ uint64_t ts_ns, tstamp;
+ int ret;
+
+ ret = idpf_ptp_get_tx_tstamp(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get TX timestamp");
+ return ret;
+ }
+
+ latch_idx = vport->tx_tstamp_caps->latched_idx;
+ tstamp = vport->tx_tstamp_caps->tx_tstamp[latch_idx].tstamp;
+ ts_ns = idpf_tstamp_convert_32b_64b(vport->adapter, 0, false, tstamp);
+
+ /* Convert to timespec */
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ vport->tx_tstamp_caps->latched_idx = -1;
+
+ return 0;
+}
+
+static int
+idpf_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint64_t time, ns;
+ int ret;
+
+ if (ptp->adj_dev_clk_time_access != IDPF_PTP_MAILBOX) {
+ PMD_DRV_LOG(ERR, "Adjusting device clock time is not supported");
+ return -ENOTSUP;
+ }
+
+ if (delta > INT32_MAX || delta < INT32_MIN) {
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP read clock time failed, err %d", ret);
+ return ret;
+ }
+
+ ns = time + delta;
+
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err %d", ret);
+
+ return ret;
+ }
+
+ ret = idpf_ptp_adj_dev_clk_time(adapter, delta);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP adjusting clock failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ int64_t incval, diff = 0;
+ bool negative = false;
+ uint64_t div, rem;
+ uint64_t divisor = 1000000ULL << 16;
+ int shift;
+ int ret;
+
+ incval = ptp->base_incval;
+
+ if (ppm < 0) {
+ negative = true;
+ ppm = -ppm;
+ }
+
+ /* can incval * ppm overflow ? */
+ if (rte_log2_u64(incval) + rte_log2_u64(ppm) > 62) {
+ rem = ppm % divisor;
+ div = ppm / divisor;
+ diff = div * incval;
+ ppm = rem;
+
+ shift = rte_log2_u64(incval) + rte_log2_u64(ppm) - 62;
+ if (shift > 0) {
+ /* drop precision */
+ ppm >>= shift;
+ divisor >>= shift;
+ }
+ }
+
+ if (divisor)
+ diff = diff + incval * ppm / divisor;
+
+ if (negative)
+ incval -= diff;
+ else
+ incval += diff;
+
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int
+idpf_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ns;
+ int ret;
+
+ ns = rte_timespec_to_ns(ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP write time failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t time;
+ int ret;
+
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP read time failed, err %d", ret);
+ else
+ *ts = rte_ns_to_timespec(time);
+
+ return ret;
+}
+
+static int
+idpf_timesync_disable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+
+ if (vport->tx_tstamp_caps) {
+ rte_free(vport->tx_tstamp_caps);
+ vport->tx_tstamp_caps = NULL;
+ }
+
+ if (adapter->ptp) {
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ }
+
+ return 0;
+}
+
+
static const struct eth_dev_ops idpf_eth_dev_ops = {
.dev_configure = idpf_dev_configure,
.dev_close = idpf_dev_close,
@@ -867,6 +1129,14 @@ static const struct eth_dev_ops idpf_eth_dev_ops = {
.xstats_get = idpf_dev_xstats_get,
.xstats_get_names = idpf_dev_xstats_get_names,
.xstats_reset = idpf_dev_xstats_reset,
+ .timesync_enable = idpf_timesync_enable,
+ .timesync_read_rx_timestamp = idpf_timesync_read_rx_timestamp,
+ .timesync_read_tx_timestamp = idpf_timesync_read_tx_timestamp,
+ .timesync_adjust_time = idpf_timesync_adjust_time,
+ .timesync_adjust_freq = idpf_timesync_adjust_freq,
+ .timesync_read_time = idpf_timesync_read_time,
+ .timesync_write_time = idpf_timesync_write_time,
+ .timesync_disable = idpf_timesync_disable,
};
static int
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v7 4/4] doc/guides: add PTP IDPF documentation
2026-02-27 0:33 ` [PATCH v7 0/4] Enable PTP feature for MEV Soumyadeep Hore
` (2 preceding siblings ...)
2026-02-27 0:33 ` [PATCH v7 3/4] net/idpf: add timesync support Soumyadeep Hore
@ 2026-02-27 0:33 ` Soumyadeep Hore
2026-03-01 9:10 ` [PATCH v8 0/4] Enable PTP feature for MEV Soumyadeep Hore
3 siblings, 1 reply; 57+ messages in thread
From: Soumyadeep Hore @ 2026-02-27 0:33 UTC (permalink / raw)
To: dev, bruce.richardson, rajesh3.kumar, aman.deep.singh,
manoj.kumar.subbarao
Updates the IDPF Documentation with PTP feature.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Acked-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
doc/guides/nics/features/idpf.ini | 1 +
doc/guides/nics/idpf.rst | 15 +++++++++++++++
doc/guides/rel_notes/release_26_03.rst | 4 ++++
3 files changed, 20 insertions(+)
diff --git a/doc/guides/nics/features/idpf.ini b/doc/guides/nics/features/idpf.ini
index 63b799289a..f546648924 100644
--- a/doc/guides/nics/features/idpf.ini
+++ b/doc/guides/nics/features/idpf.ini
@@ -19,6 +19,7 @@ RSS reta update = Y
L3 checksum offload = P
L4 checksum offload = P
Timestamp offload = P
+Timesync = Y
Packet type parsing = Y
Basic stats = Y
Linux = Y
diff --git a/doc/guides/nics/idpf.rst b/doc/guides/nics/idpf.rst
index b99cc18188..998c1a5dfa 100644
--- a/doc/guides/nics/idpf.rst
+++ b/doc/guides/nics/idpf.rst
@@ -109,3 +109,18 @@ The paths are chosen based on 2 conditions:
A value "P" means the offload feature is not supported by vector path.
If any not supported features are used, idpf vector PMD is disabled
and the scalar paths are chosen.
+
+ Time Synchronization
+ ~~~~~~~~~~~~~~~~~~~~
+
+ The system operator can run a PTP (Precision Time Protocol) client application
+ to synchronize the time on the network card in ACC
+ (and optionally the time on the system) to the PTP primary.
+
+ IDPF PMD supports PTP client applications that use the DPDK IEEE 1588 API
+ to communicate with the PTP primary clock.
+ Note that PTP client application needs to run on PF in ACC.
+
+ .. code-block:: console
+
+ examples/dpdk-ptpclient -l 0-3 -n 3 -a 0000:ec:00.1 -- -T 1 -p 0x1
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index afdf1af06c..16b5a0b0b1 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -63,6 +63,10 @@ New Features
* The timestamp value has been updated to make it usable.
+* **Updated Intel idpf ethernet driver.**
+
+ * Added support for time sync features.
+
* **Updated Intel iavf driver.**
* Added support for pre and post VF reset callbacks.
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v8 0/4] Enable PTP feature for MEV
2026-02-27 0:33 ` [PATCH v7 4/4] doc/guides: add PTP IDPF documentation Soumyadeep Hore
@ 2026-03-01 9:10 ` Soumyadeep Hore
2026-03-01 9:10 ` [PATCH v8 1/4] net/idpf/base: add a new API for PTP support Soumyadeep Hore
` (4 more replies)
0 siblings, 5 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-03-01 9:10 UTC (permalink / raw)
To: bruce.richardson, manoj.kumar.subbarao, aman.deep.singh, dev
Enabling basic PTP feature in IDPF PMD using virtchnl messages.
---
v8:
- Refactored code based on Bruce's commit 025b898
---
v7:
- Addressed Rajesh's comments
---
v6:
- Addressed Bruce's comments
- Addressed AI comments
---
v5:
- Addressed AI comments
---
v4:
- Addressed Bruce's comments
---
v3:
- Fixed checkpatch warnings
- Addressed Bruce's comments
---
v2:
- Fixed essential checkpatch warnings
---
Milena Olech (1):
net/idpf/base: add a new API for PTP support
Soumyadeep Hore (3):
net/idpf: add PTP virtchnl2 support
net/idpf: add timesync support
doc/guides: add PTP IDPF documentation
doc/guides/nics/features/idpf.ini | 1 +
doc/guides/nics/idpf.rst | 15 +
doc/guides/rel_notes/release_26_03.rst | 4 +
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/base/virtchnl2.h | 324 +++++++----
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 126 ++---
drivers/net/intel/idpf/idpf_common_rxtx.h | 7 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 31 +-
drivers/net/intel/idpf/idpf_ethdev.c | 270 ++++++++++
drivers/net/intel/idpf/idpf_ptp.c | 504 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 130 +++++
drivers/net/intel/idpf/meson.build | 1 +
13 files changed, 1252 insertions(+), 166 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
--
2.47.1
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH v8 1/4] net/idpf/base: add a new API for PTP support
2026-03-01 9:10 ` [PATCH v8 0/4] Enable PTP feature for MEV Soumyadeep Hore
@ 2026-03-01 9:10 ` Soumyadeep Hore
2026-03-01 9:10 ` [PATCH v8 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
` (3 subsequent siblings)
4 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-03-01 9:10 UTC (permalink / raw)
To: bruce.richardson, manoj.kumar.subbarao, aman.deep.singh, dev
Cc: Milena Olech, Rajesh Kumar
From: Milena Olech <milena.olech@intel.com>
Add PTP capabilities to determine which PTP features are enabled -
get/set the time, cross timestamp, adjust the clock and timestamping.
Create appropriate structures and opcodes to support features introduced by
capabilities.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Signed-off-by: Milena Olech <milena.olech@intel.com>
Acked-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
drivers/net/intel/idpf/base/virtchnl2.h | 324 +++++++++++++++++-------
1 file changed, 235 insertions(+), 89 deletions(-)
diff --git a/drivers/net/intel/idpf/base/virtchnl2.h b/drivers/net/intel/idpf/base/virtchnl2.h
index 6cfb4f56fa..0b01daee30 100644
--- a/drivers/net/intel/idpf/base/virtchnl2.h
+++ b/drivers/net/intel/idpf/base/virtchnl2.h
@@ -118,8 +118,14 @@ enum virtchnl2_op {
VIRTCHNL2_OP_DEL_QUEUE_GROUPS = 539,
VIRTCHNL2_OP_GET_PORT_STATS = 540,
/* TimeSync opcodes */
- VIRTCHNL2_OP_GET_PTP_CAPS = 541,
- VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES = 542,
+ 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,
};
#define VIRTCHNL2_RDMA_INVALID_QUEUE_IDX 0xFFFF
@@ -1813,123 +1819,180 @@ VIRTCHNL2_CHECK_STRUCT_LEN(8, virtchnl2_promisc_info);
* enum virtchnl2_ptp_caps - PTP capabilities
*/
enum virtchnl2_ptp_caps {
- VIRTCHNL2_PTP_CAP_LEGACY_CROSS_TIME = BIT(0),
- VIRTCHNL2_PTP_CAP_PTM = BIT(1),
- VIRTCHNL2_PTP_CAP_DEVICE_CLOCK_CONTROL = BIT(2),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_DIRECT = BIT(3),
- VIRTCHNL2_PTP_CAP_TX_TSTAMPS_VIRTCHNL = BIT(4),
-};
-
-/**
- * struct virtchnl2_ptp_legacy_cross_time_reg - Legacy cross time registers
- * offsets.
+ 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_legacy_cross_time_reg {
- __le32 shadow_time_0;
- __le32 shadow_time_l;
- __le32 shadow_time_h;
- __le32 cmd_sync;
+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(16, virtchnl2_ptp_legacy_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_clk_reg_offsets);
/**
- * struct virtchnl2_ptp_ptm_cross_time_reg - PTM cross time registers 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_ptm_cross_time_reg {
- __le32 art_l;
- __le32 art_h;
- __le32 cmd_sync;
+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_ptm_cross_time_reg);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_cross_time_reg_offsets);
/**
- * struct virtchnl2_ptp_device_clock_control - Registers needed to control the
- * main clock.
+ * 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_device_clock_control {
- __le32 cmd;
- __le32 incval_l;
- __le32 incval_h;
- __le32 shadj_l;
- __le32 shadj_h;
- u8 pad[4];
+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(24, virtchnl2_ptp_device_clock_control);
+VIRTCHNL2_CHECK_STRUCT_LEN(40, virtchnl2_ptp_clk_adj_reg_offsets);
/**
- * struct virtchnl2_ptp_tx_tstamp_entry - PTP TX timestamp entry
- * @tx_latch_register_base: TX latch register base
- * @tx_latch_register_offset: TX latch register offset
- * @index: Index
- * @pad: Padding
+ * 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_entry {
- __le32 tx_latch_register_base;
- __le32 tx_latch_register_offset;
+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_entry);
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch_caps);
/**
- * struct virtchnl2_ptp_tx_tstamp - Structure that defines tx tstamp entries
+ * struct virtchnl2_ptp_get_vport_tx_tstamp_caps - Structure that defines Tx
+ * tstamp entries
+ * @vport_id: Vport number
* @num_latches: Total number of latches
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
- * @ptp_tx_tstamp_entries: Aarray of TX timestamp entries
+ * @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_tx_tstamp {
+struct virtchnl2_ptp_get_vport_tx_tstamp_caps {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
- struct virtchnl2_ptp_tx_tstamp_entry ptp_tx_tstamp_entries[STRUCT_VAR_LEN];
+ u8 tstamp_ns_lo_bit;
+ u8 tstamp_ns_hi_bit;
+ u8 pad[8];
+
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp,
- ptp_tx_tstamp_entries);
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_caps,
+ tstamp_latches);
/**
- * struct virtchnl2_get_ptp_caps - Get PTP capabilities
- * @ptp_caps: PTP capability bitmap. See enum virtchnl2_ptp_caps.
- * @pad: Padding
- * @legacy_cross_time_reg: Legacy cross time register
- * @ptm_cross_time_reg: PTM cross time register
- * @device_clock_control: Device clock control
- * @tx_tstamp: TX timestamp
+ * 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
*
- * PV/VF sends this message to negotiate PTP capabilities. CP updates bitmap
+ * PF/VF sends this message to negotiate PTP capabilities. CP updates bitmap
* with supported features and fulfills appropriate structures.
- *
- * Associated with VIRTCHNL2_OP_GET_PTP_CAPS.
- */
-struct virtchnl2_get_ptp_caps {
- __le32 ptp_caps;
+ * 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_legacy_cross_time_reg legacy_cross_time_reg;
- struct virtchnl2_ptp_ptm_cross_time_reg ptm_cross_time_reg;
- struct virtchnl2_ptp_device_clock_control device_clock_control;
- struct virtchnl2_ptp_tx_tstamp tx_tstamp;
+ 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_VAR_LEN(88, virtchnl2_get_ptp_caps,
- tx_tstamp.ptp_tx_tstamp_entries);
+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_h: Timestamp high
- * @tstamp_l: Timestamp low
+ * @tstamp: Timestamp value
* @index: Index
* @valid: Timestamp validity
- * @pad: Padding
+ * @pad: Padding for future extensions
*/
struct virtchnl2_ptp_tx_tstamp_latch {
- __le32 tstamp_h;
- __le32 tstamp_l;
+ __le64 tstamp;
u8 index;
u8 valid;
u8 pad[6];
@@ -1938,26 +2001,97 @@ struct virtchnl2_ptp_tx_tstamp_latch {
VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_tx_tstamp_latch);
/**
- * struct virtchnl2_ptp_tx_tstamp_latches - PTP TX timestamp latches
+ * 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
- * @latch_size: Latch size expressed in bits
- * @pad: Padding
+ * @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_GET_PTP_TX_TSTAMP_LATCHES.
+ * Associated with VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP.
*/
-struct virtchnl2_ptp_tx_tstamp_latches {
+struct virtchnl2_ptp_get_vport_tx_tstamp_latches {
+ __le32 vport_id;
__le16 num_latches;
- __le16 latch_size;
- u8 pad[4];
+ u8 get_devtime_with_txtstmp;
+ u8 pad[1];
+ u64 device_time;
struct virtchnl2_ptp_tx_tstamp_latch tstamp_latches[STRUCT_VAR_LEN];
};
-VIRTCHNL2_CHECK_STRUCT_VAR_LEN(24, virtchnl2_ptp_tx_tstamp_latches,
+
+VIRTCHNL2_CHECK_STRUCT_VAR_LEN(32, virtchnl2_ptp_get_vport_tx_tstamp_latches,
tstamp_latches);
+/* VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME
+ * @dev_time_ns: Device clock time value in nanoseconds
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the time from the main timer
+ */
+struct virtchnl2_ptp_get_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_get_dev_clk_time);
+
+/* 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
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to receive the cross time
+ */
+struct virtchnl2_ptp_get_cross_time {
+ __le64 sys_time_ns;
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(24, virtchnl2_ptp_get_cross_time);
+
+/* VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME
+ * @dev_time_ns: Device time value expressed in nanoseconds to set
+ * @pad: Padding for future extensions
+ *
+ * PF/VF sends this message to set the time of the main timer
+ */
+struct virtchnl2_ptp_set_dev_clk_time {
+ __le64 dev_time_ns;
+ u8 pad[8];
+};
+
+VIRTCHNL2_CHECK_STRUCT_LEN(16, virtchnl2_ptp_set_dev_clk_time);
+
+/* 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);
+
+/* 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);
+
static inline const char *virtchnl2_op_str(__le32 v_opcode)
{
switch (v_opcode) {
@@ -2025,10 +2159,22 @@ static inline const char *virtchnl2_op_str(__le32 v_opcode)
return "VIRTCHNL2_OP_DEL_QUEUE_GROUPS";
case VIRTCHNL2_OP_GET_PORT_STATS:
return "VIRTCHNL2_OP_GET_PORT_STATS";
- case VIRTCHNL2_OP_GET_PTP_CAPS:
- return "VIRTCHNL2_OP_GET_PTP_CAPS";
- case VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES:
- return "VIRTCHNL2_OP_GET_PTP_TX_TSTAMP_LATCHES";
+ case VIRTCHNL2_OP_PTP_GET_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_CAPS";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP";
+ case VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_CROSS_TIME:
+ return "VIRTCHNL2_OP_PTP_GET_CROSS_TIME";
+ case VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE";
+ case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME:
+ return "VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME";
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return "VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS";
default:
return "Unsupported (update virtchnl2.h)";
}
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v8 2/4] net/idpf: add PTP virtchnl2 support
2026-03-01 9:10 ` [PATCH v8 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-03-01 9:10 ` [PATCH v8 1/4] net/idpf/base: add a new API for PTP support Soumyadeep Hore
@ 2026-03-01 9:10 ` Soumyadeep Hore
2026-03-01 9:10 ` [PATCH v8 3/4] net/idpf: add timesync support Soumyadeep Hore
` (2 subsequent siblings)
4 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-03-01 9:10 UTC (permalink / raw)
To: bruce.richardson, manoj.kumar.subbarao, aman.deep.singh, dev; +Cc: Rajesh Kumar
Add a new file - idpf_ptp - to handle PTP virtchnl messages.
Keep the registers addresses in the PTP struct.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Acked-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
drivers/net/intel/idpf/idpf_common_device.h | 4 +
drivers/net/intel/idpf/idpf_common_virtchnl.c | 31 +-
drivers/net/intel/idpf/idpf_ptp.c | 504 ++++++++++++++++++
drivers/net/intel/idpf/idpf_ptp.h | 130 +++++
drivers/net/intel/idpf/meson.build | 1 +
5 files changed, 668 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/intel/idpf/idpf_ptp.c
create mode 100644 drivers/net/intel/idpf/idpf_ptp.h
diff --git a/drivers/net/intel/idpf/idpf_common_device.h b/drivers/net/intel/idpf/idpf_common_device.h
index bbc969c734..c4da346b2c 100644
--- a/drivers/net/intel/idpf/idpf_common_device.h
+++ b/drivers/net/intel/idpf/idpf_common_device.h
@@ -104,6 +104,7 @@ struct idpf_adapter {
/* For timestamp */
uint64_t time_hw;
+ struct idpf_ptp *ptp;
enum idpf_rx_func_type rx_func_type;
enum idpf_tx_func_type tx_func_type;
@@ -174,6 +175,9 @@ struct idpf_vport {
/* Event from ipf */
bool link_up;
uint32_t link_speed;
+
+ /* For PTP */
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
};
/* Message type read in virtual channel from PF */
diff --git a/drivers/net/intel/idpf/idpf_common_virtchnl.c b/drivers/net/intel/idpf/idpf_common_virtchnl.c
index e927d7415a..f8c487c537 100644
--- a/drivers/net/intel/idpf/idpf_common_virtchnl.c
+++ b/drivers/net/intel/idpf/idpf_common_virtchnl.c
@@ -4,6 +4,7 @@
#include "idpf_common_virtchnl.h"
#include "idpf_common_logs.h"
+#include "idpf_ptp.h"
#include <eal_export.h>
@@ -38,6 +39,24 @@ idpf_vc_clean(struct idpf_adapter *adapter)
return 0;
}
+ /* Helper to detect PTP-related mailbox messages */
+static inline bool
+idpf_mb_msg_is_ptp(uint32_t op)
+{
+ switch (op) {
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
+ 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:
+ case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int
idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
uint16_t msg_size, uint8_t *msg)
@@ -71,8 +90,16 @@ idpf_send_vc_msg(struct idpf_adapter *adapter, uint32_t op,
memcpy(dma_mem->va, msg, msg_size);
- ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
- ctlq_msg->func_id = 0;
+ if (adapter->ptp != NULL &&
+ idpf_mb_msg_is_ptp(op) &&
+ adapter->ptp->secondary_mbx.valid) {
+ 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 {
+ ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_pf;
+ ctlq_msg->func_id = 0;
+ }
ctlq_msg->data_len = msg_size;
ctlq_msg->cookie.mbx.chnl_opcode = op;
ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
diff --git a/drivers/net/intel/idpf/idpf_ptp.c b/drivers/net/intel/idpf/idpf_ptp.c
new file mode 100644
index 0000000000..5c3fcb90e4
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.c
@@ -0,0 +1,504 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#include "idpf_ptp.h"
+#include "idpf_common_virtchnl.h"
+#include "base/virtchnl2.h"
+
+static enum idpf_ptp_access
+idpf_ptp_get_access(const struct idpf_adapter *adapter, uint32_t direct,
+ uint32_t 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;
+}
+
+#define IDPF_PTP_ACCESS(adapter, cap) \
+ ((uint8_t)idpf_ptp_get_access((adapter), (cap), (cap##_MB)))
+
+static void
+idpf_ptp_get_features_access(const struct idpf_adapter *adapter)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+
+ ptp->get_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_GET_DEVICE_CLK_TIME);
+ ptp->get_cross_tstamp_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_GET_CROSS_TIME);
+ ptp->set_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME);
+ ptp->adj_dev_clk_time_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK);
+ ptp->tx_tstamp_access =
+ IDPF_PTP_ACCESS(adapter, VIRTCHNL2_CAP_PTP_TX_TSTAMPS);
+}
+
+int
+idpf_ptp_get_caps(struct idpf_adapter *adapter)
+{
+ struct virtchnl2_ptp_cross_time_reg_offsets cross_tstamp_offsets;
+ struct virtchnl2_ptp_clk_adj_reg_offsets clk_adj_offsets;
+ struct virtchnl2_ptp_get_caps send_ptp_caps_msg = {0};
+ struct virtchnl2_ptp_clk_reg_offsets clock_offsets;
+ struct virtchnl2_ptp_get_caps *recv_ptp_caps_msg;
+ struct idpf_cmd_info args = {0};
+ struct idpf_ptp_secondary_mbx *scnd_mbx;
+ struct idpf_ptp *ptp = adapter->ptp;
+ struct idpf_hw *hw = &adapter->hw;
+ enum idpf_ptp_access access_type;
+ int err;
+ uint32_t temp_offset;
+
+ 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 |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME |
+ VIRTCHNL2_CAP_PTP_SET_DEVICE_CLK_TIME_MB |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK |
+ VIRTCHNL2_CAP_PTP_ADJ_DEVICE_CLK_MB |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS |
+ VIRTCHNL2_CAP_PTP_TX_TSTAMPS_MB);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CAPS;
+ args.in_args = (uint8_t *)&send_ptp_caps_msg;
+ args.in_args_size = sizeof(send_ptp_caps_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(*recv_ptp_caps_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ recv_ptp_caps_msg = (struct virtchnl2_ptp_get_caps *)args.out_buffer;
+ 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);
+ scnd_mbx->valid = scnd_mbx->peer_mbx_q_id != 0xffff;
+ if (scnd_mbx->valid)
+ scnd_mbx->peer_id = recv_ptp_caps_msg->peer_id;
+
+ idpf_ptp_get_features_access(adapter);
+
+ access_type = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.dev_clk_ns_h);
+ ptp->dev_clk_regs.dev_clk_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_l);
+ ptp->dev_clk_regs.phy_clk_ns_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.phy_clk_ns_h);
+ ptp->dev_clk_regs.phy_clk_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clock_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ access_type = (enum idpf_ptp_access)ptp->get_cross_tstamp_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ 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_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.sys_time_ns_h);
+ ptp->dev_clk_regs.sys_time_ns_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(cross_tstamp_offsets.cmd_sync_trigger);
+ ptp->dev_clk_regs.cmd_sync = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ access_type = (enum idpf_ptp_access)ptp->adj_dev_clk_time_access;
+ if (access_type == IDPF_PTP_DIRECT) {
+ clk_adj_offsets = recv_ptp_caps_msg->clk_adj_offsets;
+
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_cmd_type);
+ ptp->dev_clk_regs.cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_l);
+ ptp->dev_clk_regs.incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_incval_h);
+ ptp->dev_clk_regs.incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_l);
+ ptp->dev_clk_regs.shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.dev_clk_shadj_h);
+ ptp->dev_clk_regs.shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_cmd_type);
+ ptp->dev_clk_regs.phy_cmd = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_l);
+ ptp->dev_clk_regs.phy_incval_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_incval_h);
+ ptp->dev_clk_regs.phy_incval_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_l);
+ ptp->dev_clk_regs.phy_shadj_l = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ temp_offset = LE32_TO_CPU(clk_adj_offsets.phy_clk_shadj_h);
+ ptp->dev_clk_regs.phy_shadj_h = IDPF_PCI_REG_ADDR(hw, temp_offset);
+ }
+
+ return err;
+}
+
+static void
+idpf_ptp_enable_shtime(struct idpf_adapter *adapter)
+{
+ uint32_t shtime_enable, exec_cmd;
+
+ shtime_enable = adapter->ptp->cmd.shtime_enable_mask;
+ exec_cmd = adapter->ptp->cmd.exec_cmd_mask;
+
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync, shtime_enable);
+ IDPF_PCI_REG_WRITE(adapter->ptp->dev_clk_regs.cmd_sync,
+ exec_cmd | shtime_enable);
+}
+
+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 = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+ uint64_t dev_time;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&get_dev_clk_time_msg;
+ args.in_args_size = sizeof(get_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(get_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ get_dev_clk_time_msg =
+ *(struct virtchnl2_ptp_get_dev_clk_time *)args.out_buffer;
+ dev_time = LE64_TO_CPU(get_dev_clk_time_msg.dev_time_ns);
+ dev_clk_time->dev_clk_time_ns = dev_time;
+
+ return err;
+}
+
+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 = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_CROSS_TIME;
+ args.in_args = (uint8_t *)&cross_time_msg;
+ args.in_args_size = sizeof(cross_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(cross_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ cross_time_msg =
+ *(struct virtchnl2_ptp_get_cross_time *)args.out_buffer;
+ 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 err;
+}
+
+int
+idpf_ptp_set_dev_clk_time(struct idpf_adapter *adapter, uint64_t time)
+{
+ struct virtchnl2_ptp_set_dev_clk_time set_dev_clk_time_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ set_dev_clk_time_msg.dev_time_ns = CPU_TO_LE64(time);
+
+ args.ops = VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&set_dev_clk_time_msg;
+ args.in_args_size = sizeof(set_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(set_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, int64_t delta)
+{
+ struct virtchnl2_ptp_adj_dev_clk_time adj_dev_clk_time_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ adj_dev_clk_time_msg.delta = CPU_TO_LE64(delta);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME;
+ args.in_args = (uint8_t *)&adj_dev_clk_time_msg;
+ args.in_args_size = sizeof(adj_dev_clk_time_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_time_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t incval)
+{
+ struct virtchnl2_ptp_adj_dev_clk_fine adj_dev_clk_fine_msg = {0};
+ struct idpf_cmd_info args = {0};
+ int err;
+
+ adj_dev_clk_fine_msg.incval = CPU_TO_LE64(incval);
+
+ args.ops = VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE;
+ args.in_args = (uint8_t *)&adj_dev_clk_fine_msg;
+ args.in_args_size = sizeof(adj_dev_clk_fine_msg);
+ args.out_buffer = adapter->mbx_resp;
+ args.out_size = sizeof(adj_dev_clk_fine_msg);
+
+ err = idpf_vc_cmd_execute(adapter, &args);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+int
+idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport)
+{
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps send_tx_tstamp_caps = {0};
+ struct virtchnl2_ptp_get_vport_tx_tstamp_caps *rcv_tx_tstamp_caps;
+ struct virtchnl2_ptp_tx_tstamp_latch_caps tx_tstamp_latch_caps;
+ enum idpf_ptp_access tstamp_access, get_dev_clk_access;
+ struct idpf_ptp_vport_tx_tstamp_caps *tstamp_caps;
+ struct idpf_ptp *ptp = vport->adapter->ptp;
+ struct idpf_cmd_info args = {0};
+ int err;
+ uint16_t num_latches, id;
+ uint32_t size;
+
+ if (ptp == NULL)
+ return -EOPNOTSUPP;
+
+ tstamp_access = (enum idpf_ptp_access)ptp->tx_tstamp_access;
+ get_dev_clk_access = (enum idpf_ptp_access)ptp->get_dev_clk_time_access;
+ if (tstamp_access == IDPF_PTP_NONE ||
+ get_dev_clk_access == IDPF_PTP_NONE)
+ return -EOPNOTSUPP;
+
+ send_tx_tstamp_caps.vport_id = CPU_TO_LE32(vport->vport_id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS;
+ args.in_args = (uint8_t *)&send_tx_tstamp_caps;
+ args.in_args_size = sizeof(send_tx_tstamp_caps);
+ args.out_size = IDPF_CTLQ_MAX_BUF_LEN;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ if (err < 0)
+ return err;
+
+ rcv_tx_tstamp_caps =
+ (struct virtchnl2_ptp_get_vport_tx_tstamp_caps *)args.out_buffer;
+ num_latches = LE16_TO_CPU(rcv_tx_tstamp_caps->num_latches);
+ size = sizeof(struct idpf_ptp_vport_tx_tstamp_caps) +
+ sizeof(struct idpf_ptp_tx_tstamp) * num_latches;
+ tstamp_caps = rte_zmalloc(NULL, size, 0);
+ if (tstamp_caps == NULL)
+ return -ENOMEM;
+
+ tstamp_caps->access = true;
+ tstamp_caps->num_entries = num_latches;
+ tstamp_caps->tstamp_ns_lo_bit = rcv_tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ for (id = 0; id < tstamp_caps->num_entries; id++) {
+ __le32 offset_l, offset_h;
+
+ tx_tstamp_latch_caps = rcv_tx_tstamp_caps->tstamp_latches[id];
+
+ if (tstamp_access == IDPF_PTP_DIRECT) {
+ offset_l = tx_tstamp_latch_caps.tx_latch_reg_offset_l;
+ offset_h = tx_tstamp_latch_caps.tx_latch_reg_offset_h;
+ tstamp_caps->tx_tstamp[id].tx_latch_reg_offset_l =
+ LE32_TO_CPU(offset_l);
+ tstamp_caps->tx_tstamp[id].tx_latch_reg_offset_h =
+ LE32_TO_CPU(offset_h);
+ }
+ tstamp_caps->tx_tstamp[id].idx = tx_tstamp_latch_caps.index;
+ }
+
+ tstamp_caps->latched_idx = -1;
+ vport->tx_tstamp_caps = tstamp_caps;
+
+ return err;
+}
+
+static int
+idpf_ptp_get_tstamp_value(struct idpf_vport *vport,
+ struct virtchnl2_ptp_tx_tstamp_latch *tstamp_latch,
+ struct idpf_ptp_tx_tstamp *tx_tstamp)
+{
+ struct idpf_ptp_vport_tx_tstamp_caps *tx_tstamp_caps;
+ uint8_t tstamp_ns_lo_bit;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ tstamp_ns_lo_bit = tx_tstamp_caps->tstamp_ns_lo_bit;
+
+ tx_tstamp->tstamp = LE64_TO_CPU(tstamp_latch->tstamp);
+ tx_tstamp->tstamp >>= tstamp_ns_lo_bit;
+
+ return 0;
+}
+
+int
+idpf_ptp_get_tx_tstamp(struct idpf_vport *vport)
+{
+ struct virtchnl2_ptp_get_vport_tx_tstamp_latches *send_tx_tstamp_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 *ptp_tx_tstamp;
+ struct idpf_cmd_info args = {0};
+ int size, msg_size;
+ uint32_t vport_id;
+ uint16_t num_latches, id;
+ int err;
+
+ tx_tstamp_caps = vport->tx_tstamp_caps;
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+
+ size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) *
+ tx_tstamp_caps->num_entries;
+ send_tx_tstamp_msg = rte_zmalloc(NULL, size, 0);
+ if (send_tx_tstamp_msg == NULL)
+ return -ENOMEM;
+
+ for (id = 0; id < tx_tstamp_caps->num_entries; id++, ptp_tx_tstamp++)
+ send_tx_tstamp_msg->tstamp_latches[id].index = ptp_tx_tstamp->idx;
+ send_tx_tstamp_msg->get_devtime_with_txtstmp = 1;
+
+ msg_size = sizeof(struct virtchnl2_ptp_get_vport_tx_tstamp_latches) +
+ sizeof(struct virtchnl2_ptp_tx_tstamp_latch) * id;
+ send_tx_tstamp_msg->vport_id = CPU_TO_LE32(vport->vport_id);
+ send_tx_tstamp_msg->num_latches = CPU_TO_LE16(id);
+
+ args.ops = VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP;
+ args.in_args = (uint8_t *)send_tx_tstamp_msg;
+ args.in_args_size = msg_size;
+ args.out_size = msg_size;
+ args.out_buffer = vport->adapter->mbx_resp;
+
+ err = idpf_vc_cmd_execute(vport->adapter, &args);
+ rte_free(send_tx_tstamp_msg);
+ if (err < 0)
+ return err;
+
+ recv_tx_tstamp_msg =
+ (struct virtchnl2_ptp_get_vport_tx_tstamp_latches *)args.out_buffer;
+ vport_id = LE32_TO_CPU(recv_tx_tstamp_msg->vport_id);
+ if (vport->vport_id != vport_id)
+ return -EINVAL;
+
+ num_latches = LE16_TO_CPU(recv_tx_tstamp_msg->num_latches);
+
+ ptp_tx_tstamp = tx_tstamp_caps->tx_tstamp;
+ for (id = 0; id < num_latches; id++, ptp_tx_tstamp++) {
+ tstamp_latch = recv_tx_tstamp_msg->tstamp_latches[id];
+
+ if (!tstamp_latch.valid)
+ continue;
+
+ err = idpf_ptp_get_tstamp_value(vport, &tstamp_latch,
+ ptp_tx_tstamp);
+ if (err == 0) {
+ tx_tstamp_caps->latched_idx = id;
+ vport->adapter->time_hw = recv_tx_tstamp_msg->device_time;
+ }
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_direct - Read directly the main timer value
+ * @adapter: Driver specific private structure
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int
+idpf_ptp_read_src_clk_reg_direct(struct idpf_adapter *adapter, uint64_t *src_clk)
+{
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint32_t hi, lo;
+
+ idpf_ptp_enable_shtime(adapter);
+
+ lo = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_l);
+ hi = IDPF_PCI_REG(ptp->dev_clk_regs.dev_clk_ns_h);
+
+ *src_clk = ((uint64_t)hi << 32) | lo;
+ return 0;
+}
+
+/**
+ * idpf_ptp_read_src_clk_reg_mailbox - Read the main timer value through mailbox
+ * @adapter: Driver specific private structure
+ * @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,
+ uint64_t *src_clk)
+{
+ struct idpf_ptp_dev_timers clk_time;
+ int err;
+
+ err = idpf_ptp_get_dev_clk_time(adapter, &clk_time);
+ if (err)
+ return err;
+
+ *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
+ * @src_clk: Returned main timer value in nanoseconds unit
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int
+idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t *src_clk)
+{
+ if (adapter->ptp == NULL)
+ return -EINVAL;
+
+ switch ((enum idpf_ptp_access)adapter->ptp->get_dev_clk_time_access) {
+ case IDPF_PTP_MAILBOX:
+ return idpf_ptp_read_src_clk_reg_mailbox(adapter, src_clk);
+ case IDPF_PTP_DIRECT:
+ return idpf_ptp_read_src_clk_reg_direct(adapter, src_clk);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
diff --git a/drivers/net/intel/idpf/idpf_ptp.h b/drivers/net/intel/idpf/idpf_ptp.h
new file mode 100644
index 0000000000..0c901440fb
--- /dev/null
+++ b/drivers/net/intel/idpf/idpf_ptp.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#ifndef _IDPF_PTP_H_
+#define _IDPF_PTP_H_
+
+#include "base/idpf_osdep.h"
+#include <rte_time.h>
+#include "idpf_common_device.h"
+
+struct idpf_ptp_cmd {
+ uint32_t exec_cmd_mask;
+ uint32_t shtime_enable_mask;
+};
+
+struct idpf_ptp_dev_clk_regs {
+ volatile uint32_t *dev_clk_ns_l;
+ volatile uint32_t *dev_clk_ns_h;
+ volatile uint32_t *phy_clk_ns_l;
+ volatile uint32_t *phy_clk_ns_h;
+ volatile uint32_t *sys_time_ns_l;
+ volatile uint32_t *sys_time_ns_h;
+ volatile uint32_t *incval_l;
+ volatile uint32_t *incval_h;
+ volatile uint32_t *shadj_l;
+ volatile uint32_t *shadj_h;
+ volatile uint32_t *phy_incval_l;
+ volatile uint32_t *phy_incval_h;
+ volatile uint32_t *phy_shadj_l;
+ volatile uint32_t *phy_shadj_h;
+ volatile uint32_t *cmd;
+ volatile uint32_t *phy_cmd;
+ volatile uint32_t *cmd_sync;
+};
+
+enum idpf_ptp_access {
+ IDPF_PTP_NONE = 0,
+ IDPF_PTP_DIRECT,
+ IDPF_PTP_MAILBOX,
+};
+
+struct idpf_ptp_secondary_mbx {
+ uint16_t peer_mbx_q_id;
+ uint16_t peer_id;
+ bool valid:1;
+};
+
+enum idpf_ptp_tx_tstamp_state {
+ IDPF_PTP_FREE,
+ IDPF_PTP_REQUEST,
+ IDPF_PTP_READ_VALUE,
+};
+
+struct idpf_ptp_tx_tstamp {
+ uint64_t tstamp;
+ uint32_t tx_latch_reg_offset_l;
+ uint32_t tx_latch_reg_offset_h;
+ uint32_t idx;
+};
+
+struct idpf_ptp_vport_tx_tstamp_caps {
+ uint32_t vport_id;
+ uint16_t num_entries;
+ uint16_t tstamp_ns_lo_bit;
+ uint16_t latched_idx;
+ bool access:1;
+ struct idpf_ptp_tx_tstamp tx_tstamp[];
+};
+
+struct idpf_ptp {
+ uint64_t base_incval;
+ uint64_t max_adj;
+ struct idpf_ptp_cmd cmd;
+ struct idpf_ptp_dev_clk_regs dev_clk_regs;
+ uint32_t caps;
+ uint8_t get_dev_clk_time_access:2;
+ uint8_t get_cross_tstamp_access:2;
+ uint8_t set_dev_clk_time_access:2;
+ uint8_t adj_dev_clk_time_access:2;
+ uint8_t tx_tstamp_access:2;
+ uint8_t rsv:6;
+ struct idpf_ptp_secondary_mbx secondary_mbx;
+};
+
+struct idpf_ptp_dev_timers {
+ uint64_t sys_time_ns;
+ uint64_t dev_clk_time_ns;
+};
+
+int idpf_ptp_get_caps(struct idpf_adapter *adapter);
+int idpf_ptp_read_src_clk_reg(struct idpf_adapter *adapter, uint64_t *src_clk);
+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, uint64_t time);
+int idpf_ptp_adj_dev_clk_fine(struct idpf_adapter *adapter, uint64_t incval);
+int idpf_ptp_adj_dev_clk_time(struct idpf_adapter *adapter, int64_t delta);
+int idpf_ptp_get_vport_tstamps_caps(struct idpf_vport *vport);
+int idpf_ptp_get_tx_tstamp(struct idpf_vport *vport);
+
+static inline uint64_t
+idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
+ bool is_rx, uint32_t in_timestamp)
+{
+ const uint64_t mask = 0xFFFFFFFFULL;
+ uint32_t phc_time_lo, delta;
+ uint64_t ns;
+
+ if (flag != 0)
+ idpf_ptp_read_src_clk_reg(ad, &ad->time_hw);
+
+ phc_time_lo = (uint32_t)(ad->time_hw);
+ delta = in_timestamp - phc_time_lo;
+
+ if (delta > mask / 2) {
+ delta = phc_time_lo - in_timestamp;
+ ns = ad->time_hw - delta;
+ } else {
+ if (is_rx)
+ ns = ad->time_hw - delta;
+ else
+ ns = ad->time_hw + delta;
+ }
+
+ return ns;
+}
+
+#endif /* _IDPF_PTP_H_ */
diff --git a/drivers/net/intel/idpf/meson.build b/drivers/net/intel/idpf/meson.build
index a805d02ea2..740c1a5985 100644
--- a/drivers/net/intel/idpf/meson.build
+++ b/drivers/net/intel/idpf/meson.build
@@ -17,6 +17,7 @@ sources += files(
'idpf_common_virtchnl.c',
'idpf_ethdev.c',
+ 'idpf_ptp.c',
'idpf_rxtx.c',
)
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v8 3/4] net/idpf: add timesync support
2026-03-01 9:10 ` [PATCH v8 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-03-01 9:10 ` [PATCH v8 1/4] net/idpf/base: add a new API for PTP support Soumyadeep Hore
2026-03-01 9:10 ` [PATCH v8 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
@ 2026-03-01 9:10 ` Soumyadeep Hore
2026-03-01 9:10 ` [PATCH v8 4/4] doc/guides: add PTP IDPF documentation Soumyadeep Hore
2026-03-02 11:19 ` [PATCH v8 0/4] Enable PTP feature for MEV Bruce Richardson
4 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-03-01 9:10 UTC (permalink / raw)
To: bruce.richardson, manoj.kumar.subbarao, aman.deep.singh, dev; +Cc: Rajesh Kumar
Add timesync support including:
- Capability negotiation with the Control Plane
to determine supported PTP features.
- Get/set/adjust operations on the main device
clock.
- Tx timestamping support using a PTP-dedicated
secondary mailbox.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Acked-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
drivers/net/intel/common/tx.h | 1 +
drivers/net/intel/idpf/idpf_common_rxtx.c | 126 ++++------
drivers/net/intel/idpf/idpf_common_rxtx.h | 7 +
drivers/net/intel/idpf/idpf_ethdev.c | 270 ++++++++++++++++++++++
4 files changed, 329 insertions(+), 75 deletions(-)
diff --git a/drivers/net/intel/common/tx.h b/drivers/net/intel/common/tx.h
index 8fec8d7909..283bd58d5d 100644
--- a/drivers/net/intel/common/tx.h
+++ b/drivers/net/intel/common/tx.h
@@ -216,6 +216,7 @@ struct ci_tx_queue {
struct ci_tx_queue *complq;
void **txqs; /*only valid for split queue mode*/
uint32_t tx_start_qid;
+ uint32_t latch_idx; /* Tx timestamp latch index */
uint16_t sw_nb_desc;
uint16_t sw_tail;
uint16_t rs_compl_count;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.c b/drivers/net/intel/idpf/idpf_common_rxtx.c
index b8f6418d4a..ec020d4910 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.c
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.c
@@ -9,6 +9,7 @@
#include "idpf_common_rxtx.h"
#include "idpf_common_device.h"
+#include "idpf_ptp.h"
int idpf_timestamp_dynfield_offset = -1;
uint64_t idpf_timestamp_dynflag;
@@ -437,58 +438,6 @@ idpf_qc_split_rxq_mbufs_alloc(struct idpf_rx_queue *rxq)
return 0;
}
-#define IDPF_TIMESYNC_REG_WRAP_GUARD_BAND 10000
-/* Helper function to convert a 32b nanoseconds timestamp to 64b. */
-static inline uint64_t
-idpf_tstamp_convert_32b_64b(struct idpf_adapter *ad, uint32_t flag,
- uint32_t in_timestamp)
-{
-#ifdef RTE_ARCH_X86_64
- struct idpf_hw *hw = &ad->hw;
- const uint64_t mask = 0xFFFFFFFF;
- uint32_t hi, lo, lo2, delta;
- uint64_t ns;
-
- if (flag != 0) {
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- IDPF_WRITE_REG(hw, GLTSYN_CMD_SYNC_0_0, PF_GLTSYN_CMD_SYNC_EXEC_CMD_M |
- PF_GLTSYN_CMD_SYNC_SHTIME_EN_M);
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- /*
- * On typical system, the delta between lo and lo2 is ~1000ns,
- * so 10000 seems a large-enough but not overly-big guard band.
- */
- if (lo > (UINT32_MAX - IDPF_TIMESYNC_REG_WRAP_GUARD_BAND))
- lo2 = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- else
- lo2 = lo;
-
- if (lo2 < lo) {
- lo = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_L_0);
- hi = IDPF_READ_REG(hw, PF_GLTSYN_SHTIME_H_0);
- }
-
- ad->time_hw = ((uint64_t)hi << 32) | lo;
- }
-
- delta = (in_timestamp - (uint32_t)(ad->time_hw & mask));
- if (delta > (mask / 2)) {
- delta = ((uint32_t)(ad->time_hw & mask) - in_timestamp);
- ns = ad->time_hw - delta;
- } else {
- ns = ad->time_hw + delta;
- }
-
- return ns;
-#else /* !RTE_ARCH_X86_64 */
- RTE_SET_USED(ad);
- RTE_SET_USED(flag);
- RTE_SET_USED(in_timestamp);
- return 0;
-#endif /* RTE_ARCH_X86_64 */
-}
-
#define IDPF_RX_FLEX_DESC_ADV_STATUS0_XSUM_S \
(RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S) | \
RTE_BIT32(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S) | \
@@ -734,20 +683,27 @@ idpf_dp_splitq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(rte_le_to_cpu_16(rx_desc->ptype_err_fflags0) &
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M) >>
VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_S];
+
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
status_err0_qw1 = rte_le_to_cpu_16(rx_desc->status_err0_qw1);
pkt_flags = idpf_splitq_rx_csum_offload(status_err0_qw1);
pkt_flags |= idpf_splitq_rx_rss_offload(rxm, rx_desc);
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP)) {
/* timestamp */
+ rxq->time_high = rte_le_to_cpu_32(rx_desc->ts_high);
ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rx_desc->ts_high));
+ rxq->hw_register_set, true,
+ rxq->time_high);
rxq->hw_register_set = 0;
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ if (rx_desc->ts_low & VIRTCHNL2_RX_FLEX_TSTAMP_VALID)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -841,21 +797,20 @@ idpf_split_tx_free(struct ci_tx_queue *cq)
static inline uint16_t
idpf_calc_context_desc(uint64_t flags)
{
- if ((flags & RTE_MBUF_F_TX_TCP_SEG) != 0)
- return 1;
+ static uint64_t mask = RTE_MBUF_F_TX_TCP_SEG |
+ RTE_MBUF_F_TX_IEEE1588_TMST;
- return 0;
+ return (flags & mask) ? 1 : 0;
}
-/* set TSO context descriptor, returns 0 if no context needed, 1 if context set
+/* set a context descriptor, returns 0 if no context needed, 1 if context set
*/
static inline uint16_t
-idpf_set_tso_ctx(uint64_t ol_flags, const struct rte_mbuf *mbuf,
+idpf_get_context_desc(uint64_t ol_flags, const struct rte_mbuf *mbuf,
const union ci_tx_offload *tx_offload,
const struct ci_tx_queue *txq __rte_unused,
uint64_t *qw0, uint64_t *qw1)
{
- uint16_t cmd_dtype = IDPF_TX_DESC_DTYPE_FLEX_TSO_CTX | IDPF_TX_FLEX_CTX_DESC_CMD_TSO;
uint16_t tso_segsz = mbuf->tso_segsz;
uint32_t tso_len;
uint8_t hdr_len;
@@ -863,19 +818,27 @@ idpf_set_tso_ctx(uint64_t ol_flags, const struct rte_mbuf *mbuf,
if (idpf_calc_context_desc(ol_flags) == 0)
return 0;
- /* TSO context descriptor setup */
- if (tx_offload->l4_len == 0) {
- TX_LOG(DEBUG, "L4 length set to 0");
- return 0;
- }
+ if (ol_flags & RTE_MBUF_F_TX_TCP_SEG) {
+ /* TSO context descriptor setup */
+ if (tx_offload->l4_len == 0) {
+ TX_LOG(DEBUG, "L4 length set to 0");
+ return 0;
+ }
- hdr_len = tx_offload->l2_len + tx_offload->l3_len + tx_offload->l4_len;
- tso_len = mbuf->pkt_len - hdr_len;
+ hdr_len = tx_offload->l2_len + tx_offload->l3_len + tx_offload->l4_len;
+ tso_len = mbuf->pkt_len - hdr_len;
- *qw0 = rte_cpu_to_le_32(tso_len & IDPF_TXD_FLEX_CTX_MSS_RT_M) |
+ *qw0 = rte_cpu_to_le_32(tso_len & IDPF_TXD_FLEX_CTX_MSS_RT_M) |
((uint64_t)rte_cpu_to_le_16(tso_segsz & IDPF_TXD_FLEX_CTX_MSS_RT_M) << 32) |
((uint64_t)hdr_len << 48);
- *qw1 = rte_cpu_to_le_16(cmd_dtype);
+ *qw1 = rte_cpu_to_le_16(IDPF_TX_DESC_DTYPE_FLEX_TSO_CTX |
+ IDPF_TX_FLEX_CTX_DESC_CMD_TSO);
+ } else if (ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST)
+ *qw1 = FIELD_PREP(IDPF_TXD_QW1_CMD_M, IDPF_TX_CTX_DESC_TSYN) |
+ FIELD_PREP(IDPF_TXD_QW1_DTYPE_M, IDPF_TX_DESC_DTYPE_CTX) |
+ ((uint64_t)FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_L_M, txq->latch_idx) << 16) |
+ ((uint64_t)FIELD_PREP(IDPF_TX_DESC_CTX_TSYN_H_M, txq->latch_idx >> 2)
+ << 32);
return 1;
}
@@ -936,7 +899,7 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
tx_offload.tso_segsz = tx_pkt->tso_segsz;
/* Calculate the number of context descriptors needed. */
uint64_t cd_qw0 = 0, cd_qw1 = 0;
- nb_ctx = idpf_set_tso_ctx(ol_flags, tx_pkt, &tx_offload, txq,
+ nb_ctx = idpf_get_context_desc(ol_flags, tx_pkt, &tx_offload, txq,
&cd_qw0, &cd_qw1);
/* Calculate the number of TX descriptors needed for
@@ -1178,17 +1141,22 @@ idpf_dp_singleq_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
rxm->ol_flags |= pkt_flags;
+ if ((rxm->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
+ rxq->hw_register_set, true,
+ rxq->time_high);
rxq->hw_register_set = 0;
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
rxm->ol_flags |= idpf_timestamp_dynflag;
+ rxm->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
rx_pkts[nb_rx++] = rxm;
@@ -1229,6 +1197,9 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
if (unlikely(!rxq) || unlikely(!rxq->q_started))
return nb_rx;
+ if ((rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0)
+ rxq->hw_register_set = 1;
+
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
rx_status0 = rte_le_to_cpu_16(rxdp->flex_nic_wb.status_error0);
@@ -1309,17 +1280,22 @@ idpf_dp_singleq_recv_scatter_pkts(void *rx_queue, struct rte_mbuf **rx_pkts,
ptype_tbl[(uint8_t)(rte_cpu_to_le_16(rxd.flex_nic_wb.ptype_flex_flags0) &
VIRTCHNL2_RX_FLEX_DESC_PTYPE_M)];
+ if ((first_seg->packet_type & RTE_PTYPE_L2_MASK) == RTE_PTYPE_L2_ETHER_TIMESYNC)
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_PTP;
+
if (idpf_timestamp_dynflag > 0 &&
(rxq->offloads & IDPF_RX_OFFLOAD_TIMESTAMP) != 0) {
/* timestamp */
+ rxq->time_high = rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high);
ts_ns = idpf_tstamp_convert_32b_64b(ad,
- rxq->hw_register_set,
- rte_le_to_cpu_32(rxd.flex_nic_wb.flex_ts.ts_high));
+ rxq->hw_register_set, true,
+ rxq->time_high);
rxq->hw_register_set = 0;
*RTE_MBUF_DYNFIELD(rxm,
idpf_timestamp_dynfield_offset,
rte_mbuf_timestamp_t *) = ts_ns;
first_seg->ol_flags |= idpf_timestamp_dynflag;
+ first_seg->ol_flags |= RTE_MBUF_F_RX_IEEE1588_TMST;
}
first_seg->ol_flags |= pkt_flags;
@@ -1344,7 +1320,7 @@ idpf_dp_singleq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
uint16_t nb_pkts)
{
return ci_xmit_pkts(tx_queue, tx_pkts, nb_pkts, CI_VLAN_IN_L2TAG1,
- idpf_set_tso_ctx, NULL, NULL);
+ idpf_get_context_desc, NULL, NULL);
}
RTE_EXPORT_INTERNAL_SYMBOL(idpf_dp_singleq_xmit_pkts_simple)
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.h b/drivers/net/intel/idpf/idpf_common_rxtx.h
index 914cab0f25..a71af7103f 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.h
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.h
@@ -13,6 +13,12 @@
#include "../common/tx.h"
#include "../common/rx.h"
+#define FIELD_PREP(_mask, _val) \
+ (__extension__ ({ \
+ typeof(_mask) _x = (_mask); \
+ ((typeof(_x))(_val) << rte_bsf32(_x)) & (_x); \
+ }))
+
#define IDPF_RX_MAX_BURST 32
#define IDPF_RX_OFFLOAD_IPV4_CKSUM RTE_BIT64(1)
@@ -162,6 +168,7 @@ struct idpf_rx_queue {
uint64_t offloads;
uint32_t hw_register_set;
+ uint32_t time_high; /* high 32 bits of hardware timestamp register */
};
union idpf_tx_desc {
diff --git a/drivers/net/intel/idpf/idpf_ethdev.c b/drivers/net/intel/idpf/idpf_ethdev.c
index 5d786fbba6..5e57a45775 100644
--- a/drivers/net/intel/idpf/idpf_ethdev.c
+++ b/drivers/net/intel/idpf/idpf_ethdev.c
@@ -14,6 +14,7 @@
#include "idpf_ethdev.h"
#include "idpf_rxtx.h"
#include "../common/tx.h"
+#include "idpf_ptp.h"
#define IDPF_TX_SINGLE_Q "tx_single"
#define IDPF_RX_SINGLE_Q "rx_single"
@@ -841,6 +842,267 @@ idpf_dev_close(struct rte_eth_dev *dev)
return 0;
}
+static int
+idpf_timesync_enable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct timespec sys_ts;
+ uint64_t ns;
+ int ret, q_id;
+ if (dev->data->dev_started && !(dev->data->dev_conf.rxmode.offloads &
+ RTE_ETH_RX_OFFLOAD_TIMESTAMP)) {
+ PMD_DRV_LOG(ERR, "Rx timestamp offload not configured");
+ return -1;
+ }
+
+ adapter->ptp = rte_zmalloc(NULL, sizeof(struct idpf_ptp), 0);
+ if (adapter->ptp == NULL) {
+ PMD_DRV_LOG(ERR, "Failed to allocate memory for PTP");
+ return -ENOMEM;
+ }
+
+ ret = idpf_ptp_get_caps(adapter);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get PTP capabilities, err=%d", ret);
+ goto fail_ptp;
+ }
+
+ /* Write the default increment time value if the clock adjustments are enabled. */
+ if (adapter->ptp->adj_dev_clk_time_access != IDPF_PTP_NONE) {
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, adapter->ptp->base_incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set incval failed, err=%d", ret);
+ goto fail_ptp;
+ }
+ }
+
+ /* Do not initialize the PTP if the device clock time cannot be read. */
+ if (adapter->ptp->get_dev_clk_time_access == IDPF_PTP_NONE) {
+ PMD_DRV_LOG(ERR, "Getting device clock time is not supported");
+ ret = -EIO;
+ goto fail_ptp;
+ }
+
+ /* Set the device clock time to system time. */
+ if (adapter->ptp->set_dev_clk_time_access != IDPF_PTP_NONE) {
+ clock_gettime(CLOCK_REALTIME, &sys_ts);
+ ns = rte_timespec_to_ns(&sys_ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err=%d", ret);
+ goto fail_ptp;
+ }
+ }
+
+ ret = idpf_ptp_get_vport_tstamps_caps(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get vport timestamp capabilities, err=%d", ret);
+ goto fail_ptp;
+ }
+
+ for (q_id = 0; q_id < dev->data->nb_tx_queues; q_id++) {
+ struct ci_tx_queue *txq = dev->data->tx_queues[q_id];
+ txq->latch_idx = vport->tx_tstamp_caps->tx_tstamp[q_id].idx;
+ }
+
+ 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;
+
+ return 0;
+
+fail_ptp:
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ return ret;
+}
+
+static int
+idpf_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp,
+ uint32_t flags)
+{
+ struct idpf_rx_queue *rxq;
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ts_ns;
+
+ rxq = dev->data->rx_queues[flags];
+
+ ts_ns = idpf_tstamp_convert_32b_64b(adapter, 1, true, rxq->time_high);
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ return 0;
+}
+
+static int
+idpf_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
+ struct timespec *timestamp)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ uint16_t latch_idx;
+ uint64_t ts_ns, tstamp;
+ int ret;
+
+ ret = idpf_ptp_get_tx_tstamp(vport);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "Failed to get TX timestamp");
+ return ret;
+ }
+
+ latch_idx = vport->tx_tstamp_caps->latched_idx;
+ tstamp = vport->tx_tstamp_caps->tx_tstamp[latch_idx].tstamp;
+ ts_ns = idpf_tstamp_convert_32b_64b(vport->adapter, 0, false, tstamp);
+
+ /* Convert to timespec */
+ *timestamp = rte_ns_to_timespec(ts_ns);
+
+ vport->tx_tstamp_caps->latched_idx = -1;
+
+ return 0;
+}
+
+static int
+idpf_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ uint64_t time, ns;
+ int ret;
+
+ if (ptp->adj_dev_clk_time_access != IDPF_PTP_MAILBOX) {
+ PMD_DRV_LOG(ERR, "Adjusting device clock time is not supported");
+ return -ENOTSUP;
+ }
+
+ if (delta > INT32_MAX || delta < INT32_MIN) {
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP read clock time failed, err %d", ret);
+ return ret;
+ }
+
+ ns = time + delta;
+
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP set clock time failed, err %d", ret);
+
+ return ret;
+ }
+
+ ret = idpf_ptp_adj_dev_clk_time(adapter, delta);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP adjusting clock failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ struct idpf_ptp *ptp = adapter->ptp;
+ int64_t incval, diff = 0;
+ bool negative = false;
+ uint64_t div, rem;
+ uint64_t divisor = 1000000ULL << 16;
+ int shift;
+ int ret;
+
+ incval = ptp->base_incval;
+
+ if (ppm < 0) {
+ negative = true;
+ ppm = -ppm;
+ }
+
+ /* can incval * ppm overflow ? */
+ if (rte_log2_u64(incval) + rte_log2_u64(ppm) > 62) {
+ rem = ppm % divisor;
+ div = ppm / divisor;
+ diff = div * incval;
+ ppm = rem;
+
+ shift = rte_log2_u64(incval) + rte_log2_u64(ppm) - 62;
+ if (shift > 0) {
+ /* drop precision */
+ ppm >>= shift;
+ divisor >>= shift;
+ }
+ }
+
+ if (divisor)
+ diff = diff + incval * ppm / divisor;
+
+ if (negative)
+ incval -= diff;
+ else
+ incval += diff;
+
+ ret = idpf_ptp_adj_dev_clk_fine(adapter, incval);
+ if (ret) {
+ PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int
+idpf_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t ns;
+ int ret;
+
+ ns = rte_timespec_to_ns(ts);
+ ret = idpf_ptp_set_dev_clk_time(adapter, ns);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP write time failed, err %d", ret);
+
+ return ret;
+}
+
+static int
+idpf_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+ uint64_t time;
+ int ret;
+
+ ret = idpf_ptp_read_src_clk_reg(adapter, &time);
+ if (ret)
+ PMD_DRV_LOG(ERR, "PTP read time failed, err %d", ret);
+ else
+ *ts = rte_ns_to_timespec(time);
+
+ return ret;
+}
+
+static int
+idpf_timesync_disable(struct rte_eth_dev *dev)
+{
+ struct idpf_vport *vport = dev->data->dev_private;
+ struct idpf_adapter *adapter = vport->adapter;
+
+ if (vport->tx_tstamp_caps) {
+ rte_free(vport->tx_tstamp_caps);
+ vport->tx_tstamp_caps = NULL;
+ }
+
+ if (adapter->ptp) {
+ rte_free(adapter->ptp);
+ adapter->ptp = NULL;
+ }
+
+ return 0;
+}
+
+
static const struct eth_dev_ops idpf_eth_dev_ops = {
.dev_configure = idpf_dev_configure,
.dev_close = idpf_dev_close,
@@ -867,6 +1129,14 @@ static const struct eth_dev_ops idpf_eth_dev_ops = {
.xstats_get = idpf_dev_xstats_get,
.xstats_get_names = idpf_dev_xstats_get_names,
.xstats_reset = idpf_dev_xstats_reset,
+ .timesync_enable = idpf_timesync_enable,
+ .timesync_read_rx_timestamp = idpf_timesync_read_rx_timestamp,
+ .timesync_read_tx_timestamp = idpf_timesync_read_tx_timestamp,
+ .timesync_adjust_time = idpf_timesync_adjust_time,
+ .timesync_adjust_freq = idpf_timesync_adjust_freq,
+ .timesync_read_time = idpf_timesync_read_time,
+ .timesync_write_time = idpf_timesync_write_time,
+ .timesync_disable = idpf_timesync_disable,
};
static int
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH v8 4/4] doc/guides: add PTP IDPF documentation
2026-03-01 9:10 ` [PATCH v8 0/4] Enable PTP feature for MEV Soumyadeep Hore
` (2 preceding siblings ...)
2026-03-01 9:10 ` [PATCH v8 3/4] net/idpf: add timesync support Soumyadeep Hore
@ 2026-03-01 9:10 ` Soumyadeep Hore
2026-03-02 11:19 ` [PATCH v8 0/4] Enable PTP feature for MEV Bruce Richardson
4 siblings, 0 replies; 57+ messages in thread
From: Soumyadeep Hore @ 2026-03-01 9:10 UTC (permalink / raw)
To: bruce.richardson, manoj.kumar.subbarao, aman.deep.singh, dev; +Cc: Rajesh Kumar
Updates the IDPF Documentation with PTP feature.
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
Acked-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
doc/guides/nics/features/idpf.ini | 1 +
doc/guides/nics/idpf.rst | 15 +++++++++++++++
doc/guides/rel_notes/release_26_03.rst | 4 ++++
3 files changed, 20 insertions(+)
diff --git a/doc/guides/nics/features/idpf.ini b/doc/guides/nics/features/idpf.ini
index 63b799289a..f546648924 100644
--- a/doc/guides/nics/features/idpf.ini
+++ b/doc/guides/nics/features/idpf.ini
@@ -19,6 +19,7 @@ RSS reta update = Y
L3 checksum offload = P
L4 checksum offload = P
Timestamp offload = P
+Timesync = Y
Packet type parsing = Y
Basic stats = Y
Linux = Y
diff --git a/doc/guides/nics/idpf.rst b/doc/guides/nics/idpf.rst
index b99cc18188..998c1a5dfa 100644
--- a/doc/guides/nics/idpf.rst
+++ b/doc/guides/nics/idpf.rst
@@ -109,3 +109,18 @@ The paths are chosen based on 2 conditions:
A value "P" means the offload feature is not supported by vector path.
If any not supported features are used, idpf vector PMD is disabled
and the scalar paths are chosen.
+
+ Time Synchronization
+ ~~~~~~~~~~~~~~~~~~~~
+
+ The system operator can run a PTP (Precision Time Protocol) client application
+ to synchronize the time on the network card in ACC
+ (and optionally the time on the system) to the PTP primary.
+
+ IDPF PMD supports PTP client applications that use the DPDK IEEE 1588 API
+ to communicate with the PTP primary clock.
+ Note that PTP client application needs to run on PF in ACC.
+
+ .. code-block:: console
+
+ examples/dpdk-ptpclient -l 0-3 -n 3 -a 0000:ec:00.1 -- -T 1 -p 0x1
diff --git a/doc/guides/rel_notes/release_26_03.rst b/doc/guides/rel_notes/release_26_03.rst
index b4499ec066..ec55a4888e 100644
--- a/doc/guides/rel_notes/release_26_03.rst
+++ b/doc/guides/rel_notes/release_26_03.rst
@@ -73,6 +73,10 @@ New Features
* Added application-initiated device reset.
+* **Updated Intel idpf ethernet driver.**
+
+ * Added support for time sync features.
+
* **Updated Intel iavf driver.**
* Added support for pre and post VF reset callbacks.
--
2.47.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* Re: [PATCH v8 0/4] Enable PTP feature for MEV
2026-03-01 9:10 ` [PATCH v8 0/4] Enable PTP feature for MEV Soumyadeep Hore
` (3 preceding siblings ...)
2026-03-01 9:10 ` [PATCH v8 4/4] doc/guides: add PTP IDPF documentation Soumyadeep Hore
@ 2026-03-02 11:19 ` Bruce Richardson
4 siblings, 0 replies; 57+ messages in thread
From: Bruce Richardson @ 2026-03-02 11:19 UTC (permalink / raw)
To: Soumyadeep Hore; +Cc: manoj.kumar.subbarao, aman.deep.singh, dev
On Sun, Mar 01, 2026 at 04:10:36AM -0500, Soumyadeep Hore wrote:
> Enabling basic PTP feature in IDPF PMD using virtchnl messages.
>
> ---
> v8:
> - Refactored code based on Bruce's commit 025b898
> ---
> v7:
> - Addressed Rajesh's comments
> ---
> v6:
> - Addressed Bruce's comments
> - Addressed AI comments
> ---
> v5:
> - Addressed AI comments
> ---
> v4:
> - Addressed Bruce's comments
> ---
> v3:
> - Fixed checkpatch warnings
> - Addressed Bruce's comments
> ---
> v2:
> - Fixed essential checkpatch warnings
> ---
>
>
> Milena Olech (1):
> net/idpf/base: add a new API for PTP support
>
> Soumyadeep Hore (3):
> net/idpf: add PTP virtchnl2 support
> net/idpf: add timesync support
> doc/guides: add PTP IDPF documentation
>
Fixed documentation build issue, and pushed to next-net-intel tree. Also
squashed doc patch (patch 4) into feature code patch (patch 3).
Thanks,
/Bruce
^ permalink raw reply [flat|nested] 57+ messages in thread
end of thread, other threads:[~2026-03-02 11:19 UTC | newest]
Thread overview: 57+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-24 12:08 [PATCH v1 0/4] Enable PTP feature for MEV Soumyadeep Hore
2025-10-24 12:08 ` [PATCH v1 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
2025-10-24 12:08 ` [PATCH v1 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
2025-10-24 12:08 ` [PATCH v1 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
2025-10-24 12:08 ` [PATCH v1 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
2025-10-24 16:28 ` Stephen Hemminger
2025-10-28 6:07 ` [PATCH v2 0/4] Enable PTP feature for MEV Soumyadeep Hore
2025-10-28 6:07 ` [PATCH v2 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
2025-10-28 6:07 ` [PATCH v2 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
2025-10-28 6:07 ` [PATCH v2 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
2025-10-28 6:07 ` [PATCH v2 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
2025-10-30 9:41 ` Bruce Richardson
2026-01-20 14:34 ` Stephen Hemminger
2025-11-03 16:06 ` [PATCH v3 0/4] Enable PTP feature for MEV Soumyadeep Hore
2025-11-03 16:06 ` [PATCH v3 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
2025-11-03 16:06 ` [PATCH v3 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
2025-11-03 16:06 ` [PATCH v3 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
2025-12-16 12:43 ` Bruce Richardson
2025-11-03 16:06 ` [PATCH v3 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 3/4] net/intel: add support for Precision Time Protocol Soumyadeep Hore
2026-01-23 0:04 ` [PATCH v5 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-01-22 23:40 ` Stephen Hemminger
2026-01-23 0:04 ` [PATCH v5 1/4] net/idpf: add a new API for PTP support Soumyadeep Hore
2026-02-03 11:56 ` Bruce Richardson
2026-01-23 0:04 ` [PATCH v5 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
2026-02-03 12:52 ` Bruce Richardson
2026-02-14 7:10 ` Hore, Soumyadeep
2026-02-14 19:49 ` [PATCH v6 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-02-14 19:49 ` [PATCH v6 1/4] net/idpf/base: add a new API for PTP support Soumyadeep Hore
2026-02-26 9:57 ` Kumar, Rajesh3
2026-02-14 19:49 ` [PATCH v6 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
2026-02-26 10:15 ` Kumar, Rajesh3
2026-02-26 11:12 ` Hore, Soumyadeep
2026-02-14 19:49 ` [PATCH v6 3/4] net/idpf: add timesync support Soumyadeep Hore
2026-02-26 10:32 ` Kumar, Rajesh3
2026-02-14 19:49 ` [PATCH v6 4/4] doc/guides: add PTP IDPF documentation Soumyadeep Hore
2026-02-26 10:39 ` Kumar, Rajesh3
2026-02-27 0:33 ` [PATCH v7 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-02-27 0:33 ` [PATCH v7 1/4] net/idpf/base: add a new API for PTP support Soumyadeep Hore
2026-02-27 0:33 ` [PATCH v7 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
2026-02-26 12:00 ` Kumar, Rajesh3
2026-02-27 0:33 ` [PATCH v7 3/4] net/idpf: add timesync support Soumyadeep Hore
2026-02-27 0:33 ` [PATCH v7 4/4] doc/guides: add PTP IDPF documentation Soumyadeep Hore
2026-03-01 9:10 ` [PATCH v8 0/4] Enable PTP feature for MEV Soumyadeep Hore
2026-03-01 9:10 ` [PATCH v8 1/4] net/idpf/base: add a new API for PTP support Soumyadeep Hore
2026-03-01 9:10 ` [PATCH v8 2/4] net/idpf: add PTP virtchnl2 support Soumyadeep Hore
2026-03-01 9:10 ` [PATCH v8 3/4] net/idpf: add timesync support Soumyadeep Hore
2026-03-01 9:10 ` [PATCH v8 4/4] doc/guides: add PTP IDPF documentation Soumyadeep Hore
2026-03-02 11:19 ` [PATCH v8 0/4] Enable PTP feature for MEV Bruce Richardson
2026-01-23 0:04 ` [PATCH v5 3/4] net/intel: add support for PTP Soumyadeep Hore
2026-02-03 15:37 ` Bruce Richardson
2026-01-23 0:04 ` [PATCH v5 4/4] doc: add PTP IDPF documentation Soumyadeep Hore
2026-01-20 19:11 ` [PATCH v4 " Soumyadeep Hore
2026-01-20 14:56 ` [PATCH v2 0/4] Enable PTP feature for MEV Stephen Hemminger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox