DPDK patches and discussions
 help / color / mirror / Atom feed
* [PATCH 0/3] add frequency adjustment support for PTP
@ 2024-09-05  1:31 Mingjin Ye
  2024-09-05  1:31 ` [PATCH 1/3] ethdev: add frequency adjustment API Mingjin Ye
                   ` (3 more replies)
  0 siblings, 4 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-09-05  1:31 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye

[1/3] ethdev: add frequency adjustment API
[2/3] net/ice: add frequency adjustment support for PTP
[3/3] examples/ptpclient: add frequency adjustment support

Mingjin Ye (3):
  ethdev: add frequency adjustment API
  net/ice: add frequency adjustment support for PTP
  examples/ptpclient: add frequency adjustment support

 doc/guides/nics/features.rst     |   3 +-
 doc/guides/nics/ice.rst          |  15 ++
 drivers/net/ice/ice_ethdev.c     | 177 +++++++++++++-----
 drivers/net/ice/ice_ethdev.h     |   2 +
 drivers/net/ice/ice_rxtx.c       |   4 +-
 examples/ptpclient/ptpclient.c   | 300 +++++++++++++++++++++++++++----
 lib/ethdev/ethdev_driver.h       |   5 +
 lib/ethdev/ethdev_trace.h        |   9 +
 lib/ethdev/ethdev_trace_points.c |   3 +
 lib/ethdev/rte_ethdev.c          |  18 ++
 lib/ethdev/rte_ethdev.h          |  19 ++
 11 files changed, 474 insertions(+), 81 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH 1/3] ethdev: add frequency adjustment API
  2024-09-05  1:31 [PATCH 0/3] add frequency adjustment support for PTP Mingjin Ye
@ 2024-09-05  1:31 ` Mingjin Ye
  2024-09-05  1:31 ` [PATCH 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-09-05  1:31 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko

This patch adds freq adjustment API for PTP high accuracy.

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
 doc/guides/nics/features.rst     |  3 ++-
 lib/ethdev/ethdev_driver.h       |  5 +++++
 lib/ethdev/ethdev_trace.h        |  9 +++++++++
 lib/ethdev/ethdev_trace_points.c |  3 +++
 lib/ethdev/rte_ethdev.c          | 18 ++++++++++++++++++
 lib/ethdev/rte_ethdev.h          | 19 +++++++++++++++++++
 6 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/doc/guides/nics/features.rst b/doc/guides/nics/features.rst
index cd0115ffb3..a2a19623b7 100644
--- a/doc/guides/nics/features.rst
+++ b/doc/guides/nics/features.rst
@@ -677,7 +677,8 @@ Supports IEEE1588/802.1AS timestamping.
 
 * **[implements] eth_dev_ops**: ``timesync_enable``, ``timesync_disable``
   ``timesync_read_rx_timestamp``, ``timesync_read_tx_timestamp``,
-  ``timesync_adjust_time``, ``timesync_read_time``, ``timesync_write_time``.
+  ``timesync_adjust_time``, ``timesync_adjust_freq``,
+  ``timesync_read_time``, ``timesync_write_time``.
 * **[related]    API**: ``rte_eth_timesync_enable()``, ``rte_eth_timesync_disable()``,
   ``rte_eth_timesync_read_rx_timestamp()``,
   ``rte_eth_timesync_read_tx_timestamp``, ``rte_eth_timesync_adjust_time()``,
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 883e59a927..68cadc14a5 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -664,6 +664,9 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 /** @internal Function used to adjust the device clock. */
 typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
 
+/** @internal Function used to adjust the clock frequency. */
+typedef int (*eth_timesync_adjust_freq)(struct rte_eth_dev *dev, int64_t);
+
 /** @internal Function used to get time from the device clock. */
 typedef int (*eth_timesync_read_time)(struct rte_eth_dev *dev,
 				      struct timespec *timestamp);
@@ -1378,6 +1381,8 @@ struct eth_dev_ops {
 	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
 	/** Adjust the device clock */
 	eth_timesync_adjust_time   timesync_adjust_time;
+	/** Adjust the clock frequency */
+	eth_timesync_adjust_freq   timesync_adjust_freq;
 	/** Get the device clock time */
 	eth_timesync_read_time     timesync_read_time;
 	/** Set the device clock time */
diff --git a/lib/ethdev/ethdev_trace.h b/lib/ethdev/ethdev_trace.h
index 3bec87bfdb..e273d5853e 100644
--- a/lib/ethdev/ethdev_trace.h
+++ b/lib/ethdev/ethdev_trace.h
@@ -2183,6 +2183,15 @@ RTE_TRACE_POINT_FP(
 	rte_trace_point_emit_int(ret);
 )
 
+/* Called in loop in examples/ptpclient */
+RTE_TRACE_POINT_FP(
+	rte_eth_trace_timesync_adjust_freq,
+	RTE_TRACE_POINT_ARGS(uint16_t port_id, int64_t ppm, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_i64(ppm);
+	rte_trace_point_emit_int(ret);
+)
+
 /* Called in loop in app/test-flow-perf */
 RTE_TRACE_POINT_FP(
 	rte_flow_trace_create,
diff --git a/lib/ethdev/ethdev_trace_points.c b/lib/ethdev/ethdev_trace_points.c
index 99e04f5893..a99fec0c1e 100644
--- a/lib/ethdev/ethdev_trace_points.c
+++ b/lib/ethdev/ethdev_trace_points.c
@@ -409,6 +409,9 @@ RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_tx_timestamp,
 RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_time,
 	lib.ethdev.timesync_adjust_time)
 
+RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_freq,
+	lib.ethdev.timesync_adjust_freq)
+
 RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_time,
 	lib.ethdev.timesync_read_time)
 
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index f1c658f49e..660eab2f1e 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -6310,6 +6310,24 @@ rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta)
 	return ret;
 }
 
+int
+rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (*dev->dev_ops->timesync_adjust_freq == NULL)
+		return -ENOTSUP;
+	ret = eth_err(port_id, (*dev->dev_ops->timesync_adjust_freq)(dev, ppm));
+
+	rte_eth_trace_timesync_adjust_freq(port_id, ppm, ret);
+
+	return ret;
+}
+
 int
 rte_eth_timesync_read_time(uint16_t port_id, struct timespec *timestamp)
 {
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index 548fada1c7..42fa2ca39c 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -5297,6 +5297,25 @@ int rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
  */
 int rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta);
 
+/**
+ * Adjust the clock increment rate on an Ethernet device.
+ *
+ * This is usually used in conjunction with other Ethdev timesync functions to
+ * synchronize the device time using the IEEE1588/802.1AS protocol.
+ *
+ * @param port_id
+ *  The port identifier of the Ethernet device.
+ * @param ppm
+ *  Parts per million with 16-bit fractional field
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+int rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm);
+
 /**
  * Read the time from the timesync clock on an Ethernet device.
  *
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH 2/3] net/ice: add frequency adjustment support for PTP
  2024-09-05  1:31 [PATCH 0/3] add frequency adjustment support for PTP Mingjin Ye
  2024-09-05  1:31 ` [PATCH 1/3] ethdev: add frequency adjustment API Mingjin Ye
@ 2024-09-05  1:31 ` Mingjin Ye
  2024-09-05  1:31 ` [PATCH 3/3] examples/ptpclient: add frequency adjustment support Mingjin Ye
  2024-09-10  9:13 ` [PATCH v2 0/3] add frequency adjustment support for PTP Mingjin Ye
  3 siblings, 0 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-09-05  1:31 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su

Add ice support for new ethdev API to adjust frequency for IEEE1588
PTP. Also, this patch reworks code for converting software update
to hardware update.

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
 doc/guides/nics/ice.rst      |  15 +++
 drivers/net/ice/ice_ethdev.c | 177 ++++++++++++++++++++++++++---------
 drivers/net/ice/ice_ethdev.h |   2 +
 drivers/net/ice/ice_rxtx.c   |   4 +-
 4 files changed, 153 insertions(+), 45 deletions(-)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index ae975d19ad..dec4cc2434 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -328,6 +328,21 @@ Forward Error Correction (FEC)
 
 Supports get/set FEC mode and get FEC capability.
 
+Time Synchronisation
+~~~~~~~~~~~~~~~~~~~~
+
+The system operator can run a PTP (Precision Time Protocol) client application
+to synchronise the time on the network card (and optionally the time on the
+system) to the PTP master.
+
+ICE PMD supports PTP client applications that use the DPDK IEEE1588 API to
+communicate with the PTP master clock. Note that the PTP client application
+needs to run on the PF and vector mode needs to be disabled.
+
+.. code-block:: console
+
+    examples/dpdk-ptpclient -c f -n 3 -a 0000:ec:00.1 -- -T 1 -p 0x1 --controller=pi
+
 Generic Flow Support
 ~~~~~~~~~~~~~~~~~~~~
 
diff --git a/drivers/net/ice/ice_ethdev.c b/drivers/net/ice/ice_ethdev.c
index 304f959b7e..33293a917f 100644
--- a/drivers/net/ice/ice_ethdev.c
+++ b/drivers/net/ice/ice_ethdev.c
@@ -10,6 +10,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <math.h>
 
 #include <rte_tailq.h>
 #include <rte_os_shim.h>
@@ -176,6 +177,7 @@ static int ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 static int ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					  struct timespec *timestamp);
 static int ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
+static int ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm);
 static int ice_timesync_read_time(struct rte_eth_dev *dev,
 				  struct timespec *timestamp);
 static int ice_timesync_write_time(struct rte_eth_dev *dev,
@@ -307,6 +309,7 @@ static const struct eth_dev_ops ice_eth_dev_ops = {
 	.timesync_read_rx_timestamp   = ice_timesync_read_rx_timestamp,
 	.timesync_read_tx_timestamp   = ice_timesync_read_tx_timestamp,
 	.timesync_adjust_time         = ice_timesync_adjust_time,
+	.timesync_adjust_freq         = ice_timesync_adjust_freq,
 	.timesync_read_time           = ice_timesync_read_time,
 	.timesync_write_time          = ice_timesync_write_time,
 	.timesync_disable             = ice_timesync_disable,
@@ -2332,6 +2335,34 @@ ice_get_supported_rxdid(struct ice_hw *hw)
 	return supported_rxdid;
 }
 
+static void ice_ptp_init_info(struct rte_eth_dev *dev)
+{
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ice_adapter *ad =
+		ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+
+	switch (hw->phy_model) {
+	case ICE_PHY_ETH56G:
+		ad->ptp_tx_block = hw->pf_id;
+		ad->ptp_tx_index = 0;
+		break;
+	case ICE_PHY_E810:
+	/* fallthrough */
+	case ICE_PHY_E830:
+		ad->ptp_tx_block = hw->port_info->lport;
+		ad->ptp_tx_index = 0;
+		break;
+	case ICE_PHY_E822:
+		ad->ptp_tx_block = hw->pf_id / ICE_PORTS_PER_QUAD;
+		ad->ptp_tx_index = (hw->pf_id % ICE_PORTS_PER_QUAD) *
+				ICE_PORTS_PER_PHY_E822 * ICE_QUADS_PER_PHY_E822;
+		break;
+	default:
+		PMD_DRV_LOG(WARNING, "Unsupported PHY model");
+		break;
+	}
+}
+
 static int
 ice_dev_init(struct rte_eth_dev *dev)
 {
@@ -2499,6 +2530,9 @@ ice_dev_init(struct rte_eth_dev *dev)
 	/* Initialize PHY model */
 	ice_ptp_init_phy_model(hw);
 
+	/* Initialize PTP info */
+	ice_ptp_init_info(dev);
+
 	if (hw->phy_model == ICE_PHY_E822) {
 		ret = ice_start_phy_timer_e822(hw, hw->pf_id);
 		if (ret)
@@ -6466,23 +6500,6 @@ ice_timesync_enable(struct rte_eth_dev *dev)
 		}
 	}
 
-	/* Initialize cycle counters for system time/RX/TX timestamp */
-	memset(&ad->systime_tc, 0, sizeof(struct rte_timecounter));
-	memset(&ad->rx_tstamp_tc, 0, sizeof(struct rte_timecounter));
-	memset(&ad->tx_tstamp_tc, 0, sizeof(struct rte_timecounter));
-
-	ad->systime_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->systime_tc.cc_shift = 0;
-	ad->systime_tc.nsec_mask = 0;
-
-	ad->rx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->rx_tstamp_tc.cc_shift = 0;
-	ad->rx_tstamp_tc.nsec_mask = 0;
-
-	ad->tx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->tx_tstamp_tc.cc_shift = 0;
-	ad->tx_tstamp_tc.nsec_mask = 0;
-
 	ad->ptp_ena = 1;
 
 	return 0;
@@ -6497,14 +6514,13 @@ ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	struct ice_rx_queue *rxq;
 	uint32_t ts_high;
-	uint64_t ts_ns, ns;
+	uint64_t ts_ns;
 
 	rxq = dev->data->rx_queues[flags];
 
 	ts_high = rxq->time_high;
 	ts_ns = ice_tstamp_convert_32b_64b(hw, ad, 1, ts_high);
-	ns = rte_timecounter_update(&ad->rx_tstamp_tc, ts_ns);
-	*timestamp = rte_ns_to_timespec(ns);
+	*timestamp = rte_ns_to_timespec(ts_ns);
 
 	return 0;
 }
@@ -6516,22 +6532,18 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	struct ice_adapter *ad =
 			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-	uint8_t lport;
-	uint64_t ts_ns, ns, tstamp;
+	uint64_t ts_ns, tstamp;
 	const uint64_t mask = 0xFFFFFFFF;
 	int ret;
 
-	lport = hw->port_info->lport;
-
-	ret = ice_read_phy_tstamp(hw, lport, 0, &tstamp);
-	if (ret) {
+	ret = ice_read_phy_tstamp(hw, ad->ptp_tx_block, ad->ptp_tx_index, &tstamp);
+	if (ret || tstamp == 0) {
 		PMD_DRV_LOG(ERR, "Failed to read phy timestamp");
 		return -1;
 	}
 
 	ts_ns = ice_tstamp_convert_32b_64b(hw, ad, 1, (tstamp >> 8) & mask);
-	ns = rte_timecounter_update(&ad->tx_tstamp_tc, ts_ns);
-	*timestamp = rte_ns_to_timespec(ns);
+	*timestamp = rte_ns_to_timespec(ts_ns);
 
 	return 0;
 }
@@ -6539,28 +6551,108 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 static int
 ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
 {
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint8_t tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc;
+	uint32_t lo, lo2, hi;
+	uint64_t time, ns;
+	int ret;
+
+	if (delta > INT32_MAX || delta < INT32_MIN) {
+		lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+		hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
+		lo2 = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+
+		if (lo2 < lo) {
+			lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+			hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
+		}
+
+		time = ((uint64_t)hi << 32) | lo;
+		ns = time + delta;
+
+		wr32(hw, GLTSYN_SHTIME_L(tmr_idx), ICE_LO_DWORD(ns));
+		wr32(hw, GLTSYN_SHTIME_H(tmr_idx), ICE_HI_DWORD(ns));
+		wr32(hw, GLTSYN_SHTIME_0(tmr_idx), 0);
+
+		ret = ice_ptp_init_time(hw, ns, true);
+		if (ret) {
+			PMD_DRV_LOG(ERR, "PTP init time failed, err %d", ret);
+			return -1;
+		}
+		return 0;
+	}
+
+	ret = ice_ptp_adj_clock(hw, delta, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP adj clock failed, err %d", ret);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	int64_t incval, diff = 0;
+	bool negative = false;
+	uint64_t div, rem;
+	uint64_t divisor = 1000000ULL << 16;
+	int shift;
+	int ret;
+
+	incval = ice_get_base_incval(hw, ICE_SRC_TMR_MODE_NANOSECONDS);
+
+	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;
 
-	ad->systime_tc.nsec += delta;
-	ad->rx_tstamp_tc.nsec += delta;
-	ad->tx_tstamp_tc.nsec += delta;
+		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 = ice_ptp_write_incval_locked(hw, incval, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+		return -1;
+	}
 	return 0;
 }
 
 static int
 ice_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
 {
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint64_t ns;
+	int ret;
 
 	ns = rte_timespec_to_ns(ts);
-
-	ad->systime_tc.nsec = ns;
-	ad->rx_tstamp_tc.nsec = ns;
-	ad->tx_tstamp_tc.nsec = ns;
+	ret = ice_ptp_init_time(hw, ns, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP init time failed, err %d", ret);
+		return -1;
+	}
 
 	return 0;
 }
@@ -6569,11 +6661,9 @@ static int
 ice_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
 {
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	uint8_t tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc;
 	uint32_t hi, lo, lo2;
-	uint64_t time, ns;
+	uint64_t time;
 
 	lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
 	hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
@@ -6585,8 +6675,7 @@ ice_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
 	}
 
 	time = ((uint64_t)hi << 32) | lo;
-	ns = rte_timecounter_update(&ad->systime_tc, time);
-	*ts = rte_ns_to_timespec(ns);
+	*ts = rte_ns_to_timespec(time);
 
 	return 0;
 }
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index 3ea9f37dc8..db192e76fe 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -614,6 +614,8 @@ struct ice_adapter {
 	struct rte_timecounter systime_tc;
 	struct rte_timecounter rx_tstamp_tc;
 	struct rte_timecounter tx_tstamp_tc;
+	uint8_t ptp_tx_block;
+	uint8_t ptp_tx_index;
 	bool ptp_ena;
 	uint64_t time_hw;
 	struct ice_fdir_prof_info fdir_prof_info[ICE_MAX_PTGS];
diff --git a/drivers/net/ice/ice_rxtx.c b/drivers/net/ice/ice_rxtx.c
index f270498ed1..5f399e8aae 100644
--- a/drivers/net/ice/ice_rxtx.c
+++ b/drivers/net/ice/ice_rxtx.c
@@ -3066,7 +3066,9 @@ ice_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
 			else if (ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST)
 				cd_type_cmd_tso_mss |=
 					((uint64_t)ICE_TX_CTX_DESC_TSYN <<
-					ICE_TXD_CTX_QW1_CMD_S);
+					ICE_TXD_CTX_QW1_CMD_S) |
+					 (((uint64_t)txq->vsi->adapter->ptp_tx_index <<
+					 ICE_TXD_CTX_QW1_TSYN_S) & ICE_TXD_CTX_QW1_TSYN_M);
 
 			ctx_txd->tunneling_params =
 				rte_cpu_to_le_32(cd_tunneling_params);
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH 3/3] examples/ptpclient: add frequency adjustment support
  2024-09-05  1:31 [PATCH 0/3] add frequency adjustment support for PTP Mingjin Ye
  2024-09-05  1:31 ` [PATCH 1/3] ethdev: add frequency adjustment API Mingjin Ye
  2024-09-05  1:31 ` [PATCH 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
@ 2024-09-05  1:31 ` Mingjin Ye
  2024-09-10  9:13 ` [PATCH v2 0/3] add frequency adjustment support for PTP Mingjin Ye
  3 siblings, 0 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-09-05  1:31 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su, Wenjun Wu, Kirill Rybalchenko

This patch adds PI servo algorithm to support frequency
adjustment API for IEEE1588 PTP.

For example, the command for starting ptpclient with PI algorithm is:
./build/examples/dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1
--controller=pi

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
 examples/ptpclient/ptpclient.c | 300 +++++++++++++++++++++++++++++----
 1 file changed, 265 insertions(+), 35 deletions(-)

diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
index afb61bba51..b97b858310 100644
--- a/examples/ptpclient/ptpclient.c
+++ b/examples/ptpclient/ptpclient.c
@@ -46,6 +46,35 @@ static volatile bool force_quit;
 #define KERNEL_TIME_ADJUST_LIMIT  20000
 #define PTP_PROTOCOL             0x88F7
 
+#define KP 0.7
+#define KI 0.3
+#define FREQ_EST_MARGIN 0.001
+
+enum servo_state {
+	SERVO_UNLOCKED,
+	SERVO_JUMP,
+	SERVO_LOCKED,
+};
+
+struct pi_servo {
+	double offset[2];
+	double local[2];
+	double drift;
+	double last_freq;
+	int count;
+
+	double max_frequency;
+	double step_threshold;
+	double first_step_threshold;
+	int first_update;
+};
+
+enum controller_mode {
+	MODE_NONE,
+	MODE_PI,
+	MAX_ALL
+} mode = MODE_PI;
+
 struct rte_mempool *mbuf_pool;
 uint32_t ptp_enabled_port_mask;
 uint8_t ptp_enabled_port_nb;
@@ -135,6 +164,9 @@ struct ptpv2_data_slave_ordinary {
 	uint8_t ptpset;
 	uint8_t kernel_time_set;
 	uint16_t current_ptp_port;
+	int64_t master_offset;
+	int64_t path_delay;
+	struct pi_servo *servo;
 };
 
 static struct ptpv2_data_slave_ordinary ptp_data;
@@ -293,36 +325,44 @@ print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
 			ptp_data->tstamp3.tv_sec,
 			(ptp_data->tstamp3.tv_nsec));
 
-	printf("\nT4 - Master Clock.  %lds %ldns ",
+	printf("\nT4 - Master Clock.  %lds %ldns\n",
 			ptp_data->tstamp4.tv_sec,
 			(ptp_data->tstamp4.tv_nsec));
 
-	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+	if (mode == MODE_NONE) {
+		printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
 			ptp_data->delta);
 
-	clock_gettime(CLOCK_REALTIME, &sys_time);
-	rte_eth_timesync_read_time(ptp_data->current_ptp_port, &net_time);
+		clock_gettime(CLOCK_REALTIME, &sys_time);
+		rte_eth_timesync_read_time(ptp_data->current_ptp_port,
+					   &net_time);
 
-	time_t ts = net_time.tv_sec;
+		time_t ts = net_time.tv_sec;
 
-	printf("\n\nComparison between Linux kernel Time and PTP:");
+		printf("\n\nComparison between Linux kernel Time and PTP:");
 
-	printf("\nCurrent PTP Time: %.24s %.9ld ns",
+		printf("\nCurrent PTP Time: %.24s %.9ld ns",
 			ctime(&ts), net_time.tv_nsec);
 
-	nsec = (int64_t)timespec64_to_ns(&net_time) -
+		nsec = (int64_t)timespec64_to_ns(&net_time) -
 			(int64_t)timespec64_to_ns(&sys_time);
-	ptp_data->new_adj = ns_to_timeval(nsec);
+		ptp_data->new_adj = ns_to_timeval(nsec);
 
-	gettimeofday(&ptp_data->new_adj, NULL);
+		gettimeofday(&ptp_data->new_adj, NULL);
 
-	time_t tp = ptp_data->new_adj.tv_sec;
+		time_t tp = ptp_data->new_adj.tv_sec;
 
-	printf("\nCurrent SYS Time: %.24s %.6ld ns",
-				ctime(&tp), ptp_data->new_adj.tv_usec);
+		printf("\nCurrent SYS Time: %.24s %.6ld ns",
+			ctime(&tp), ptp_data->new_adj.tv_usec);
 
-	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
-				nsec);
+		printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
+			nsec);
+	}
+
+	if (mode == MODE_PI) {
+		printf("path delay: %"PRId64"ns\n", ptp_data->path_delay);
+		printf("master offset: %"PRId64"ns\n", ptp_data->master_offset);
+	}
 
 	printf("[Ctrl+C to quit]\n");
 
@@ -385,21 +425,11 @@ parse_sync(struct ptpv2_data_slave_ordinary *ptp_data, uint16_t rx_tstamp_idx)
 static void
 parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
 {
-	struct rte_ether_hdr *eth_hdr;
-	struct rte_ether_addr eth_addr;
 	struct ptp_header *ptp_hdr;
-	struct clock_id *client_clkid;
 	struct ptp_message *ptp_msg;
-	struct delay_req_msg *req_msg;
-	struct rte_mbuf *created_pkt;
 	struct tstamp *origin_tstamp;
-	struct rte_ether_addr eth_multicast = ether_multicast;
-	size_t pkt_size;
-	int wait_us;
 	struct rte_mbuf *m = ptp_data->m;
-	int ret;
 
-	eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
 	ptp_hdr = rte_pktmbuf_mtod_offset(m, struct ptp_header *,
 					  sizeof(struct rte_ether_hdr));
 	if (memcmp(&ptp_data->master_clock_id,
@@ -416,6 +446,150 @@ parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
 	ptp_data->tstamp1.tv_sec =
 		((uint64_t)ntohl(origin_tstamp->sec_lsb)) |
 		(((uint64_t)ntohs(origin_tstamp->sec_msb)) << 32);
+}
+
+static double
+pi_sample(struct pi_servo *s, int64_t offset, double local_ts,
+	  enum servo_state *state)
+{
+	double ki_term, ppb = s->last_freq;
+	double freq_est_interval, localdiff;
+
+	switch (s->count) {
+	case 0:
+		s->offset[0] = offset;
+		s->local[0] = local_ts;
+		*state = SERVO_UNLOCKED;
+		s->count = 1;
+		break;
+	case 1:
+		s->offset[1] = offset;
+		s->local[1] = local_ts;
+
+		/* Make sure the first sample is older than the second. */
+		if (s->local[0] >= s->local[1]) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		/* Wait long enough before estimating the frequency offset. */
+		localdiff = (s->local[1] - s->local[0]) / 1e9;
+		localdiff += localdiff * FREQ_EST_MARGIN;
+		freq_est_interval = 0.016 / KI;
+		if (freq_est_interval > 1000.0)
+			freq_est_interval = 1000.0;
+
+		if (localdiff < freq_est_interval) {
+			*state = SERVO_UNLOCKED;
+			break;
+		}
+
+		/* Adjust drift by the measured frequency offset. */
+		s->drift += (1e9 - s->drift) * (s->offset[1] - s->offset[0]) /
+						(s->local[1] - s->local[0]);
+
+		if (s->drift < -s->max_frequency)
+			s->drift = -s->max_frequency;
+		else if (s->drift > s->max_frequency)
+			s->drift = s->max_frequency;
+
+		if ((s->first_update &&
+		     s->first_step_threshold &&
+		     s->first_step_threshold < llabs(offset)) ||
+		    (s->step_threshold &&
+		     s->step_threshold < llabs(offset)))
+			*state = SERVO_JUMP;
+		else
+			*state = SERVO_LOCKED;
+
+		ppb = s->drift;
+		s->count = 2;
+		break;
+	case 2:
+		/*
+		 * reset the clock servo when offset is greater than the max
+		 * offset value. Note that the clock jump will be performed in
+		 * step 1, so it is not necessary to have clock jump
+		 * immediately. This allows re-calculating drift as in initial
+		 * clock startup.
+		 */
+		if (s->step_threshold &&
+		    s->step_threshold < llabs(offset)) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		ki_term = KI * offset;
+		ppb = KP * offset + s->drift + ki_term;
+		if (ppb < -s->max_frequency)
+			ppb = -s->max_frequency;
+		else if (ppb > s->max_frequency)
+			ppb = s->max_frequency;
+		else
+			s->drift += ki_term;
+
+		*state = SERVO_LOCKED;
+		break;
+	}
+
+	s->last_freq = ppb;
+	return ppb;
+}
+
+static void
+ptp_adjust_freq(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns;
+	double adj_freq;
+	enum servo_state state = SERVO_UNLOCKED;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	ptp_data->master_offset = t2_ns - t1_ns - ptp_data->path_delay;
+	if (!ptp_data->path_delay)
+		return;
+
+	adj_freq = pi_sample(ptp_data->servo, ptp_data->master_offset, t2_ns,
+		     &state);
+
+	switch (state) {
+	case SERVO_UNLOCKED:
+		break;
+	case SERVO_JUMP:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+						-(long)(adj_freq * 65.536));
+		rte_eth_timesync_adjust_time(ptp_data->portid,
+					     -ptp_data->master_offset);
+		break;
+	case SERVO_LOCKED:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+					     -(long)(adj_freq * 65.536));
+		break;
+	}
+}
+
+static void
+send_delay_request(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct rte_ether_hdr *eth_hdr;
+	struct rte_ether_addr eth_addr;
+	struct ptp_header *ptp_hdr;
+	struct clock_id *client_clkid;
+	struct delay_req_msg *req_msg;
+	struct rte_mbuf *created_pkt;
+	struct rte_ether_addr eth_multicast = ether_multicast;
+	size_t pkt_size;
+	int wait_us;
+	struct rte_mbuf *m = ptp_data->m;
+	int ret;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+			+ sizeof(struct rte_ether_hdr));
 
 	if (ptp_data->seqID_FOLLOWUP == ptp_data->seqID_SYNC) {
 		ret = rte_eth_macaddr_get(ptp_data->portid, &eth_addr);
@@ -488,11 +662,10 @@ parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
 		ptp_data->tstamp3.tv_sec = 0;
 
 		/* Wait at least 1 us to read TX timestamp. */
-		while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
-				&ptp_data->tstamp3) < 0) && (wait_us < 1000)) {
+		do {
 			rte_delay_us(1);
-			wait_us++;
-		}
+		} while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->tstamp3) < 0) && (wait_us++ < 10));
 	}
 }
 
@@ -529,6 +702,25 @@ update_kernel_time(void)
 
 }
 
+static void
+clock_path_delay(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns, t3_ns, t4_ns;
+	int64_t pd, diff;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	t3_ns = timespec64_to_ns(&ptp_data->tstamp3);
+	t4_ns = timespec64_to_ns(&ptp_data->tstamp4);
+
+	pd = (t2_ns - t3_ns) + (t4_ns - t1_ns);
+	diff = t3_ns - t2_ns;
+	if (diff <= INT32_MAX && diff >= INT32_MIN)
+		ptp_data->path_delay = pd / 2;
+	else
+		ptp_data->path_delay = 0;
+}
+
 /*
  * Parse the DELAY_RESP message.
  */
@@ -541,7 +733,7 @@ parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
 	uint16_t seq_id;
 
 	ptp_msg = rte_pktmbuf_mtod_offset(m, struct ptp_message *,
-					  sizeof(struct rte_ether_hdr));
+					sizeof(struct rte_ether_hdr));
 	seq_id = rte_be_to_cpu_16(ptp_msg->delay_resp.hdr.seq_id);
 	if (memcmp(&ptp_data->client_clock_id,
 		   &ptp_msg->delay_resp.req_port_id.clock_id,
@@ -553,11 +745,15 @@ parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
 				((uint64_t)ntohl(rx_tstamp->sec_lsb)) |
 				(((uint64_t)ntohs(rx_tstamp->sec_msb)) << 32);
 
-			/* Evaluate the delta for adjustment. */
-			ptp_data->delta = delta_eval(ptp_data);
+			if (mode == MODE_PI) {
+				clock_path_delay(ptp_data);
+			} else {
+				/* Evaluate the delta for adjustment. */
+				ptp_data->delta = delta_eval(ptp_data);
 
-			rte_eth_timesync_adjust_time(ptp_data->portid,
-						     ptp_data->delta);
+				rte_eth_timesync_adjust_time(ptp_data->portid,
+								ptp_data->delta);
+			}
 
 			ptp_data->current_ptp_port = ptp_data->portid;
 
@@ -597,6 +793,9 @@ parse_ptp_frames(uint16_t portid, struct rte_mbuf *m) {
 			break;
 		case FOLLOW_UP:
 			parse_fup(&ptp_data);
+			if (mode == MODE_PI)
+				ptp_adjust_freq(&ptp_data);
+			send_delay_request(&ptp_data);
 			break;
 		case DELAY_RESP:
 			parse_drsp(&ptp_data);
@@ -688,6 +887,18 @@ parse_ptp_kernel(const char *param)
 	return 1;
 }
 
+static void
+servo_init(struct pi_servo *servo)
+{
+	memset(servo, 0x00, sizeof(*servo));
+
+	servo->drift = 100000000;
+	servo->last_freq = 100000000;
+	servo->count = 0;
+	servo->max_frequency = 100000000;
+	servo->step_threshold = 0.1 * NSEC_PER_SEC;
+}
+
 /* Parse the commandline arguments. */
 static int
 ptp_parse_args(int argc, char **argv)
@@ -696,7 +907,10 @@ ptp_parse_args(int argc, char **argv)
 	char **argvopt;
 	int option_index;
 	char *prgname = argv[0];
-	static struct option lgopts[] = { {NULL, 0, 0, 0} };
+	static struct option lgopts[] = {
+		{"controller", 1, 0, 0},
+		{NULL, 0, 0, 0}
+	};
 
 	argvopt = argv;
 
@@ -724,6 +938,11 @@ ptp_parse_args(int argc, char **argv)
 
 			ptp_data.kernel_time_set = ret;
 			break;
+		case 0:
+			if (!strcmp(lgopts[option_index].name, "controller"))
+				if (!strcmp(optarg, "pi"))
+					mode = MODE_PI;
+			break;
 
 		default:
 			print_usage(prgname);
@@ -778,6 +997,14 @@ main(int argc, char *argv[])
 		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
 	/* >8 End of parsing specific arguments. */
 
+	if (mode == MODE_PI) {
+		ptp_data.servo = malloc(sizeof(*(ptp_data.servo)));
+		if (!ptp_data.servo)
+			rte_exit(EXIT_FAILURE, "no memory for servo\n");
+
+		servo_init(ptp_data.servo);
+	}
+
 	/* Check that there is an even number of ports to send/receive on. */
 	nb_ports = rte_eth_dev_count_avail();
 
@@ -831,6 +1058,9 @@ main(int argc, char *argv[])
 		rte_eth_dev_close(portid);
 	}
 
+	if (mode == MODE_PI)
+		free(ptp_data.servo);
+
 	/* clean up the EAL */
 	rte_eal_cleanup();
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v2 0/3] add frequency adjustment support for PTP
  2024-09-05  1:31 [PATCH 0/3] add frequency adjustment support for PTP Mingjin Ye
                   ` (2 preceding siblings ...)
  2024-09-05  1:31 ` [PATCH 3/3] examples/ptpclient: add frequency adjustment support Mingjin Ye
@ 2024-09-10  9:13 ` Mingjin Ye
  2024-09-10  9:13   ` [PATCH v2 1/3] ethdev: add frequency adjustment API Mingjin Ye
                     ` (4 more replies)
  3 siblings, 5 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-09-10  9:13 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye

[1/3] ethdev: add frequency adjustment API
[2/3] net/ice: add frequency adjustment support for PTP
[3/3] examples/ptpclient: add frequency adjustment support
---
v2: rte_eth_timesync_adjust_freq marked as experimental.

Mingjin Ye (3):
  ethdev: add frequency adjustment API
  net/ice: add frequency adjustment support for PTP
  examples/ptpclient: add frequency adjustment support

 doc/guides/nics/features.rst           |   4 +-
 doc/guides/nics/ice.rst                |  15 ++
 doc/guides/rel_notes/release_24_11.rst |  30 +--
 drivers/net/ice/ice_ethdev.c           | 177 +++++++++++----
 drivers/net/ice/ice_ethdev.h           |   2 +
 drivers/net/ice/ice_rxtx.c             |   4 +-
 examples/ptpclient/ptpclient.c         | 298 ++++++++++++++++++++++---
 lib/ethdev/ethdev_driver.h             |   5 +
 lib/ethdev/ethdev_trace.h              |   9 +
 lib/ethdev/ethdev_trace_points.c       |   3 +
 lib/ethdev/rte_ethdev.c                |  18 ++
 lib/ethdev/rte_ethdev.h                |  20 ++
 lib/ethdev/version.map                 |   3 +
 13 files changed, 481 insertions(+), 107 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v2 1/3] ethdev: add frequency adjustment API
  2024-09-10  9:13 ` [PATCH v2 0/3] add frequency adjustment support for PTP Mingjin Ye
@ 2024-09-10  9:13   ` Mingjin Ye
  2024-09-22 18:59     ` Ferruh Yigit
  2024-09-23  3:11     ` fengchengwen
  2024-09-10  9:13   ` [PATCH v2 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
                     ` (3 subsequent siblings)
  4 siblings, 2 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-09-10  9:13 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko

This patch adds freq adjustment API for PTP high accuracy.

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
 doc/guides/nics/features.rst           |  4 +++-
 doc/guides/rel_notes/release_24_11.rst | 30 +++-----------------------
 lib/ethdev/ethdev_driver.h             |  5 +++++
 lib/ethdev/ethdev_trace.h              |  9 ++++++++
 lib/ethdev/ethdev_trace_points.c       |  3 +++
 lib/ethdev/rte_ethdev.c                | 18 ++++++++++++++++
 lib/ethdev/rte_ethdev.h                | 20 +++++++++++++++++
 lib/ethdev/version.map                 |  3 +++
 8 files changed, 64 insertions(+), 28 deletions(-)

diff --git a/doc/guides/nics/features.rst b/doc/guides/nics/features.rst
index cd0115ffb3..0508f118fe 100644
--- a/doc/guides/nics/features.rst
+++ b/doc/guides/nics/features.rst
@@ -677,10 +677,12 @@ Supports IEEE1588/802.1AS timestamping.
 
 * **[implements] eth_dev_ops**: ``timesync_enable``, ``timesync_disable``
   ``timesync_read_rx_timestamp``, ``timesync_read_tx_timestamp``,
-  ``timesync_adjust_time``, ``timesync_read_time``, ``timesync_write_time``.
+  ``timesync_adjust_time``, ``timesync_adjust_freq``,
+  ``timesync_read_time``, ``timesync_write_time``.
 * **[related]    API**: ``rte_eth_timesync_enable()``, ``rte_eth_timesync_disable()``,
   ``rte_eth_timesync_read_rx_timestamp()``,
   ``rte_eth_timesync_read_tx_timestamp``, ``rte_eth_timesync_adjust_time()``,
+  ``rte_eth_timesync_adjust_freq()``,
   ``rte_eth_timesync_read_time()``, ``rte_eth_timesync_write_time()``.
 
 
diff --git a/doc/guides/rel_notes/release_24_11.rst b/doc/guides/rel_notes/release_24_11.rst
index 0ff70d9057..10ac35e5c0 100644
--- a/doc/guides/rel_notes/release_24_11.rst
+++ b/doc/guides/rel_notes/release_24_11.rst
@@ -25,35 +25,11 @@ New Features
 ------------
 
 .. This section should contain new features added in this release.
-   Sample format:
 
-   * **Add a title in the past tense with a full stop.**
+* **Added Ethernet device clock incremental rate adjustment.**
 
-     Add a short 1-2 sentence description in the past tense.
-     The description should be enough to allow someone scanning
-     the release notes to understand the new feature.
-
-     If the feature adds a lot of sub-features you can use a bullet list
-     like this:
-
-     * Added feature foo to do something.
-     * Enhanced feature bar to do something else.
-
-     Refer to the previous release notes for examples.
-
-     Suggested order in release notes items:
-     * Core libs (EAL, mempool, ring, mbuf, buses)
-     * Device abstraction libs and PMDs (ordered alphabetically by vendor name)
-       - ethdev (lib, PMDs)
-       - cryptodev (lib, PMDs)
-       - eventdev (lib, PMDs)
-       - etc
-     * Other libs
-     * Apps, Examples, Tools (if significant)
-
-     This section is a comment. Do not overwrite or remove it.
-     Also, make sure to start the actual text at the margin.
-     =======================================================
+  Added new function ``rte_eth_timesync_adjust_freq`` to
+  adjust the clock increment rate for Ethernet devices.
 
 
 Removed Items
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 883e59a927..68cadc14a5 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -664,6 +664,9 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 /** @internal Function used to adjust the device clock. */
 typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
 
+/** @internal Function used to adjust the clock frequency. */
+typedef int (*eth_timesync_adjust_freq)(struct rte_eth_dev *dev, int64_t);
+
 /** @internal Function used to get time from the device clock. */
 typedef int (*eth_timesync_read_time)(struct rte_eth_dev *dev,
 				      struct timespec *timestamp);
@@ -1378,6 +1381,8 @@ struct eth_dev_ops {
 	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
 	/** Adjust the device clock */
 	eth_timesync_adjust_time   timesync_adjust_time;
+	/** Adjust the clock frequency */
+	eth_timesync_adjust_freq   timesync_adjust_freq;
 	/** Get the device clock time */
 	eth_timesync_read_time     timesync_read_time;
 	/** Set the device clock time */
diff --git a/lib/ethdev/ethdev_trace.h b/lib/ethdev/ethdev_trace.h
index 3bec87bfdb..e273d5853e 100644
--- a/lib/ethdev/ethdev_trace.h
+++ b/lib/ethdev/ethdev_trace.h
@@ -2183,6 +2183,15 @@ RTE_TRACE_POINT_FP(
 	rte_trace_point_emit_int(ret);
 )
 
+/* Called in loop in examples/ptpclient */
+RTE_TRACE_POINT_FP(
+	rte_eth_trace_timesync_adjust_freq,
+	RTE_TRACE_POINT_ARGS(uint16_t port_id, int64_t ppm, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_i64(ppm);
+	rte_trace_point_emit_int(ret);
+)
+
 /* Called in loop in app/test-flow-perf */
 RTE_TRACE_POINT_FP(
 	rte_flow_trace_create,
diff --git a/lib/ethdev/ethdev_trace_points.c b/lib/ethdev/ethdev_trace_points.c
index 99e04f5893..a99fec0c1e 100644
--- a/lib/ethdev/ethdev_trace_points.c
+++ b/lib/ethdev/ethdev_trace_points.c
@@ -409,6 +409,9 @@ RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_tx_timestamp,
 RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_time,
 	lib.ethdev.timesync_adjust_time)
 
+RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_freq,
+	lib.ethdev.timesync_adjust_freq)
+
 RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_time,
 	lib.ethdev.timesync_read_time)
 
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index f1c658f49e..660eab2f1e 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -6310,6 +6310,24 @@ rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta)
 	return ret;
 }
 
+int
+rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (*dev->dev_ops->timesync_adjust_freq == NULL)
+		return -ENOTSUP;
+	ret = eth_err(port_id, (*dev->dev_ops->timesync_adjust_freq)(dev, ppm));
+
+	rte_eth_trace_timesync_adjust_freq(port_id, ppm, ret);
+
+	return ret;
+}
+
 int
 rte_eth_timesync_read_time(uint16_t port_id, struct timespec *timestamp)
 {
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index 548fada1c7..c3587089b9 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -5297,6 +5297,26 @@ int rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
  */
 int rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta);
 
+/**
+ * Adjust the clock increment rate on an Ethernet device.
+ *
+ * This is usually used in conjunction with other Ethdev timesync functions to
+ * synchronize the device time using the IEEE1588/802.1AS protocol.
+ *
+ * @param port_id
+ *  The port identifier of the Ethernet device.
+ * @param ppm
+ *  Parts per million with 16-bit fractional field
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+__rte_experimental
+int rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm);
+
 /**
  * Read the time from the timesync clock on an Ethernet device.
  *
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 1669055ca5..199480ef07 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -325,6 +325,9 @@ EXPERIMENTAL {
 	rte_flow_template_table_resizable;
 	rte_flow_template_table_resize;
 	rte_flow_template_table_resize_complete;
+
+	# added in 24.11
+	rte_eth_timesync_adjust_freq;
 };
 
 INTERNAL {
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v2 2/3] net/ice: add frequency adjustment support for PTP
  2024-09-10  9:13 ` [PATCH v2 0/3] add frequency adjustment support for PTP Mingjin Ye
  2024-09-10  9:13   ` [PATCH v2 1/3] ethdev: add frequency adjustment API Mingjin Ye
@ 2024-09-10  9:13   ` Mingjin Ye
  2024-09-10  9:13   ` [PATCH v2 3/3] examples/ptpclient: add frequency adjustment support Mingjin Ye
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-09-10  9:13 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su

Add ice support for new ethdev API to adjust frequency for IEEE1588
PTP. Also, this patch reworks code for converting software update
to hardware update.

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
 doc/guides/nics/ice.rst      |  15 +++
 drivers/net/ice/ice_ethdev.c | 177 ++++++++++++++++++++++++++---------
 drivers/net/ice/ice_ethdev.h |   2 +
 drivers/net/ice/ice_rxtx.c   |   4 +-
 4 files changed, 153 insertions(+), 45 deletions(-)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index ae975d19ad..dec4cc2434 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -328,6 +328,21 @@ Forward Error Correction (FEC)
 
 Supports get/set FEC mode and get FEC capability.
 
+Time Synchronisation
+~~~~~~~~~~~~~~~~~~~~
+
+The system operator can run a PTP (Precision Time Protocol) client application
+to synchronise the time on the network card (and optionally the time on the
+system) to the PTP master.
+
+ICE PMD supports PTP client applications that use the DPDK IEEE1588 API to
+communicate with the PTP master clock. Note that the PTP client application
+needs to run on the PF and vector mode needs to be disabled.
+
+.. code-block:: console
+
+    examples/dpdk-ptpclient -c f -n 3 -a 0000:ec:00.1 -- -T 1 -p 0x1 --controller=pi
+
 Generic Flow Support
 ~~~~~~~~~~~~~~~~~~~~
 
diff --git a/drivers/net/ice/ice_ethdev.c b/drivers/net/ice/ice_ethdev.c
index 304f959b7e..33293a917f 100644
--- a/drivers/net/ice/ice_ethdev.c
+++ b/drivers/net/ice/ice_ethdev.c
@@ -10,6 +10,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <math.h>
 
 #include <rte_tailq.h>
 #include <rte_os_shim.h>
@@ -176,6 +177,7 @@ static int ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 static int ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					  struct timespec *timestamp);
 static int ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
+static int ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm);
 static int ice_timesync_read_time(struct rte_eth_dev *dev,
 				  struct timespec *timestamp);
 static int ice_timesync_write_time(struct rte_eth_dev *dev,
@@ -307,6 +309,7 @@ static const struct eth_dev_ops ice_eth_dev_ops = {
 	.timesync_read_rx_timestamp   = ice_timesync_read_rx_timestamp,
 	.timesync_read_tx_timestamp   = ice_timesync_read_tx_timestamp,
 	.timesync_adjust_time         = ice_timesync_adjust_time,
+	.timesync_adjust_freq         = ice_timesync_adjust_freq,
 	.timesync_read_time           = ice_timesync_read_time,
 	.timesync_write_time          = ice_timesync_write_time,
 	.timesync_disable             = ice_timesync_disable,
@@ -2332,6 +2335,34 @@ ice_get_supported_rxdid(struct ice_hw *hw)
 	return supported_rxdid;
 }
 
+static void ice_ptp_init_info(struct rte_eth_dev *dev)
+{
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ice_adapter *ad =
+		ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+
+	switch (hw->phy_model) {
+	case ICE_PHY_ETH56G:
+		ad->ptp_tx_block = hw->pf_id;
+		ad->ptp_tx_index = 0;
+		break;
+	case ICE_PHY_E810:
+	/* fallthrough */
+	case ICE_PHY_E830:
+		ad->ptp_tx_block = hw->port_info->lport;
+		ad->ptp_tx_index = 0;
+		break;
+	case ICE_PHY_E822:
+		ad->ptp_tx_block = hw->pf_id / ICE_PORTS_PER_QUAD;
+		ad->ptp_tx_index = (hw->pf_id % ICE_PORTS_PER_QUAD) *
+				ICE_PORTS_PER_PHY_E822 * ICE_QUADS_PER_PHY_E822;
+		break;
+	default:
+		PMD_DRV_LOG(WARNING, "Unsupported PHY model");
+		break;
+	}
+}
+
 static int
 ice_dev_init(struct rte_eth_dev *dev)
 {
@@ -2499,6 +2530,9 @@ ice_dev_init(struct rte_eth_dev *dev)
 	/* Initialize PHY model */
 	ice_ptp_init_phy_model(hw);
 
+	/* Initialize PTP info */
+	ice_ptp_init_info(dev);
+
 	if (hw->phy_model == ICE_PHY_E822) {
 		ret = ice_start_phy_timer_e822(hw, hw->pf_id);
 		if (ret)
@@ -6466,23 +6500,6 @@ ice_timesync_enable(struct rte_eth_dev *dev)
 		}
 	}
 
-	/* Initialize cycle counters for system time/RX/TX timestamp */
-	memset(&ad->systime_tc, 0, sizeof(struct rte_timecounter));
-	memset(&ad->rx_tstamp_tc, 0, sizeof(struct rte_timecounter));
-	memset(&ad->tx_tstamp_tc, 0, sizeof(struct rte_timecounter));
-
-	ad->systime_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->systime_tc.cc_shift = 0;
-	ad->systime_tc.nsec_mask = 0;
-
-	ad->rx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->rx_tstamp_tc.cc_shift = 0;
-	ad->rx_tstamp_tc.nsec_mask = 0;
-
-	ad->tx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->tx_tstamp_tc.cc_shift = 0;
-	ad->tx_tstamp_tc.nsec_mask = 0;
-
 	ad->ptp_ena = 1;
 
 	return 0;
@@ -6497,14 +6514,13 @@ ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	struct ice_rx_queue *rxq;
 	uint32_t ts_high;
-	uint64_t ts_ns, ns;
+	uint64_t ts_ns;
 
 	rxq = dev->data->rx_queues[flags];
 
 	ts_high = rxq->time_high;
 	ts_ns = ice_tstamp_convert_32b_64b(hw, ad, 1, ts_high);
-	ns = rte_timecounter_update(&ad->rx_tstamp_tc, ts_ns);
-	*timestamp = rte_ns_to_timespec(ns);
+	*timestamp = rte_ns_to_timespec(ts_ns);
 
 	return 0;
 }
@@ -6516,22 +6532,18 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	struct ice_adapter *ad =
 			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-	uint8_t lport;
-	uint64_t ts_ns, ns, tstamp;
+	uint64_t ts_ns, tstamp;
 	const uint64_t mask = 0xFFFFFFFF;
 	int ret;
 
-	lport = hw->port_info->lport;
-
-	ret = ice_read_phy_tstamp(hw, lport, 0, &tstamp);
-	if (ret) {
+	ret = ice_read_phy_tstamp(hw, ad->ptp_tx_block, ad->ptp_tx_index, &tstamp);
+	if (ret || tstamp == 0) {
 		PMD_DRV_LOG(ERR, "Failed to read phy timestamp");
 		return -1;
 	}
 
 	ts_ns = ice_tstamp_convert_32b_64b(hw, ad, 1, (tstamp >> 8) & mask);
-	ns = rte_timecounter_update(&ad->tx_tstamp_tc, ts_ns);
-	*timestamp = rte_ns_to_timespec(ns);
+	*timestamp = rte_ns_to_timespec(ts_ns);
 
 	return 0;
 }
@@ -6539,28 +6551,108 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 static int
 ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
 {
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint8_t tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc;
+	uint32_t lo, lo2, hi;
+	uint64_t time, ns;
+	int ret;
+
+	if (delta > INT32_MAX || delta < INT32_MIN) {
+		lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+		hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
+		lo2 = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+
+		if (lo2 < lo) {
+			lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+			hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
+		}
+
+		time = ((uint64_t)hi << 32) | lo;
+		ns = time + delta;
+
+		wr32(hw, GLTSYN_SHTIME_L(tmr_idx), ICE_LO_DWORD(ns));
+		wr32(hw, GLTSYN_SHTIME_H(tmr_idx), ICE_HI_DWORD(ns));
+		wr32(hw, GLTSYN_SHTIME_0(tmr_idx), 0);
+
+		ret = ice_ptp_init_time(hw, ns, true);
+		if (ret) {
+			PMD_DRV_LOG(ERR, "PTP init time failed, err %d", ret);
+			return -1;
+		}
+		return 0;
+	}
+
+	ret = ice_ptp_adj_clock(hw, delta, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP adj clock failed, err %d", ret);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	int64_t incval, diff = 0;
+	bool negative = false;
+	uint64_t div, rem;
+	uint64_t divisor = 1000000ULL << 16;
+	int shift;
+	int ret;
+
+	incval = ice_get_base_incval(hw, ICE_SRC_TMR_MODE_NANOSECONDS);
+
+	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;
 
-	ad->systime_tc.nsec += delta;
-	ad->rx_tstamp_tc.nsec += delta;
-	ad->tx_tstamp_tc.nsec += delta;
+		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 = ice_ptp_write_incval_locked(hw, incval, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+		return -1;
+	}
 	return 0;
 }
 
 static int
 ice_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
 {
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint64_t ns;
+	int ret;
 
 	ns = rte_timespec_to_ns(ts);
-
-	ad->systime_tc.nsec = ns;
-	ad->rx_tstamp_tc.nsec = ns;
-	ad->tx_tstamp_tc.nsec = ns;
+	ret = ice_ptp_init_time(hw, ns, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP init time failed, err %d", ret);
+		return -1;
+	}
 
 	return 0;
 }
@@ -6569,11 +6661,9 @@ static int
 ice_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
 {
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	uint8_t tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc;
 	uint32_t hi, lo, lo2;
-	uint64_t time, ns;
+	uint64_t time;
 
 	lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
 	hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
@@ -6585,8 +6675,7 @@ ice_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
 	}
 
 	time = ((uint64_t)hi << 32) | lo;
-	ns = rte_timecounter_update(&ad->systime_tc, time);
-	*ts = rte_ns_to_timespec(ns);
+	*ts = rte_ns_to_timespec(time);
 
 	return 0;
 }
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index 3ea9f37dc8..db192e76fe 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -614,6 +614,8 @@ struct ice_adapter {
 	struct rte_timecounter systime_tc;
 	struct rte_timecounter rx_tstamp_tc;
 	struct rte_timecounter tx_tstamp_tc;
+	uint8_t ptp_tx_block;
+	uint8_t ptp_tx_index;
 	bool ptp_ena;
 	uint64_t time_hw;
 	struct ice_fdir_prof_info fdir_prof_info[ICE_MAX_PTGS];
diff --git a/drivers/net/ice/ice_rxtx.c b/drivers/net/ice/ice_rxtx.c
index f270498ed1..5f399e8aae 100644
--- a/drivers/net/ice/ice_rxtx.c
+++ b/drivers/net/ice/ice_rxtx.c
@@ -3066,7 +3066,9 @@ ice_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
 			else if (ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST)
 				cd_type_cmd_tso_mss |=
 					((uint64_t)ICE_TX_CTX_DESC_TSYN <<
-					ICE_TXD_CTX_QW1_CMD_S);
+					ICE_TXD_CTX_QW1_CMD_S) |
+					 (((uint64_t)txq->vsi->adapter->ptp_tx_index <<
+					 ICE_TXD_CTX_QW1_TSYN_S) & ICE_TXD_CTX_QW1_TSYN_M);
 
 			ctx_txd->tunneling_params =
 				rte_cpu_to_le_32(cd_tunneling_params);
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v2 3/3] examples/ptpclient: add frequency adjustment support
  2024-09-10  9:13 ` [PATCH v2 0/3] add frequency adjustment support for PTP Mingjin Ye
  2024-09-10  9:13   ` [PATCH v2 1/3] ethdev: add frequency adjustment API Mingjin Ye
  2024-09-10  9:13   ` [PATCH v2 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
@ 2024-09-10  9:13   ` Mingjin Ye
  2024-09-22 19:06   ` [PATCH v2 0/3] add frequency adjustment support for PTP Ferruh Yigit
  2024-09-30  8:42   ` [PATCH v3 " Mingjin Ye
  4 siblings, 0 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-09-10  9:13 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su, Wenjun Wu, Kirill Rybalchenko

This patch adds PI servo algorithm to support frequency
adjustment API for IEEE1588 PTP.

For example, the command for starting ptpclient with PI algorithm is:
./build/examples/dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1
--controller=pi

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
 examples/ptpclient/ptpclient.c | 298 +++++++++++++++++++++++++++++----
 1 file changed, 264 insertions(+), 34 deletions(-)

diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
index afb61bba51..e92f7c5c8d 100644
--- a/examples/ptpclient/ptpclient.c
+++ b/examples/ptpclient/ptpclient.c
@@ -46,6 +46,35 @@ static volatile bool force_quit;
 #define KERNEL_TIME_ADJUST_LIMIT  20000
 #define PTP_PROTOCOL             0x88F7
 
+#define KP 0.7
+#define KI 0.3
+#define FREQ_EST_MARGIN 0.001
+
+enum servo_state {
+	SERVO_UNLOCKED,
+	SERVO_JUMP,
+	SERVO_LOCKED,
+};
+
+struct pi_servo {
+	double offset[2];
+	double local[2];
+	double drift;
+	double last_freq;
+	int count;
+
+	double max_frequency;
+	double step_threshold;
+	double first_step_threshold;
+	int first_update;
+};
+
+enum controller_mode {
+	MODE_NONE,
+	MODE_PI,
+	MAX_ALL
+} mode = MODE_PI;
+
 struct rte_mempool *mbuf_pool;
 uint32_t ptp_enabled_port_mask;
 uint8_t ptp_enabled_port_nb;
@@ -135,6 +164,9 @@ struct ptpv2_data_slave_ordinary {
 	uint8_t ptpset;
 	uint8_t kernel_time_set;
 	uint16_t current_ptp_port;
+	int64_t master_offset;
+	int64_t path_delay;
+	struct pi_servo *servo;
 };
 
 static struct ptpv2_data_slave_ordinary ptp_data;
@@ -293,36 +325,44 @@ print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
 			ptp_data->tstamp3.tv_sec,
 			(ptp_data->tstamp3.tv_nsec));
 
-	printf("\nT4 - Master Clock.  %lds %ldns ",
+	printf("\nT4 - Master Clock.  %lds %ldns\n",
 			ptp_data->tstamp4.tv_sec,
 			(ptp_data->tstamp4.tv_nsec));
 
-	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+	if (mode == MODE_NONE) {
+		printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
 			ptp_data->delta);
 
-	clock_gettime(CLOCK_REALTIME, &sys_time);
-	rte_eth_timesync_read_time(ptp_data->current_ptp_port, &net_time);
+		clock_gettime(CLOCK_REALTIME, &sys_time);
+		rte_eth_timesync_read_time(ptp_data->current_ptp_port,
+					   &net_time);
 
-	time_t ts = net_time.tv_sec;
+		time_t ts = net_time.tv_sec;
 
-	printf("\n\nComparison between Linux kernel Time and PTP:");
+		printf("\n\nComparison between Linux kernel Time and PTP:");
 
-	printf("\nCurrent PTP Time: %.24s %.9ld ns",
+		printf("\nCurrent PTP Time: %.24s %.9ld ns",
 			ctime(&ts), net_time.tv_nsec);
 
-	nsec = (int64_t)timespec64_to_ns(&net_time) -
+		nsec = (int64_t)timespec64_to_ns(&net_time) -
 			(int64_t)timespec64_to_ns(&sys_time);
-	ptp_data->new_adj = ns_to_timeval(nsec);
+		ptp_data->new_adj = ns_to_timeval(nsec);
 
-	gettimeofday(&ptp_data->new_adj, NULL);
+		gettimeofday(&ptp_data->new_adj, NULL);
 
-	time_t tp = ptp_data->new_adj.tv_sec;
+		time_t tp = ptp_data->new_adj.tv_sec;
 
-	printf("\nCurrent SYS Time: %.24s %.6ld ns",
-				ctime(&tp), ptp_data->new_adj.tv_usec);
+		printf("\nCurrent SYS Time: %.24s %.6ld ns",
+			ctime(&tp), ptp_data->new_adj.tv_usec);
 
-	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
-				nsec);
+		printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
+			nsec);
+	}
+
+	if (mode == MODE_PI) {
+		printf("path delay: %"PRId64"ns\n", ptp_data->path_delay);
+		printf("master offset: %"PRId64"ns\n", ptp_data->master_offset);
+	}
 
 	printf("[Ctrl+C to quit]\n");
 
@@ -385,21 +425,11 @@ parse_sync(struct ptpv2_data_slave_ordinary *ptp_data, uint16_t rx_tstamp_idx)
 static void
 parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
 {
-	struct rte_ether_hdr *eth_hdr;
-	struct rte_ether_addr eth_addr;
 	struct ptp_header *ptp_hdr;
-	struct clock_id *client_clkid;
 	struct ptp_message *ptp_msg;
-	struct delay_req_msg *req_msg;
-	struct rte_mbuf *created_pkt;
 	struct tstamp *origin_tstamp;
-	struct rte_ether_addr eth_multicast = ether_multicast;
-	size_t pkt_size;
-	int wait_us;
 	struct rte_mbuf *m = ptp_data->m;
-	int ret;
 
-	eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
 	ptp_hdr = rte_pktmbuf_mtod_offset(m, struct ptp_header *,
 					  sizeof(struct rte_ether_hdr));
 	if (memcmp(&ptp_data->master_clock_id,
@@ -416,6 +446,150 @@ parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
 	ptp_data->tstamp1.tv_sec =
 		((uint64_t)ntohl(origin_tstamp->sec_lsb)) |
 		(((uint64_t)ntohs(origin_tstamp->sec_msb)) << 32);
+}
+
+static double
+pi_sample(struct pi_servo *s, int64_t offset, double local_ts,
+	  enum servo_state *state)
+{
+	double ki_term, ppb = s->last_freq;
+	double freq_est_interval, localdiff;
+
+	switch (s->count) {
+	case 0:
+		s->offset[0] = offset;
+		s->local[0] = local_ts;
+		*state = SERVO_UNLOCKED;
+		s->count = 1;
+		break;
+	case 1:
+		s->offset[1] = offset;
+		s->local[1] = local_ts;
+
+		/* Make sure the first sample is older than the second. */
+		if (s->local[0] >= s->local[1]) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		/* Wait long enough before estimating the frequency offset. */
+		localdiff = (s->local[1] - s->local[0]) / 1e9;
+		localdiff += localdiff * FREQ_EST_MARGIN;
+		freq_est_interval = 0.016 / KI;
+		if (freq_est_interval > 1000.0)
+			freq_est_interval = 1000.0;
+
+		if (localdiff < freq_est_interval) {
+			*state = SERVO_UNLOCKED;
+			break;
+		}
+
+		/* Adjust drift by the measured frequency offset. */
+		s->drift += (1e9 - s->drift) * (s->offset[1] - s->offset[0]) /
+						(s->local[1] - s->local[0]);
+
+		if (s->drift < -s->max_frequency)
+			s->drift = -s->max_frequency;
+		else if (s->drift > s->max_frequency)
+			s->drift = s->max_frequency;
+
+		if ((s->first_update &&
+		     s->first_step_threshold &&
+		     s->first_step_threshold < llabs(offset)) ||
+		    (s->step_threshold &&
+		     s->step_threshold < llabs(offset)))
+			*state = SERVO_JUMP;
+		else
+			*state = SERVO_LOCKED;
+
+		ppb = s->drift;
+		s->count = 2;
+		break;
+	case 2:
+		/*
+		 * reset the clock servo when offset is greater than the max
+		 * offset value. Note that the clock jump will be performed in
+		 * step 1, so it is not necessary to have clock jump
+		 * immediately. This allows re-calculating drift as in initial
+		 * clock startup.
+		 */
+		if (s->step_threshold &&
+		    s->step_threshold < llabs(offset)) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		ki_term = KI * offset;
+		ppb = KP * offset + s->drift + ki_term;
+		if (ppb < -s->max_frequency)
+			ppb = -s->max_frequency;
+		else if (ppb > s->max_frequency)
+			ppb = s->max_frequency;
+		else
+			s->drift += ki_term;
+
+		*state = SERVO_LOCKED;
+		break;
+	}
+
+	s->last_freq = ppb;
+	return ppb;
+}
+
+static void
+ptp_adjust_freq(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns;
+	double adj_freq;
+	enum servo_state state = SERVO_UNLOCKED;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	ptp_data->master_offset = t2_ns - t1_ns - ptp_data->path_delay;
+	if (!ptp_data->path_delay)
+		return;
+
+	adj_freq = pi_sample(ptp_data->servo, ptp_data->master_offset, t2_ns,
+		     &state);
+
+	switch (state) {
+	case SERVO_UNLOCKED:
+		break;
+	case SERVO_JUMP:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+						-(long)(adj_freq * 65.536));
+		rte_eth_timesync_adjust_time(ptp_data->portid,
+					     -ptp_data->master_offset);
+		break;
+	case SERVO_LOCKED:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+					     -(long)(adj_freq * 65.536));
+		break;
+	}
+}
+
+static void
+send_delay_request(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct rte_ether_hdr *eth_hdr;
+	struct rte_ether_addr eth_addr;
+	struct ptp_header *ptp_hdr;
+	struct clock_id *client_clkid;
+	struct delay_req_msg *req_msg;
+	struct rte_mbuf *created_pkt;
+	struct rte_ether_addr eth_multicast = ether_multicast;
+	size_t pkt_size;
+	int wait_us;
+	struct rte_mbuf *m = ptp_data->m;
+	int ret;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+			+ sizeof(struct rte_ether_hdr));
 
 	if (ptp_data->seqID_FOLLOWUP == ptp_data->seqID_SYNC) {
 		ret = rte_eth_macaddr_get(ptp_data->portid, &eth_addr);
@@ -488,11 +662,10 @@ parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
 		ptp_data->tstamp3.tv_sec = 0;
 
 		/* Wait at least 1 us to read TX timestamp. */
-		while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
-				&ptp_data->tstamp3) < 0) && (wait_us < 1000)) {
+		do {
 			rte_delay_us(1);
-			wait_us++;
-		}
+		} while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->tstamp3) < 0) && (wait_us++ < 10));
 	}
 }
 
@@ -529,6 +702,25 @@ update_kernel_time(void)
 
 }
 
+static void
+clock_path_delay(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns, t3_ns, t4_ns;
+	int64_t pd, diff;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	t3_ns = timespec64_to_ns(&ptp_data->tstamp3);
+	t4_ns = timespec64_to_ns(&ptp_data->tstamp4);
+
+	pd = (t2_ns - t3_ns) + (t4_ns - t1_ns);
+	diff = t3_ns - t2_ns;
+	if (diff <= INT32_MAX && diff >= INT32_MIN)
+		ptp_data->path_delay = pd / 2;
+	else
+		ptp_data->path_delay = 0;
+}
+
 /*
  * Parse the DELAY_RESP message.
  */
@@ -553,11 +745,15 @@ parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
 				((uint64_t)ntohl(rx_tstamp->sec_lsb)) |
 				(((uint64_t)ntohs(rx_tstamp->sec_msb)) << 32);
 
-			/* Evaluate the delta for adjustment. */
-			ptp_data->delta = delta_eval(ptp_data);
+			if (mode == MODE_PI) {
+				clock_path_delay(ptp_data);
+			} else {
+				/* Evaluate the delta for adjustment. */
+				ptp_data->delta = delta_eval(ptp_data);
 
-			rte_eth_timesync_adjust_time(ptp_data->portid,
-						     ptp_data->delta);
+				rte_eth_timesync_adjust_time(ptp_data->portid,
+								ptp_data->delta);
+			}
 
 			ptp_data->current_ptp_port = ptp_data->portid;
 
@@ -597,6 +793,9 @@ parse_ptp_frames(uint16_t portid, struct rte_mbuf *m) {
 			break;
 		case FOLLOW_UP:
 			parse_fup(&ptp_data);
+			if (mode == MODE_PI)
+				ptp_adjust_freq(&ptp_data);
+			send_delay_request(&ptp_data);
 			break;
 		case DELAY_RESP:
 			parse_drsp(&ptp_data);
@@ -688,6 +887,18 @@ parse_ptp_kernel(const char *param)
 	return 1;
 }
 
+static void
+servo_init(struct pi_servo *servo)
+{
+	memset(servo, 0x00, sizeof(*servo));
+
+	servo->drift = 100000000;
+	servo->last_freq = 100000000;
+	servo->count = 0;
+	servo->max_frequency = 100000000;
+	servo->step_threshold = 0.1 * NSEC_PER_SEC;
+}
+
 /* Parse the commandline arguments. */
 static int
 ptp_parse_args(int argc, char **argv)
@@ -696,7 +907,10 @@ ptp_parse_args(int argc, char **argv)
 	char **argvopt;
 	int option_index;
 	char *prgname = argv[0];
-	static struct option lgopts[] = { {NULL, 0, 0, 0} };
+	static struct option lgopts[] = {
+		{"controller", 1, 0, 0},
+		{NULL, 0, 0, 0}
+	};
 
 	argvopt = argv;
 
@@ -724,6 +938,11 @@ ptp_parse_args(int argc, char **argv)
 
 			ptp_data.kernel_time_set = ret;
 			break;
+		case 0:
+			if (!strcmp(lgopts[option_index].name, "controller"))
+				if (!strcmp(optarg, "pi"))
+					mode = MODE_PI;
+			break;
 
 		default:
 			print_usage(prgname);
@@ -778,6 +997,14 @@ main(int argc, char *argv[])
 		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
 	/* >8 End of parsing specific arguments. */
 
+	if (mode == MODE_PI) {
+		ptp_data.servo = malloc(sizeof(*(ptp_data.servo)));
+		if (!ptp_data.servo)
+			rte_exit(EXIT_FAILURE, "no memory for servo\n");
+
+		servo_init(ptp_data.servo);
+	}
+
 	/* Check that there is an even number of ports to send/receive on. */
 	nb_ports = rte_eth_dev_count_avail();
 
@@ -831,6 +1058,9 @@ main(int argc, char *argv[])
 		rte_eth_dev_close(portid);
 	}
 
+	if (mode == MODE_PI)
+		free(ptp_data.servo);
+
 	/* clean up the EAL */
 	rte_eal_cleanup();
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 1/3] ethdev: add frequency adjustment API
  2024-09-10  9:13   ` [PATCH v2 1/3] ethdev: add frequency adjustment API Mingjin Ye
@ 2024-09-22 18:59     ` Ferruh Yigit
  2024-09-23  3:11     ` fengchengwen
  1 sibling, 0 replies; 49+ messages in thread
From: Ferruh Yigit @ 2024-09-22 18:59 UTC (permalink / raw)
  To: Mingjin Ye, dev; +Cc: Simei Su, Thomas Monjalon, Andrew Rybchenko

On 9/10/2024 10:13 AM, Mingjin Ye wrote:
> This patch adds freq adjustment API for PTP high accuracy.
> 
> Signed-off-by: Simei Su <simei.su@intel.com>
> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>

<...>

> diff --git a/doc/guides/rel_notes/release_24_11.rst b/doc/guides/rel_notes/release_24_11.rst
> index 0ff70d9057..10ac35e5c0 100644
> --- a/doc/guides/rel_notes/release_24_11.rst
> +++ b/doc/guides/rel_notes/release_24_11.rst
> @@ -25,35 +25,11 @@ New Features
>  ------------
>  
>  .. This section should contain new features added in this release.
> -   Sample format:
>  
> -   * **Add a title in the past tense with a full stop.**
> +* **Added Ethernet device clock incremental rate adjustment.**
>  
> -     Add a short 1-2 sentence description in the past tense.
> -     The description should be enough to allow someone scanning
> -     the release notes to understand the new feature.
> -
> -     If the feature adds a lot of sub-features you can use a bullet list
> -     like this:
> -
> -     * Added feature foo to do something.
> -     * Enhanced feature bar to do something else.
> -
> -     Refer to the previous release notes for examples.
> -
> -     Suggested order in release notes items:
> -     * Core libs (EAL, mempool, ring, mbuf, buses)
> -     * Device abstraction libs and PMDs (ordered alphabetically by vendor name)
> -       - ethdev (lib, PMDs)
> -       - cryptodev (lib, PMDs)
> -       - eventdev (lib, PMDs)
> -       - etc
> -     * Other libs
> -     * Apps, Examples, Tools (if significant)
> -
> -     This section is a comment. Do not overwrite or remove it.
> -     Also, make sure to start the actual text at the margin.
> -     =======================================================
> +  Added new function ``rte_eth_timesync_adjust_freq`` to
> +  adjust the clock increment rate for Ethernet devices.
> 

Can you please check above, patch removes unrelated parts of the
document, I assume caused by rebase issues.

>  
>  Removed Items
> diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
> index 883e59a927..68cadc14a5 100644
> --- a/lib/ethdev/ethdev_driver.h
> +++ b/lib/ethdev/ethdev_driver.h
> @@ -664,6 +664,9 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
>  /** @internal Function used to adjust the device clock. */
>  typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
>  
> +/** @internal Function used to adjust the clock frequency. */
> +typedef int (*eth_timesync_adjust_freq)(struct rte_eth_dev *dev, int64_t);
> +
>  /** @internal Function used to get time from the device clock. */
>  typedef int (*eth_timesync_read_time)(struct rte_eth_dev *dev,
>  				      struct timespec *timestamp);
> @@ -1378,6 +1381,8 @@ struct eth_dev_ops {
>  	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
>  	/** Adjust the device clock */
>  	eth_timesync_adjust_time   timesync_adjust_time;
> +	/** Adjust the clock frequency */
> +	eth_timesync_adjust_freq   timesync_adjust_freq;
>  	/** Get the device clock time */
>  	eth_timesync_read_time     timesync_read_time;
>  	/** Set the device clock time */
> diff --git a/lib/ethdev/ethdev_trace.h b/lib/ethdev/ethdev_trace.h
> index 3bec87bfdb..e273d5853e 100644
> --- a/lib/ethdev/ethdev_trace.h
> +++ b/lib/ethdev/ethdev_trace.h
> @@ -2183,6 +2183,15 @@ RTE_TRACE_POINT_FP(
>  	rte_trace_point_emit_int(ret);
>  )
>  
> +/* Called in loop in examples/ptpclient */
> +RTE_TRACE_POINT_FP(
> +	rte_eth_trace_timesync_adjust_freq,
> +	RTE_TRACE_POINT_ARGS(uint16_t port_id, int64_t ppm, int ret),
> +	rte_trace_point_emit_u16(port_id);
> +	rte_trace_point_emit_i64(ppm);
> +	rte_trace_point_emit_int(ret);
> +)
> +
>  /* Called in loop in app/test-flow-perf */
>  RTE_TRACE_POINT_FP(
>  	rte_flow_trace_create,
> diff --git a/lib/ethdev/ethdev_trace_points.c b/lib/ethdev/ethdev_trace_points.c
> index 99e04f5893..a99fec0c1e 100644
> --- a/lib/ethdev/ethdev_trace_points.c
> +++ b/lib/ethdev/ethdev_trace_points.c
> @@ -409,6 +409,9 @@ RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_tx_timestamp,
>  RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_time,
>  	lib.ethdev.timesync_adjust_time)
>  
> +RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_freq,
> +	lib.ethdev.timesync_adjust_freq)
> +
>  RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_time,
>  	lib.ethdev.timesync_read_time)
>  
> diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
> index f1c658f49e..660eab2f1e 100644
> --- a/lib/ethdev/rte_ethdev.c
> +++ b/lib/ethdev/rte_ethdev.c
> @@ -6310,6 +6310,24 @@ rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta)
>  	return ret;
>  }
>  
> +int
> +rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm)
> +{
> +	struct rte_eth_dev *dev;
> +	int ret;
> +
> +	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
> +	dev = &rte_eth_devices[port_id];
> +
> +	if (*dev->dev_ops->timesync_adjust_freq == NULL)
> +		return -ENOTSUP;
> +	ret = eth_err(port_id, (*dev->dev_ops->timesync_adjust_freq)(dev, ppm));
> +
> +	rte_eth_trace_timesync_adjust_freq(port_id, ppm, ret);
> +
> +	return ret;
> +}
> +
>  int
>  rte_eth_timesync_read_time(uint16_t port_id, struct timespec *timestamp)
>  {
> diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
> index 548fada1c7..c3587089b9 100644
> --- a/lib/ethdev/rte_ethdev.h
> +++ b/lib/ethdev/rte_ethdev.h
> @@ -5297,6 +5297,26 @@ int rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
>   */
>  int rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta);
>  
> +/**
> + * Adjust the clock increment rate on an Ethernet device.
> + *
> + * This is usually used in conjunction with other Ethdev timesync functions to
> + * synchronize the device time using the IEEE1588/802.1AS protocol.
> + *
> + * @param port_id
> + *  The port identifier of the Ethernet device.
> + * @param ppm
> + *  Parts per million with 16-bit fractional field
> + *
>

Can you please describe the API a little bit more?

I assume frequency can be adjusted up and down, how this reflected via API?
Can you please give some practical examples of how this API intended to use?
I assume after an initial sync, some time later the externally received
time information and device time mismatch and requires adjustment. In
this case:
1. How to decide which one of the API use,
'rte_eth_timesync_adjust_time()' and this new
'rte_eth_timesync_adjust_freq()' ?
2. Lets say device is behind 1us after 1 minute (is these scale of the
measurements makes sense, if not please give better examples), what
should be the 'ppm' parameter to fix this? And similar sample for the
case device time is faster than external case.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 0/3] add frequency adjustment support for PTP
  2024-09-10  9:13 ` [PATCH v2 0/3] add frequency adjustment support for PTP Mingjin Ye
                     ` (2 preceding siblings ...)
  2024-09-10  9:13   ` [PATCH v2 3/3] examples/ptpclient: add frequency adjustment support Mingjin Ye
@ 2024-09-22 19:06   ` Ferruh Yigit
  2024-09-23  2:47     ` Ye, MingjinX
  2024-09-30  8:42   ` [PATCH v3 " Mingjin Ye
  4 siblings, 1 reply; 49+ messages in thread
From: Ferruh Yigit @ 2024-09-22 19:06 UTC (permalink / raw)
  To: Mingjin Ye, dev; +Cc: Bruce Richardson

On 9/10/2024 10:13 AM, Mingjin Ye wrote:
> [1/3] ethdev: add frequency adjustment API
> [2/3] net/ice: add frequency adjustment support for PTP
> [3/3] examples/ptpclient: add frequency adjustment support
> ---
> v2: rte_eth_timesync_adjust_freq marked as experimental.
> 
> Mingjin Ye (3):
>   ethdev: add frequency adjustment API
>   net/ice: add frequency adjustment support for PTP
>   examples/ptpclient: add frequency adjustment support
>

Hi Mingjin,

Overall OK to have the API, and except from a minor documentation issue
patch is OK.

Only lets try to make describe it a little more detail, so users and
driver developers can use is better without confusion.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v2 0/3] add frequency adjustment support for PTP
  2024-09-22 19:06   ` [PATCH v2 0/3] add frequency adjustment support for PTP Ferruh Yigit
@ 2024-09-23  2:47     ` Ye, MingjinX
  0 siblings, 0 replies; 49+ messages in thread
From: Ye, MingjinX @ 2024-09-23  2:47 UTC (permalink / raw)
  To: Ferruh Yigit, dev; +Cc: Richardson, Bruce

Hi Yigit,

Ok, I will describe the documentation in detail to avoid confusion. Thanks for the review.

Regards,
Mingjin

> -----Original Message-----
> From: Ferruh Yigit <ferruh.yigit@amd.com>
> Sent: Monday, September 23, 2024 3:06 AM
> To: Ye, MingjinX <mingjinx.ye@intel.com>; dev@dpdk.org
> Cc: Richardson, Bruce <bruce.richardson@intel.com>
> Subject: Re: [PATCH v2 0/3] add frequency adjustment support for PTP
> 
> On 9/10/2024 10:13 AM, Mingjin Ye wrote:
> > [1/3] ethdev: add frequency adjustment API [2/3] net/ice: add
> > frequency adjustment support for PTP [3/3] examples/ptpclient: add
> > frequency adjustment support
> > ---
> > v2: rte_eth_timesync_adjust_freq marked as experimental.
> >
> > Mingjin Ye (3):
> >   ethdev: add frequency adjustment API
> >   net/ice: add frequency adjustment support for PTP
> >   examples/ptpclient: add frequency adjustment support
> >
> 
> Hi Mingjin,
> 
> Overall OK to have the API, and except from a minor documentation issue
> patch is OK.
> 
> Only lets try to make describe it a little more detail, so users and driver
> developers can use is better without confusion.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 1/3] ethdev: add frequency adjustment API
  2024-09-10  9:13   ` [PATCH v2 1/3] ethdev: add frequency adjustment API Mingjin Ye
  2024-09-22 18:59     ` Ferruh Yigit
@ 2024-09-23  3:11     ` fengchengwen
  2024-09-23  6:28       ` Ye, MingjinX
  1 sibling, 1 reply; 49+ messages in thread
From: fengchengwen @ 2024-09-23  3:11 UTC (permalink / raw)
  To: Mingjin Ye, dev; +Cc: Simei Su, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko

Hi Mingjin,

  Why we need this API? What's the use-case?

Thanks

On 2024/9/10 17:13, Mingjin Ye wrote:
> This patch adds freq adjustment API for PTP high accuracy.
> 
> Signed-off-by: Simei Su <simei.su@intel.com>
> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
> ---
>  doc/guides/nics/features.rst           |  4 +++-
>  doc/guides/rel_notes/release_24_11.rst | 30 +++-----------------------
>  lib/ethdev/ethdev_driver.h             |  5 +++++
>  lib/ethdev/ethdev_trace.h              |  9 ++++++++
>  lib/ethdev/ethdev_trace_points.c       |  3 +++
>  lib/ethdev/rte_ethdev.c                | 18 ++++++++++++++++
>  lib/ethdev/rte_ethdev.h                | 20 +++++++++++++++++
>  lib/ethdev/version.map                 |  3 +++
>  8 files changed, 64 insertions(+), 28 deletions(-)
> 
> diff --git a/doc/guides/nics/features.rst b/doc/guides/nics/features.rst
> index cd0115ffb3..0508f118fe 100644
> --- a/doc/guides/nics/features.rst
> +++ b/doc/guides/nics/features.rst
> @@ -677,10 +677,12 @@ Supports IEEE1588/802.1AS timestamping.
>  
>  * **[implements] eth_dev_ops**: ``timesync_enable``, ``timesync_disable``
>    ``timesync_read_rx_timestamp``, ``timesync_read_tx_timestamp``,
> -  ``timesync_adjust_time``, ``timesync_read_time``, ``timesync_write_time``.
> +  ``timesync_adjust_time``, ``timesync_adjust_freq``,
> +  ``timesync_read_time``, ``timesync_write_time``.
>  * **[related]    API**: ``rte_eth_timesync_enable()``, ``rte_eth_timesync_disable()``,
>    ``rte_eth_timesync_read_rx_timestamp()``,
>    ``rte_eth_timesync_read_tx_timestamp``, ``rte_eth_timesync_adjust_time()``,
> +  ``rte_eth_timesync_adjust_freq()``,
>    ``rte_eth_timesync_read_time()``, ``rte_eth_timesync_write_time()``.
>  
>  
> diff --git a/doc/guides/rel_notes/release_24_11.rst b/doc/guides/rel_notes/release_24_11.rst
> index 0ff70d9057..10ac35e5c0 100644
> --- a/doc/guides/rel_notes/release_24_11.rst
> +++ b/doc/guides/rel_notes/release_24_11.rst
> @@ -25,35 +25,11 @@ New Features
>  ------------
>  
>  .. This section should contain new features added in this release.
> -   Sample format:
>  
> -   * **Add a title in the past tense with a full stop.**
> +* **Added Ethernet device clock incremental rate adjustment.**
>  
> -     Add a short 1-2 sentence description in the past tense.
> -     The description should be enough to allow someone scanning
> -     the release notes to understand the new feature.
> -
> -     If the feature adds a lot of sub-features you can use a bullet list
> -     like this:
> -
> -     * Added feature foo to do something.
> -     * Enhanced feature bar to do something else.
> -
> -     Refer to the previous release notes for examples.
> -
> -     Suggested order in release notes items:
> -     * Core libs (EAL, mempool, ring, mbuf, buses)
> -     * Device abstraction libs and PMDs (ordered alphabetically by vendor name)
> -       - ethdev (lib, PMDs)
> -       - cryptodev (lib, PMDs)
> -       - eventdev (lib, PMDs)
> -       - etc
> -     * Other libs
> -     * Apps, Examples, Tools (if significant)
> -
> -     This section is a comment. Do not overwrite or remove it.
> -     Also, make sure to start the actual text at the margin.
> -     =======================================================

The above should not delete in this patch, it will remove when release

> +  Added new function ``rte_eth_timesync_adjust_freq`` to
> +  adjust the clock increment rate for Ethernet devices.
>  
>  
>  Removed Items
> diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
> index 883e59a927..68cadc14a5 100644
> --- a/lib/ethdev/ethdev_driver.h
> +++ b/lib/ethdev/ethdev_driver.h
> @@ -664,6 +664,9 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
>  /** @internal Function used to adjust the device clock. */
>  typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
>  
> +/** @internal Function used to adjust the clock frequency. */
> +typedef int (*eth_timesync_adjust_freq)(struct rte_eth_dev *dev, int64_t);
> +
>  /** @internal Function used to get time from the device clock. */
>  typedef int (*eth_timesync_read_time)(struct rte_eth_dev *dev,
>  				      struct timespec *timestamp);
> @@ -1378,6 +1381,8 @@ struct eth_dev_ops {
>  	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
>  	/** Adjust the device clock */
>  	eth_timesync_adjust_time   timesync_adjust_time;
> +	/** Adjust the clock frequency */
> +	eth_timesync_adjust_freq   timesync_adjust_freq;
>  	/** Get the device clock time */
>  	eth_timesync_read_time     timesync_read_time;
>  	/** Set the device clock time */
> diff --git a/lib/ethdev/ethdev_trace.h b/lib/ethdev/ethdev_trace.h
> index 3bec87bfdb..e273d5853e 100644
> --- a/lib/ethdev/ethdev_trace.h
> +++ b/lib/ethdev/ethdev_trace.h
> @@ -2183,6 +2183,15 @@ RTE_TRACE_POINT_FP(
>  	rte_trace_point_emit_int(ret);
>  )
>  
> +/* Called in loop in examples/ptpclient */
> +RTE_TRACE_POINT_FP(
> +	rte_eth_trace_timesync_adjust_freq,
> +	RTE_TRACE_POINT_ARGS(uint16_t port_id, int64_t ppm, int ret),
> +	rte_trace_point_emit_u16(port_id);
> +	rte_trace_point_emit_i64(ppm);
> +	rte_trace_point_emit_int(ret);
> +)
> +
>  /* Called in loop in app/test-flow-perf */
>  RTE_TRACE_POINT_FP(
>  	rte_flow_trace_create,
> diff --git a/lib/ethdev/ethdev_trace_points.c b/lib/ethdev/ethdev_trace_points.c
> index 99e04f5893..a99fec0c1e 100644
> --- a/lib/ethdev/ethdev_trace_points.c
> +++ b/lib/ethdev/ethdev_trace_points.c
> @@ -409,6 +409,9 @@ RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_tx_timestamp,
>  RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_time,
>  	lib.ethdev.timesync_adjust_time)
>  
> +RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_freq,
> +	lib.ethdev.timesync_adjust_freq)
> +
>  RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_time,
>  	lib.ethdev.timesync_read_time)
>  
> diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
> index f1c658f49e..660eab2f1e 100644
> --- a/lib/ethdev/rte_ethdev.c
> +++ b/lib/ethdev/rte_ethdev.c
> @@ -6310,6 +6310,24 @@ rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta)
>  	return ret;
>  }
>  
> +int
> +rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm)
> +{
> +	struct rte_eth_dev *dev;
> +	int ret;
> +
> +	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
> +	dev = &rte_eth_devices[port_id];
> +
> +	if (*dev->dev_ops->timesync_adjust_freq == NULL)
> +		return -ENOTSUP;
> +	ret = eth_err(port_id, (*dev->dev_ops->timesync_adjust_freq)(dev, ppm));
> +
> +	rte_eth_trace_timesync_adjust_freq(port_id, ppm, ret);
> +
> +	return ret;
> +}
> +
>  int
>  rte_eth_timesync_read_time(uint16_t port_id, struct timespec *timestamp)
>  {
> diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
> index 548fada1c7..c3587089b9 100644
> --- a/lib/ethdev/rte_ethdev.h
> +++ b/lib/ethdev/rte_ethdev.h
> @@ -5297,6 +5297,26 @@ int rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
>   */
>  int rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta);
>  
> +/**
> + * Adjust the clock increment rate on an Ethernet device.
> + *
> + * This is usually used in conjunction with other Ethdev timesync functions to
> + * synchronize the device time using the IEEE1588/802.1AS protocol.
> + *
> + * @param port_id
> + *  The port identifier of the Ethernet device.
> + * @param ppm
> + *  Parts per million with 16-bit fractional field
> + *
> + * @return
> + *   - 0: Success.
> + *   - -ENODEV: The port ID is invalid.
> + *   - -EIO: if device is removed.
> + *   - -ENOTSUP: The function is not supported by the Ethernet driver.
> + */
> +__rte_experimental
> +int rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm);
> +
>  /**
>   * Read the time from the timesync clock on an Ethernet device.
>   *
> diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
> index 1669055ca5..199480ef07 100644
> --- a/lib/ethdev/version.map
> +++ b/lib/ethdev/version.map
> @@ -325,6 +325,9 @@ EXPERIMENTAL {
>  	rte_flow_template_table_resizable;
>  	rte_flow_template_table_resize;
>  	rte_flow_template_table_resize_complete;
> +
> +	# added in 24.11
> +	rte_eth_timesync_adjust_freq;
>  };
>  
>  INTERNAL {


^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v2 1/3] ethdev: add frequency adjustment API
  2024-09-23  3:11     ` fengchengwen
@ 2024-09-23  6:28       ` Ye, MingjinX
  0 siblings, 0 replies; 49+ messages in thread
From: Ye, MingjinX @ 2024-09-23  6:28 UTC (permalink / raw)
  To: fengchengwen, dev
  Cc: Su, Simei, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko

Hi Chengwen,

This API is used to synchronise the frequency of the slave node with the master node.

use-case: https://patchwork.dpdk.org/project/dpdk/patch/20240910091328.922082-4-mingjinx.ye@intel.com/

Also, I will refine the description file to avoid confusion.

Regards,
Mingjin

> -----Original Message-----
> From: fengchengwen <fengchengwen@huawei.com>
> Sent: Monday, September 23, 2024 11:12 AM
> To: Ye, MingjinX <mingjinx.ye@intel.com>; dev@dpdk.org
> Cc: Su, Simei <simei.su@intel.com>; Thomas Monjalon
> <thomas@monjalon.net>; Ferruh Yigit <ferruh.yigit@amd.com>; Andrew
> Rybchenko <andrew.rybchenko@oktetlabs.ru>
> Subject: Re: [PATCH v2 1/3] ethdev: add frequency adjustment API
> 
> Hi Mingjin,
> 
>   Why we need this API? What's the use-case?
> 
> Thanks
> 
> On 2024/9/10 17:13, Mingjin Ye wrote:
> > This patch adds freq adjustment API for PTP high accuracy.
> >
> > Signed-off-by: Simei Su <simei.su@intel.com>
> > Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
> > ---
> >  doc/guides/nics/features.rst           |  4 +++-
> >  doc/guides/rel_notes/release_24_11.rst | 30 +++-----------------------
> >  lib/ethdev/ethdev_driver.h             |  5 +++++
> >  lib/ethdev/ethdev_trace.h              |  9 ++++++++
> >  lib/ethdev/ethdev_trace_points.c       |  3 +++
> >  lib/ethdev/rte_ethdev.c                | 18 ++++++++++++++++
> >  lib/ethdev/rte_ethdev.h                | 20 +++++++++++++++++
> >  lib/ethdev/version.map                 |  3 +++
> >  8 files changed, 64 insertions(+), 28 deletions(-)
> >
> > diff --git a/doc/guides/nics/features.rst
> > b/doc/guides/nics/features.rst index cd0115ffb3..0508f118fe 100644
> > --- a/doc/guides/nics/features.rst
> > +++ b/doc/guides/nics/features.rst
> > @@ -677,10 +677,12 @@ Supports IEEE1588/802.1AS timestamping.
> >
> >  * **[implements] eth_dev_ops**: ``timesync_enable``,
> ``timesync_disable``
> >    ``timesync_read_rx_timestamp``, ``timesync_read_tx_timestamp``,
> > -  ``timesync_adjust_time``, ``timesync_read_time``,
> ``timesync_write_time``.
> > +  ``timesync_adjust_time``, ``timesync_adjust_freq``,
> > + ``timesync_read_time``, ``timesync_write_time``.
> >  * **[related]    API**: ``rte_eth_timesync_enable()``,
> ``rte_eth_timesync_disable()``,
> >    ``rte_eth_timesync_read_rx_timestamp()``,
> >    ``rte_eth_timesync_read_tx_timestamp``,
> > ``rte_eth_timesync_adjust_time()``,
> > +  ``rte_eth_timesync_adjust_freq()``,
> >    ``rte_eth_timesync_read_time()``, ``rte_eth_timesync_write_time()``.
> >
> >
> > diff --git a/doc/guides/rel_notes/release_24_11.rst
> > b/doc/guides/rel_notes/release_24_11.rst
> > index 0ff70d9057..10ac35e5c0 100644
> > --- a/doc/guides/rel_notes/release_24_11.rst
> > +++ b/doc/guides/rel_notes/release_24_11.rst
> > @@ -25,35 +25,11 @@ New Features
> >  ------------
> >
> >  .. This section should contain new features added in this release.
> > -   Sample format:
> >
> > -   * **Add a title in the past tense with a full stop.**
> > +* **Added Ethernet device clock incremental rate adjustment.**
> >
> > -     Add a short 1-2 sentence description in the past tense.
> > -     The description should be enough to allow someone scanning
> > -     the release notes to understand the new feature.
> > -
> > -     If the feature adds a lot of sub-features you can use a bullet list
> > -     like this:
> > -
> > -     * Added feature foo to do something.
> > -     * Enhanced feature bar to do something else.
> > -
> > -     Refer to the previous release notes for examples.
> > -
> > -     Suggested order in release notes items:
> > -     * Core libs (EAL, mempool, ring, mbuf, buses)
> > -     * Device abstraction libs and PMDs (ordered alphabetically by vendor
> name)
> > -       - ethdev (lib, PMDs)
> > -       - cryptodev (lib, PMDs)
> > -       - eventdev (lib, PMDs)
> > -       - etc
> > -     * Other libs
> > -     * Apps, Examples, Tools (if significant)
> > -
> > -     This section is a comment. Do not overwrite or remove it.
> > -     Also, make sure to start the actual text at the margin.
> > -
> =======================================================
> 
> The above should not delete in this patch, it will remove when release
> 
> > +  Added new function ``rte_eth_timesync_adjust_freq`` to  adjust the
> > + clock increment rate for Ethernet devices.
> >
> >
> >  Removed Items
> > diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
> > index 883e59a927..68cadc14a5 100644
> > --- a/lib/ethdev/ethdev_driver.h
> > +++ b/lib/ethdev/ethdev_driver.h
> > @@ -664,6 +664,9 @@ typedef int
> > (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
> >  /** @internal Function used to adjust the device clock. */  typedef
> > int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
> >
> > +/** @internal Function used to adjust the clock frequency. */ typedef
> > +int (*eth_timesync_adjust_freq)(struct rte_eth_dev *dev, int64_t);
> > +
> >  /** @internal Function used to get time from the device clock. */
> > typedef int (*eth_timesync_read_time)(struct rte_eth_dev *dev,
> >  				      struct timespec *timestamp); @@ -1378,6
> +1381,8 @@ struct
> > eth_dev_ops {
> >  	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
> >  	/** Adjust the device clock */
> >  	eth_timesync_adjust_time   timesync_adjust_time;
> > +	/** Adjust the clock frequency */
> > +	eth_timesync_adjust_freq   timesync_adjust_freq;
> >  	/** Get the device clock time */
> >  	eth_timesync_read_time     timesync_read_time;
> >  	/** Set the device clock time */
> > diff --git a/lib/ethdev/ethdev_trace.h b/lib/ethdev/ethdev_trace.h
> > index 3bec87bfdb..e273d5853e 100644
> > --- a/lib/ethdev/ethdev_trace.h
> > +++ b/lib/ethdev/ethdev_trace.h
> > @@ -2183,6 +2183,15 @@ RTE_TRACE_POINT_FP(
> >  	rte_trace_point_emit_int(ret);
> >  )
> >
> > +/* Called in loop in examples/ptpclient */ RTE_TRACE_POINT_FP(
> > +	rte_eth_trace_timesync_adjust_freq,
> > +	RTE_TRACE_POINT_ARGS(uint16_t port_id, int64_t ppm, int ret),
> > +	rte_trace_point_emit_u16(port_id);
> > +	rte_trace_point_emit_i64(ppm);
> > +	rte_trace_point_emit_int(ret);
> > +)
> > +
> >  /* Called in loop in app/test-flow-perf */  RTE_TRACE_POINT_FP(
> >  	rte_flow_trace_create,
> > diff --git a/lib/ethdev/ethdev_trace_points.c
> > b/lib/ethdev/ethdev_trace_points.c
> > index 99e04f5893..a99fec0c1e 100644
> > --- a/lib/ethdev/ethdev_trace_points.c
> > +++ b/lib/ethdev/ethdev_trace_points.c
> > @@ -409,6 +409,9 @@
> >
> RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_tx_timestamp,
> >  RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_time,
> >  	lib.ethdev.timesync_adjust_time)
> >
> > +RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_freq,
> > +	lib.ethdev.timesync_adjust_freq)
> > +
> >  RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_time,
> >  	lib.ethdev.timesync_read_time)
> >
> > diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c index
> > f1c658f49e..660eab2f1e 100644
> > --- a/lib/ethdev/rte_ethdev.c
> > +++ b/lib/ethdev/rte_ethdev.c
> > @@ -6310,6 +6310,24 @@ rte_eth_timesync_adjust_time(uint16_t port_id,
> int64_t delta)
> >  	return ret;
> >  }
> >
> > +int
> > +rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm) {
> > +	struct rte_eth_dev *dev;
> > +	int ret;
> > +
> > +	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
> > +	dev = &rte_eth_devices[port_id];
> > +
> > +	if (*dev->dev_ops->timesync_adjust_freq == NULL)
> > +		return -ENOTSUP;
> > +	ret = eth_err(port_id, (*dev->dev_ops->timesync_adjust_freq)(dev,
> > +ppm));
> > +
> > +	rte_eth_trace_timesync_adjust_freq(port_id, ppm, ret);
> > +
> > +	return ret;
> > +}
> > +
> >  int
> >  rte_eth_timesync_read_time(uint16_t port_id, struct timespec
> > *timestamp)  { diff --git a/lib/ethdev/rte_ethdev.h
> > b/lib/ethdev/rte_ethdev.h index 548fada1c7..c3587089b9 100644
> > --- a/lib/ethdev/rte_ethdev.h
> > +++ b/lib/ethdev/rte_ethdev.h
> > @@ -5297,6 +5297,26 @@ int
> rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
> >   */
> >  int rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta);
> >
> > +/**
> > + * Adjust the clock increment rate on an Ethernet device.
> > + *
> > + * This is usually used in conjunction with other Ethdev timesync
> > +functions to
> > + * synchronize the device time using the IEEE1588/802.1AS protocol.
> > + *
> > + * @param port_id
> > + *  The port identifier of the Ethernet device.
> > + * @param ppm
> > + *  Parts per million with 16-bit fractional field
> > + *
> > + * @return
> > + *   - 0: Success.
> > + *   - -ENODEV: The port ID is invalid.
> > + *   - -EIO: if device is removed.
> > + *   - -ENOTSUP: The function is not supported by the Ethernet driver.
> > + */
> > +__rte_experimental
> > +int rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm);
> > +
> >  /**
> >   * Read the time from the timesync clock on an Ethernet device.
> >   *
> > diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map index
> > 1669055ca5..199480ef07 100644
> > --- a/lib/ethdev/version.map
> > +++ b/lib/ethdev/version.map
> > @@ -325,6 +325,9 @@ EXPERIMENTAL {
> >  	rte_flow_template_table_resizable;
> >  	rte_flow_template_table_resize;
> >  	rte_flow_template_table_resize_complete;
> > +
> > +	# added in 24.11
> > +	rte_eth_timesync_adjust_freq;
> >  };
> >
> >  INTERNAL {


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 0/3] add frequency adjustment support for PTP
  2024-09-10  9:13 ` [PATCH v2 0/3] add frequency adjustment support for PTP Mingjin Ye
                     ` (3 preceding siblings ...)
  2024-09-22 19:06   ` [PATCH v2 0/3] add frequency adjustment support for PTP Ferruh Yigit
@ 2024-09-30  8:42   ` Mingjin Ye
  2024-09-30  8:42     ` [PATCH v3 1/3] ethdev: add frequency adjustment API Mingjin Ye
                       ` (4 more replies)
  4 siblings, 5 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-09-30  8:42 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye

[1/3] ethdev: add frequency adjustment API
[2/3] net/ice: add frequency adjustment support for PTP
[3/3] examples/ptpclient: add frequency adjustment support
---
v2: rte_eth_timesync_adjust_freq marked as experimental.
---
v3: Add more description for API.

Mingjin Ye (3):
  ethdev: add frequency adjustment API
  net/ice: add frequency adjustment support for PTP
  examples/ptpclient: add frequency adjustment support

 doc/guides/nics/features.rst           |   4 +-
 doc/guides/nics/ice.rst                |  15 ++
 doc/guides/rel_notes/release_24_11.rst |   5 +
 drivers/net/ice/ice_ethdev.c           | 177 +++++++++++----
 drivers/net/ice/ice_ethdev.h           |   2 +
 drivers/net/ice/ice_rxtx.c             |   4 +-
 examples/ptpclient/ptpclient.c         | 303 ++++++++++++++++++++++---
 lib/ethdev/ethdev_driver.h             |   5 +
 lib/ethdev/ethdev_trace.h              |   9 +
 lib/ethdev/ethdev_trace_points.c       |   3 +
 lib/ethdev/rte_ethdev.c                |  18 ++
 lib/ethdev/rte_ethdev.h                |  43 ++++
 lib/ethdev/version.map                 |   3 +
 13 files changed, 510 insertions(+), 81 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 1/3] ethdev: add frequency adjustment API
  2024-09-30  8:42   ` [PATCH v3 " Mingjin Ye
@ 2024-09-30  8:42     ` Mingjin Ye
  2024-09-30 20:51       ` Ferruh Yigit
  2024-09-30  8:42     ` [PATCH v3 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
                       ` (3 subsequent siblings)
  4 siblings, 1 reply; 49+ messages in thread
From: Mingjin Ye @ 2024-09-30  8:42 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko

This patch adds freq adjustment API for PTP high accuracy.

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
v2: rte_eth_timesync_adjust_freq marked as experimental
---
v3: Add more description for API.
---
 doc/guides/nics/features.rst           |  4 ++-
 doc/guides/rel_notes/release_24_11.rst |  5 +++
 lib/ethdev/ethdev_driver.h             |  5 +++
 lib/ethdev/ethdev_trace.h              |  9 ++++++
 lib/ethdev/ethdev_trace_points.c       |  3 ++
 lib/ethdev/rte_ethdev.c                | 18 +++++++++++
 lib/ethdev/rte_ethdev.h                | 43 ++++++++++++++++++++++++++
 lib/ethdev/version.map                 |  3 ++
 8 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/doc/guides/nics/features.rst b/doc/guides/nics/features.rst
index cd0115ffb3..0508f118fe 100644
--- a/doc/guides/nics/features.rst
+++ b/doc/guides/nics/features.rst
@@ -677,10 +677,12 @@ Supports IEEE1588/802.1AS timestamping.
 
 * **[implements] eth_dev_ops**: ``timesync_enable``, ``timesync_disable``
   ``timesync_read_rx_timestamp``, ``timesync_read_tx_timestamp``,
-  ``timesync_adjust_time``, ``timesync_read_time``, ``timesync_write_time``.
+  ``timesync_adjust_time``, ``timesync_adjust_freq``,
+  ``timesync_read_time``, ``timesync_write_time``.
 * **[related]    API**: ``rte_eth_timesync_enable()``, ``rte_eth_timesync_disable()``,
   ``rte_eth_timesync_read_rx_timestamp()``,
   ``rte_eth_timesync_read_tx_timestamp``, ``rte_eth_timesync_adjust_time()``,
+  ``rte_eth_timesync_adjust_freq()``,
   ``rte_eth_timesync_read_time()``, ``rte_eth_timesync_write_time()``.
 
 
diff --git a/doc/guides/rel_notes/release_24_11.rst b/doc/guides/rel_notes/release_24_11.rst
index 0ff70d9057..945211705b 100644
--- a/doc/guides/rel_notes/release_24_11.rst
+++ b/doc/guides/rel_notes/release_24_11.rst
@@ -55,6 +55,11 @@ New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* **Added Ethernet device clock frequency adjustment.**
+
+  Added new function ``rte_eth_timesync_adjust_freq`` to
+  adjust the clock frequency for Ethernet devices.
+
 
 Removed Items
 -------------
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 883e59a927..68cadc14a5 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -664,6 +664,9 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 /** @internal Function used to adjust the device clock. */
 typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
 
+/** @internal Function used to adjust the clock frequency. */
+typedef int (*eth_timesync_adjust_freq)(struct rte_eth_dev *dev, int64_t);
+
 /** @internal Function used to get time from the device clock. */
 typedef int (*eth_timesync_read_time)(struct rte_eth_dev *dev,
 				      struct timespec *timestamp);
@@ -1378,6 +1381,8 @@ struct eth_dev_ops {
 	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
 	/** Adjust the device clock */
 	eth_timesync_adjust_time   timesync_adjust_time;
+	/** Adjust the clock frequency */
+	eth_timesync_adjust_freq   timesync_adjust_freq;
 	/** Get the device clock time */
 	eth_timesync_read_time     timesync_read_time;
 	/** Set the device clock time */
diff --git a/lib/ethdev/ethdev_trace.h b/lib/ethdev/ethdev_trace.h
index 3bec87bfdb..e273d5853e 100644
--- a/lib/ethdev/ethdev_trace.h
+++ b/lib/ethdev/ethdev_trace.h
@@ -2183,6 +2183,15 @@ RTE_TRACE_POINT_FP(
 	rte_trace_point_emit_int(ret);
 )
 
+/* Called in loop in examples/ptpclient */
+RTE_TRACE_POINT_FP(
+	rte_eth_trace_timesync_adjust_freq,
+	RTE_TRACE_POINT_ARGS(uint16_t port_id, int64_t ppm, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_i64(ppm);
+	rte_trace_point_emit_int(ret);
+)
+
 /* Called in loop in app/test-flow-perf */
 RTE_TRACE_POINT_FP(
 	rte_flow_trace_create,
diff --git a/lib/ethdev/ethdev_trace_points.c b/lib/ethdev/ethdev_trace_points.c
index 99e04f5893..a99fec0c1e 100644
--- a/lib/ethdev/ethdev_trace_points.c
+++ b/lib/ethdev/ethdev_trace_points.c
@@ -409,6 +409,9 @@ RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_tx_timestamp,
 RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_time,
 	lib.ethdev.timesync_adjust_time)
 
+RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_freq,
+	lib.ethdev.timesync_adjust_freq)
+
 RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_time,
 	lib.ethdev.timesync_read_time)
 
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index f1c658f49e..660eab2f1e 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -6310,6 +6310,24 @@ rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta)
 	return ret;
 }
 
+int
+rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (*dev->dev_ops->timesync_adjust_freq == NULL)
+		return -ENOTSUP;
+	ret = eth_err(port_id, (*dev->dev_ops->timesync_adjust_freq)(dev, ppm));
+
+	rte_eth_trace_timesync_adjust_freq(port_id, ppm, ret);
+
+	return ret;
+}
+
 int
 rte_eth_timesync_read_time(uint16_t port_id, struct timespec *timestamp)
 {
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index 548fada1c7..f88c7300da 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -5297,6 +5297,49 @@ int rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
  */
 int rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta);
 
+/**
+ * Adjust the clock frequency on an Ethernet device.
+ *
+ * Adjusts the base frequency by a specified percentage of ppm (parts per
+ * million). This is usually used in conjunction with other Ethdev timesync
+ * functions to synchronize the device time using the IEEE1588/802.1AS
+ * protocol.
+ *
+ * The clock is subject to frequency deviation and rate of change drift due to
+ * the environment. The upper layer APP calculates the frequency compensation
+ * value of the slave clock relative to the master clock via a servo algorithm
+ * and adjusts the device clock frequency via "rte_eth_timesync_adjust_freq()".
+ * Commonly used servo algorithms are pi/linreg/ntpshm, for implementation
+ * see: https://github.com/nxp-archive/openil_linuxptp.git.
+ *
+ * The adjustment value obtained by the servo algorithm is usually in
+ * ppb (parts per billion). For consistency with the kernel driver .adjfine,
+ * the tuning values are in ppm. Note that 1 ppb is approximately 65.536 scaled
+ * ppm, see Linux kernel upstream commit 1060707e3809 (‘ptp: introduce helpers
+ * to adjust by scaled parts per million’).
+ *
+ * In addition, the device reference frequency is usually also the stepping
+ * threshold for the servo algorithm, and the frequency up and down adjustment
+ * range is limited by the device. The device clock frequency should be
+ * adjusted with "rte_eth_timesync_adjust_freq()" every time the clock is
+ * synchronised. Also use ‘rte_eth_timesync_adjust_time()’ to update the device
+ * clock only if the absolute value of the master/slave clock offset is greater than
+ * or equal to the step threshold.
+ *
+ * @param port_id
+ *  The port identifier of the Ethernet device.
+ * @param ppm
+ *  Parts per million with 16-bit fractional field
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+__rte_experimental
+int rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm);
+
 /**
  * Read the time from the timesync clock on an Ethernet device.
  *
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 1669055ca5..199480ef07 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -325,6 +325,9 @@ EXPERIMENTAL {
 	rte_flow_template_table_resizable;
 	rte_flow_template_table_resize;
 	rte_flow_template_table_resize_complete;
+
+	# added in 24.11
+	rte_eth_timesync_adjust_freq;
 };
 
 INTERNAL {
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 2/3] net/ice: add frequency adjustment support for PTP
  2024-09-30  8:42   ` [PATCH v3 " Mingjin Ye
  2024-09-30  8:42     ` [PATCH v3 1/3] ethdev: add frequency adjustment API Mingjin Ye
@ 2024-09-30  8:42     ` Mingjin Ye
  2024-09-30  8:42     ` [PATCH v3 3/3] examples/ptpclient: add frequency adjustment support Mingjin Ye
                       ` (2 subsequent siblings)
  4 siblings, 0 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-09-30  8:42 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su

Add ice support for new ethdev API to adjust frequency for IEEE1588
PTP. Also, this patch reworks code for converting software update
to hardware update.

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
 doc/guides/nics/ice.rst      |  15 +++
 drivers/net/ice/ice_ethdev.c | 177 ++++++++++++++++++++++++++---------
 drivers/net/ice/ice_ethdev.h |   2 +
 drivers/net/ice/ice_rxtx.c   |   4 +-
 4 files changed, 153 insertions(+), 45 deletions(-)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index ae975d19ad..dec4cc2434 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -328,6 +328,21 @@ Forward Error Correction (FEC)
 
 Supports get/set FEC mode and get FEC capability.
 
+Time Synchronisation
+~~~~~~~~~~~~~~~~~~~~
+
+The system operator can run a PTP (Precision Time Protocol) client application
+to synchronise the time on the network card (and optionally the time on the
+system) to the PTP master.
+
+ICE PMD supports PTP client applications that use the DPDK IEEE1588 API to
+communicate with the PTP master clock. Note that the PTP client application
+needs to run on the PF and vector mode needs to be disabled.
+
+.. code-block:: console
+
+    examples/dpdk-ptpclient -c f -n 3 -a 0000:ec:00.1 -- -T 1 -p 0x1 --controller=pi
+
 Generic Flow Support
 ~~~~~~~~~~~~~~~~~~~~
 
diff --git a/drivers/net/ice/ice_ethdev.c b/drivers/net/ice/ice_ethdev.c
index 304f959b7e..33293a917f 100644
--- a/drivers/net/ice/ice_ethdev.c
+++ b/drivers/net/ice/ice_ethdev.c
@@ -10,6 +10,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <math.h>
 
 #include <rte_tailq.h>
 #include <rte_os_shim.h>
@@ -176,6 +177,7 @@ static int ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 static int ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					  struct timespec *timestamp);
 static int ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
+static int ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm);
 static int ice_timesync_read_time(struct rte_eth_dev *dev,
 				  struct timespec *timestamp);
 static int ice_timesync_write_time(struct rte_eth_dev *dev,
@@ -307,6 +309,7 @@ static const struct eth_dev_ops ice_eth_dev_ops = {
 	.timesync_read_rx_timestamp   = ice_timesync_read_rx_timestamp,
 	.timesync_read_tx_timestamp   = ice_timesync_read_tx_timestamp,
 	.timesync_adjust_time         = ice_timesync_adjust_time,
+	.timesync_adjust_freq         = ice_timesync_adjust_freq,
 	.timesync_read_time           = ice_timesync_read_time,
 	.timesync_write_time          = ice_timesync_write_time,
 	.timesync_disable             = ice_timesync_disable,
@@ -2332,6 +2335,34 @@ ice_get_supported_rxdid(struct ice_hw *hw)
 	return supported_rxdid;
 }
 
+static void ice_ptp_init_info(struct rte_eth_dev *dev)
+{
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ice_adapter *ad =
+		ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+
+	switch (hw->phy_model) {
+	case ICE_PHY_ETH56G:
+		ad->ptp_tx_block = hw->pf_id;
+		ad->ptp_tx_index = 0;
+		break;
+	case ICE_PHY_E810:
+	/* fallthrough */
+	case ICE_PHY_E830:
+		ad->ptp_tx_block = hw->port_info->lport;
+		ad->ptp_tx_index = 0;
+		break;
+	case ICE_PHY_E822:
+		ad->ptp_tx_block = hw->pf_id / ICE_PORTS_PER_QUAD;
+		ad->ptp_tx_index = (hw->pf_id % ICE_PORTS_PER_QUAD) *
+				ICE_PORTS_PER_PHY_E822 * ICE_QUADS_PER_PHY_E822;
+		break;
+	default:
+		PMD_DRV_LOG(WARNING, "Unsupported PHY model");
+		break;
+	}
+}
+
 static int
 ice_dev_init(struct rte_eth_dev *dev)
 {
@@ -2499,6 +2530,9 @@ ice_dev_init(struct rte_eth_dev *dev)
 	/* Initialize PHY model */
 	ice_ptp_init_phy_model(hw);
 
+	/* Initialize PTP info */
+	ice_ptp_init_info(dev);
+
 	if (hw->phy_model == ICE_PHY_E822) {
 		ret = ice_start_phy_timer_e822(hw, hw->pf_id);
 		if (ret)
@@ -6466,23 +6500,6 @@ ice_timesync_enable(struct rte_eth_dev *dev)
 		}
 	}
 
-	/* Initialize cycle counters for system time/RX/TX timestamp */
-	memset(&ad->systime_tc, 0, sizeof(struct rte_timecounter));
-	memset(&ad->rx_tstamp_tc, 0, sizeof(struct rte_timecounter));
-	memset(&ad->tx_tstamp_tc, 0, sizeof(struct rte_timecounter));
-
-	ad->systime_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->systime_tc.cc_shift = 0;
-	ad->systime_tc.nsec_mask = 0;
-
-	ad->rx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->rx_tstamp_tc.cc_shift = 0;
-	ad->rx_tstamp_tc.nsec_mask = 0;
-
-	ad->tx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->tx_tstamp_tc.cc_shift = 0;
-	ad->tx_tstamp_tc.nsec_mask = 0;
-
 	ad->ptp_ena = 1;
 
 	return 0;
@@ -6497,14 +6514,13 @@ ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	struct ice_rx_queue *rxq;
 	uint32_t ts_high;
-	uint64_t ts_ns, ns;
+	uint64_t ts_ns;
 
 	rxq = dev->data->rx_queues[flags];
 
 	ts_high = rxq->time_high;
 	ts_ns = ice_tstamp_convert_32b_64b(hw, ad, 1, ts_high);
-	ns = rte_timecounter_update(&ad->rx_tstamp_tc, ts_ns);
-	*timestamp = rte_ns_to_timespec(ns);
+	*timestamp = rte_ns_to_timespec(ts_ns);
 
 	return 0;
 }
@@ -6516,22 +6532,18 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	struct ice_adapter *ad =
 			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-	uint8_t lport;
-	uint64_t ts_ns, ns, tstamp;
+	uint64_t ts_ns, tstamp;
 	const uint64_t mask = 0xFFFFFFFF;
 	int ret;
 
-	lport = hw->port_info->lport;
-
-	ret = ice_read_phy_tstamp(hw, lport, 0, &tstamp);
-	if (ret) {
+	ret = ice_read_phy_tstamp(hw, ad->ptp_tx_block, ad->ptp_tx_index, &tstamp);
+	if (ret || tstamp == 0) {
 		PMD_DRV_LOG(ERR, "Failed to read phy timestamp");
 		return -1;
 	}
 
 	ts_ns = ice_tstamp_convert_32b_64b(hw, ad, 1, (tstamp >> 8) & mask);
-	ns = rte_timecounter_update(&ad->tx_tstamp_tc, ts_ns);
-	*timestamp = rte_ns_to_timespec(ns);
+	*timestamp = rte_ns_to_timespec(ts_ns);
 
 	return 0;
 }
@@ -6539,28 +6551,108 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 static int
 ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
 {
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint8_t tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc;
+	uint32_t lo, lo2, hi;
+	uint64_t time, ns;
+	int ret;
+
+	if (delta > INT32_MAX || delta < INT32_MIN) {
+		lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+		hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
+		lo2 = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+
+		if (lo2 < lo) {
+			lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+			hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
+		}
+
+		time = ((uint64_t)hi << 32) | lo;
+		ns = time + delta;
+
+		wr32(hw, GLTSYN_SHTIME_L(tmr_idx), ICE_LO_DWORD(ns));
+		wr32(hw, GLTSYN_SHTIME_H(tmr_idx), ICE_HI_DWORD(ns));
+		wr32(hw, GLTSYN_SHTIME_0(tmr_idx), 0);
+
+		ret = ice_ptp_init_time(hw, ns, true);
+		if (ret) {
+			PMD_DRV_LOG(ERR, "PTP init time failed, err %d", ret);
+			return -1;
+		}
+		return 0;
+	}
+
+	ret = ice_ptp_adj_clock(hw, delta, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP adj clock failed, err %d", ret);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	int64_t incval, diff = 0;
+	bool negative = false;
+	uint64_t div, rem;
+	uint64_t divisor = 1000000ULL << 16;
+	int shift;
+	int ret;
+
+	incval = ice_get_base_incval(hw, ICE_SRC_TMR_MODE_NANOSECONDS);
+
+	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;
 
-	ad->systime_tc.nsec += delta;
-	ad->rx_tstamp_tc.nsec += delta;
-	ad->tx_tstamp_tc.nsec += delta;
+		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 = ice_ptp_write_incval_locked(hw, incval, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+		return -1;
+	}
 	return 0;
 }
 
 static int
 ice_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
 {
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint64_t ns;
+	int ret;
 
 	ns = rte_timespec_to_ns(ts);
-
-	ad->systime_tc.nsec = ns;
-	ad->rx_tstamp_tc.nsec = ns;
-	ad->tx_tstamp_tc.nsec = ns;
+	ret = ice_ptp_init_time(hw, ns, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP init time failed, err %d", ret);
+		return -1;
+	}
 
 	return 0;
 }
@@ -6569,11 +6661,9 @@ static int
 ice_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
 {
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	uint8_t tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc;
 	uint32_t hi, lo, lo2;
-	uint64_t time, ns;
+	uint64_t time;
 
 	lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
 	hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
@@ -6585,8 +6675,7 @@ ice_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
 	}
 
 	time = ((uint64_t)hi << 32) | lo;
-	ns = rte_timecounter_update(&ad->systime_tc, time);
-	*ts = rte_ns_to_timespec(ns);
+	*ts = rte_ns_to_timespec(time);
 
 	return 0;
 }
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index 3ea9f37dc8..db192e76fe 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -614,6 +614,8 @@ struct ice_adapter {
 	struct rte_timecounter systime_tc;
 	struct rte_timecounter rx_tstamp_tc;
 	struct rte_timecounter tx_tstamp_tc;
+	uint8_t ptp_tx_block;
+	uint8_t ptp_tx_index;
 	bool ptp_ena;
 	uint64_t time_hw;
 	struct ice_fdir_prof_info fdir_prof_info[ICE_MAX_PTGS];
diff --git a/drivers/net/ice/ice_rxtx.c b/drivers/net/ice/ice_rxtx.c
index f270498ed1..5f399e8aae 100644
--- a/drivers/net/ice/ice_rxtx.c
+++ b/drivers/net/ice/ice_rxtx.c
@@ -3066,7 +3066,9 @@ ice_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
 			else if (ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST)
 				cd_type_cmd_tso_mss |=
 					((uint64_t)ICE_TX_CTX_DESC_TSYN <<
-					ICE_TXD_CTX_QW1_CMD_S);
+					ICE_TXD_CTX_QW1_CMD_S) |
+					 (((uint64_t)txq->vsi->adapter->ptp_tx_index <<
+					 ICE_TXD_CTX_QW1_TSYN_S) & ICE_TXD_CTX_QW1_TSYN_M);
 
 			ctx_txd->tunneling_params =
 				rte_cpu_to_le_32(cd_tunneling_params);
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 3/3] examples/ptpclient: add frequency adjustment support
  2024-09-30  8:42   ` [PATCH v3 " Mingjin Ye
  2024-09-30  8:42     ` [PATCH v3 1/3] ethdev: add frequency adjustment API Mingjin Ye
  2024-09-30  8:42     ` [PATCH v3 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
@ 2024-09-30  8:42     ` Mingjin Ye
  2024-09-30 20:51       ` Ferruh Yigit
  2024-09-30 20:53     ` [PATCH v3 0/3] add frequency adjustment support for PTP Ferruh Yigit
  2024-10-10  9:32     ` [PATCH v4 " Mingjin Ye
  4 siblings, 1 reply; 49+ messages in thread
From: Mingjin Ye @ 2024-09-30  8:42 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su, Wenjun Wu, Kirill Rybalchenko

This patch adds PI servo algorithm to support frequency
adjustment API for IEEE1588 PTP.

For example, the command for starting ptpclient with PI algorithm is:
./build/examples/dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1
--controller=pi

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
 examples/ptpclient/ptpclient.c | 303 +++++++++++++++++++++++++++++----
 1 file changed, 268 insertions(+), 35 deletions(-)

diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
index afb61bba51..9b1ab52700 100644
--- a/examples/ptpclient/ptpclient.c
+++ b/examples/ptpclient/ptpclient.c
@@ -46,6 +46,35 @@ static volatile bool force_quit;
 #define KERNEL_TIME_ADJUST_LIMIT  20000
 #define PTP_PROTOCOL             0x88F7
 
+#define KP 0.7
+#define KI 0.3
+#define FREQ_EST_MARGIN 0.001
+
+enum servo_state {
+	SERVO_UNLOCKED,
+	SERVO_JUMP,
+	SERVO_LOCKED,
+};
+
+struct pi_servo {
+	double offset[2];
+	double local[2];
+	double drift;
+	double last_freq;
+	int count;
+
+	double max_frequency;
+	double step_threshold;
+	double first_step_threshold;
+	int first_update;
+};
+
+enum controller_mode {
+	MODE_NONE,
+	MODE_PI,
+	MAX_ALL
+} mode = MODE_NONE;
+
 struct rte_mempool *mbuf_pool;
 uint32_t ptp_enabled_port_mask;
 uint8_t ptp_enabled_port_nb;
@@ -135,6 +164,9 @@ struct ptpv2_data_slave_ordinary {
 	uint8_t ptpset;
 	uint8_t kernel_time_set;
 	uint16_t current_ptp_port;
+	int64_t master_offset;
+	int64_t path_delay;
+	struct pi_servo *servo;
 };
 
 static struct ptpv2_data_slave_ordinary ptp_data;
@@ -293,36 +325,44 @@ print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
 			ptp_data->tstamp3.tv_sec,
 			(ptp_data->tstamp3.tv_nsec));
 
-	printf("\nT4 - Master Clock.  %lds %ldns ",
+	printf("\nT4 - Master Clock.  %lds %ldns\n",
 			ptp_data->tstamp4.tv_sec,
 			(ptp_data->tstamp4.tv_nsec));
 
-	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+	if (mode == MODE_NONE) {
+		printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
 			ptp_data->delta);
 
-	clock_gettime(CLOCK_REALTIME, &sys_time);
-	rte_eth_timesync_read_time(ptp_data->current_ptp_port, &net_time);
+		clock_gettime(CLOCK_REALTIME, &sys_time);
+		rte_eth_timesync_read_time(ptp_data->current_ptp_port,
+					   &net_time);
 
-	time_t ts = net_time.tv_sec;
+		time_t ts = net_time.tv_sec;
 
-	printf("\n\nComparison between Linux kernel Time and PTP:");
+		printf("\n\nComparison between Linux kernel Time and PTP:");
 
-	printf("\nCurrent PTP Time: %.24s %.9ld ns",
+		printf("\nCurrent PTP Time: %.24s %.9ld ns",
 			ctime(&ts), net_time.tv_nsec);
 
-	nsec = (int64_t)timespec64_to_ns(&net_time) -
+		nsec = (int64_t)timespec64_to_ns(&net_time) -
 			(int64_t)timespec64_to_ns(&sys_time);
-	ptp_data->new_adj = ns_to_timeval(nsec);
+		ptp_data->new_adj = ns_to_timeval(nsec);
 
-	gettimeofday(&ptp_data->new_adj, NULL);
+		gettimeofday(&ptp_data->new_adj, NULL);
 
-	time_t tp = ptp_data->new_adj.tv_sec;
+		time_t tp = ptp_data->new_adj.tv_sec;
 
-	printf("\nCurrent SYS Time: %.24s %.6ld ns",
-				ctime(&tp), ptp_data->new_adj.tv_usec);
+		printf("\nCurrent SYS Time: %.24s %.6ld ns",
+			ctime(&tp), ptp_data->new_adj.tv_usec);
 
-	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
-				nsec);
+		printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
+			nsec);
+	}
+
+	if (mode == MODE_PI) {
+		printf("path delay: %"PRId64"ns\n", ptp_data->path_delay);
+		printf("master offset: %"PRId64"ns\n", ptp_data->master_offset);
+	}
 
 	printf("[Ctrl+C to quit]\n");
 
@@ -385,21 +425,11 @@ parse_sync(struct ptpv2_data_slave_ordinary *ptp_data, uint16_t rx_tstamp_idx)
 static void
 parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
 {
-	struct rte_ether_hdr *eth_hdr;
-	struct rte_ether_addr eth_addr;
 	struct ptp_header *ptp_hdr;
-	struct clock_id *client_clkid;
 	struct ptp_message *ptp_msg;
-	struct delay_req_msg *req_msg;
-	struct rte_mbuf *created_pkt;
 	struct tstamp *origin_tstamp;
-	struct rte_ether_addr eth_multicast = ether_multicast;
-	size_t pkt_size;
-	int wait_us;
 	struct rte_mbuf *m = ptp_data->m;
-	int ret;
 
-	eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
 	ptp_hdr = rte_pktmbuf_mtod_offset(m, struct ptp_header *,
 					  sizeof(struct rte_ether_hdr));
 	if (memcmp(&ptp_data->master_clock_id,
@@ -416,6 +446,150 @@ parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
 	ptp_data->tstamp1.tv_sec =
 		((uint64_t)ntohl(origin_tstamp->sec_lsb)) |
 		(((uint64_t)ntohs(origin_tstamp->sec_msb)) << 32);
+}
+
+static double
+pi_sample(struct pi_servo *s, int64_t offset, double local_ts,
+	  enum servo_state *state)
+{
+	double ki_term, ppb = s->last_freq;
+	double freq_est_interval, localdiff;
+
+	switch (s->count) {
+	case 0:
+		s->offset[0] = offset;
+		s->local[0] = local_ts;
+		*state = SERVO_UNLOCKED;
+		s->count = 1;
+		break;
+	case 1:
+		s->offset[1] = offset;
+		s->local[1] = local_ts;
+
+		/* Make sure the first sample is older than the second. */
+		if (s->local[0] >= s->local[1]) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		/* Wait long enough before estimating the frequency offset. */
+		localdiff = (s->local[1] - s->local[0]) / 1e9;
+		localdiff += localdiff * FREQ_EST_MARGIN;
+		freq_est_interval = 0.016 / KI;
+		if (freq_est_interval > 1000.0)
+			freq_est_interval = 1000.0;
+
+		if (localdiff < freq_est_interval) {
+			*state = SERVO_UNLOCKED;
+			break;
+		}
+
+		/* Adjust drift by the measured frequency offset. */
+		s->drift += (1e9 - s->drift) * (s->offset[1] - s->offset[0]) /
+						(s->local[1] - s->local[0]);
+
+		if (s->drift < -s->max_frequency)
+			s->drift = -s->max_frequency;
+		else if (s->drift > s->max_frequency)
+			s->drift = s->max_frequency;
+
+		if ((s->first_update &&
+		     s->first_step_threshold &&
+		     s->first_step_threshold < llabs(offset)) ||
+		    (s->step_threshold &&
+		     s->step_threshold < llabs(offset)))
+			*state = SERVO_JUMP;
+		else
+			*state = SERVO_LOCKED;
+
+		ppb = s->drift;
+		s->count = 2;
+		break;
+	case 2:
+		/*
+		 * reset the clock servo when offset is greater than the max
+		 * offset value. Note that the clock jump will be performed in
+		 * step 1, so it is not necessary to have clock jump
+		 * immediately. This allows re-calculating drift as in initial
+		 * clock startup.
+		 */
+		if (s->step_threshold &&
+		    s->step_threshold < llabs(offset)) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		ki_term = KI * offset;
+		ppb = KP * offset + s->drift + ki_term;
+		if (ppb < -s->max_frequency)
+			ppb = -s->max_frequency;
+		else if (ppb > s->max_frequency)
+			ppb = s->max_frequency;
+		else
+			s->drift += ki_term;
+
+		*state = SERVO_LOCKED;
+		break;
+	}
+
+	s->last_freq = ppb;
+	return ppb;
+}
+
+static void
+ptp_adjust_freq(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns;
+	double adj_freq;
+	enum servo_state state = SERVO_UNLOCKED;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	ptp_data->master_offset = t2_ns - t1_ns - ptp_data->path_delay;
+	if (!ptp_data->path_delay)
+		return;
+
+	adj_freq = pi_sample(ptp_data->servo, ptp_data->master_offset, t2_ns,
+		     &state);
+
+	switch (state) {
+	case SERVO_UNLOCKED:
+		break;
+	case SERVO_JUMP:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+						-(long)(adj_freq * 65.536));
+		rte_eth_timesync_adjust_time(ptp_data->portid,
+					     -ptp_data->master_offset);
+		break;
+	case SERVO_LOCKED:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+					     -(long)(adj_freq * 65.536));
+		break;
+	}
+}
+
+static void
+send_delay_request(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	struct rte_ether_hdr *eth_hdr;
+	struct rte_ether_addr eth_addr;
+	struct ptp_header *ptp_hdr;
+	struct clock_id *client_clkid;
+	struct delay_req_msg *req_msg;
+	struct rte_mbuf *created_pkt;
+	struct rte_ether_addr eth_multicast = ether_multicast;
+	size_t pkt_size;
+	int wait_us;
+	struct rte_mbuf *m = ptp_data->m;
+	int ret;
+
+	eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
+	ptp_hdr = (struct ptp_header *)(rte_pktmbuf_mtod(m, char *)
+			+ sizeof(struct rte_ether_hdr));
 
 	if (ptp_data->seqID_FOLLOWUP == ptp_data->seqID_SYNC) {
 		ret = rte_eth_macaddr_get(ptp_data->portid, &eth_addr);
@@ -488,11 +662,10 @@ parse_fup(struct ptpv2_data_slave_ordinary *ptp_data)
 		ptp_data->tstamp3.tv_sec = 0;
 
 		/* Wait at least 1 us to read TX timestamp. */
-		while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
-				&ptp_data->tstamp3) < 0) && (wait_us < 1000)) {
+		do {
 			rte_delay_us(1);
-			wait_us++;
-		}
+		} while ((rte_eth_timesync_read_tx_timestamp(ptp_data->portid,
+				&ptp_data->tstamp3) < 0) && (wait_us++ < 10));
 	}
 }
 
@@ -529,6 +702,25 @@ update_kernel_time(void)
 
 }
 
+static void
+clock_path_delay(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns, t3_ns, t4_ns;
+	int64_t pd, diff;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	t3_ns = timespec64_to_ns(&ptp_data->tstamp3);
+	t4_ns = timespec64_to_ns(&ptp_data->tstamp4);
+
+	pd = (t2_ns - t3_ns) + (t4_ns - t1_ns);
+	diff = t3_ns - t2_ns;
+	if (diff <= INT32_MAX && diff >= INT32_MIN)
+		ptp_data->path_delay = pd / 2;
+	else
+		ptp_data->path_delay = 0;
+}
+
 /*
  * Parse the DELAY_RESP message.
  */
@@ -541,7 +733,7 @@ parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
 	uint16_t seq_id;
 
 	ptp_msg = rte_pktmbuf_mtod_offset(m, struct ptp_message *,
-					  sizeof(struct rte_ether_hdr));
+					sizeof(struct rte_ether_hdr));
 	seq_id = rte_be_to_cpu_16(ptp_msg->delay_resp.hdr.seq_id);
 	if (memcmp(&ptp_data->client_clock_id,
 		   &ptp_msg->delay_resp.req_port_id.clock_id,
@@ -553,11 +745,15 @@ parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
 				((uint64_t)ntohl(rx_tstamp->sec_lsb)) |
 				(((uint64_t)ntohs(rx_tstamp->sec_msb)) << 32);
 
-			/* Evaluate the delta for adjustment. */
-			ptp_data->delta = delta_eval(ptp_data);
+			if (mode == MODE_PI) {
+				clock_path_delay(ptp_data);
+			} else {
+				/* Evaluate the delta for adjustment. */
+				ptp_data->delta = delta_eval(ptp_data);
 
-			rte_eth_timesync_adjust_time(ptp_data->portid,
-						     ptp_data->delta);
+				rte_eth_timesync_adjust_time(ptp_data->portid,
+								ptp_data->delta);
+			}
 
 			ptp_data->current_ptp_port = ptp_data->portid;
 
@@ -597,6 +793,9 @@ parse_ptp_frames(uint16_t portid, struct rte_mbuf *m) {
 			break;
 		case FOLLOW_UP:
 			parse_fup(&ptp_data);
+			if (mode == MODE_PI)
+				ptp_adjust_freq(&ptp_data);
+			send_delay_request(&ptp_data);
 			break;
 		case DELAY_RESP:
 			parse_drsp(&ptp_data);
@@ -688,6 +887,21 @@ parse_ptp_kernel(const char *param)
 	return 1;
 }
 
+static void
+servo_init(struct pi_servo *servo)
+{
+	memset(servo, 0x00, sizeof(*servo));
+
+	servo->drift = 100000000;
+	servo->last_freq = 100000000;
+	servo->count = 0;
+
+	servo->max_frequency = 100000000;
+	servo->step_threshold = 0.1 * NSEC_PER_SEC;
+	servo->first_step_threshold = 0.00002 * NSEC_PER_SEC;
+	servo->first_update = 1;
+}
+
 /* Parse the commandline arguments. */
 static int
 ptp_parse_args(int argc, char **argv)
@@ -696,7 +910,10 @@ ptp_parse_args(int argc, char **argv)
 	char **argvopt;
 	int option_index;
 	char *prgname = argv[0];
-	static struct option lgopts[] = { {NULL, 0, 0, 0} };
+	static struct option lgopts[] = {
+		{"controller", 1, 0, 0},
+		{NULL, 0, 0, 0}
+	};
 
 	argvopt = argv;
 
@@ -724,6 +941,11 @@ ptp_parse_args(int argc, char **argv)
 
 			ptp_data.kernel_time_set = ret;
 			break;
+		case 0:
+			if (!strcmp(lgopts[option_index].name, "controller"))
+				if (!strcmp(optarg, "pi"))
+					mode = MODE_PI;
+			break;
 
 		default:
 			print_usage(prgname);
@@ -778,6 +1000,14 @@ main(int argc, char *argv[])
 		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
 	/* >8 End of parsing specific arguments. */
 
+	if (mode == MODE_PI) {
+		ptp_data.servo = malloc(sizeof(*(ptp_data.servo)));
+		if (!ptp_data.servo)
+			rte_exit(EXIT_FAILURE, "no memory for servo\n");
+
+		servo_init(ptp_data.servo);
+	}
+
 	/* Check that there is an even number of ports to send/receive on. */
 	nb_ports = rte_eth_dev_count_avail();
 
@@ -831,6 +1061,9 @@ main(int argc, char *argv[])
 		rte_eth_dev_close(portid);
 	}
 
+	if (mode == MODE_PI)
+		free(ptp_data.servo);
+
 	/* clean up the EAL */
 	rte_eal_cleanup();
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 3/3] examples/ptpclient: add frequency adjustment support
  2024-09-30  8:42     ` [PATCH v3 3/3] examples/ptpclient: add frequency adjustment support Mingjin Ye
@ 2024-09-30 20:51       ` Ferruh Yigit
  0 siblings, 0 replies; 49+ messages in thread
From: Ferruh Yigit @ 2024-09-30 20:51 UTC (permalink / raw)
  To: Mingjin Ye, dev; +Cc: Simei Su, Wenjun Wu, Kirill Rybalchenko

On 9/30/2024 9:42 AM, Mingjin Ye wrote:
> This patch adds PI servo algorithm to support frequency
> adjustment API for IEEE1588 PTP.
> 
> For example, the command for starting ptpclient with PI algorithm is:
> ./build/examples/dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1
> --controller=pi
> 
> Signed-off-by: Simei Su <simei.su@intel.com>
> Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
> ---
>  examples/ptpclient/ptpclient.c | 303 +++++++++++++++++++++++++++++----
>  1 file changed, 268 insertions(+), 35 deletions(-)
> 

It can be good to update the sample application document for this update.

Also can you please provide more information how this sample can be used
to test the application. What is the environment, how to verify the
function is working etc..
This can be documented in the sample application document if makes
sense, if not in the commit log.

<...>

> @@ -724,6 +941,11 @@ ptp_parse_args(int argc, char **argv)
>  
>  			ptp_data.kernel_time_set = ret;
>  			break;
> +		case 0:
> +			if (!strcmp(lgopts[option_index].name, "controller"))
> +				if (!strcmp(optarg, "pi"))
> +					mode = MODE_PI;
>

Please use "struct option lgopts" for the return values of
'getopt_long()', instead of strcmp.
'l2fwd' (l2fwd_parse_args()) has sample usage.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 1/3] ethdev: add frequency adjustment API
  2024-09-30  8:42     ` [PATCH v3 1/3] ethdev: add frequency adjustment API Mingjin Ye
@ 2024-09-30 20:51       ` Ferruh Yigit
  0 siblings, 0 replies; 49+ messages in thread
From: Ferruh Yigit @ 2024-09-30 20:51 UTC (permalink / raw)
  To: Mingjin Ye, dev; +Cc: Simei Su, Thomas Monjalon, Andrew Rybchenko

On 9/30/2024 9:42 AM, Mingjin Ye wrote:
> This patch adds freq adjustment API for PTP high accuracy.
> 
> Signed-off-by: Simei Su <simei.su@intel.com>
> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
>

Reviewed-by: Ferruh Yigit <ferruh.yigit@amd.com>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 0/3] add frequency adjustment support for PTP
  2024-09-30  8:42   ` [PATCH v3 " Mingjin Ye
                       ` (2 preceding siblings ...)
  2024-09-30  8:42     ` [PATCH v3 3/3] examples/ptpclient: add frequency adjustment support Mingjin Ye
@ 2024-09-30 20:53     ` Ferruh Yigit
  2024-10-10  9:32     ` [PATCH v4 " Mingjin Ye
  4 siblings, 0 replies; 49+ messages in thread
From: Ferruh Yigit @ 2024-09-30 20:53 UTC (permalink / raw)
  To: Mingjin Ye, dev; +Cc: Bruce Richardson, Kirill Rybalchenko

On 9/30/2024 9:42 AM, Mingjin Ye wrote:
> [1/3] ethdev: add frequency adjustment API
> [2/3] net/ice: add frequency adjustment support for PTP
> [3/3] examples/ptpclient: add frequency adjustment support
> ---
> v2: rte_eth_timesync_adjust_freq marked as experimental.
> ---
> v3: Add more description for API.
> 
> Mingjin Ye (3):
>   ethdev: add frequency adjustment API
>   net/ice: add frequency adjustment support for PTP
>   examples/ptpclient: add frequency adjustment support
>

Who is to ack the net/ice driver, there is no maintainer listed in the
maintainers file?

@Kirill, can you please review the ptpclient changes?

Thanks,
ferruh

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v4 0/3] add frequency adjustment support for PTP
  2024-09-30  8:42   ` [PATCH v3 " Mingjin Ye
                       ` (3 preceding siblings ...)
  2024-09-30 20:53     ` [PATCH v3 0/3] add frequency adjustment support for PTP Ferruh Yigit
@ 2024-10-10  9:32     ` Mingjin Ye
  2024-10-10  9:32       ` [PATCH v4 1/3] ethdev: add frequency adjustment API Mingjin Ye
                         ` (2 more replies)
  4 siblings, 3 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-10-10  9:32 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye

[1/3] ethdev: add frequency adjustment API
[2/3] net/ice: add frequency adjustment support for PTP
[3/3] examples/ptpclient: add frequency adjustment
---
v2: rte_eth_timesync_adjust_freq marked as experimental.
---
v3: Add more description for API.
---
v4: Documentation for adding a ptpclient.

Mingjin Ye (3):
  ethdev: add frequency adjustment API
  net/ice: add frequency adjustment support for PTP
  examples/ptpclient: add frequency adjustment

 doc/guides/nics/features.rst           |   4 +-
 doc/guides/nics/ice.rst                |  15 ++
 doc/guides/rel_notes/release_24_11.rst |   4 +
 doc/guides/sample_app_ug/ptpclient.rst |  12 +-
 drivers/net/ice/ice_ethdev.c           | 177 +++++++++++----
 drivers/net/ice/ice_ethdev.h           |   2 +
 drivers/net/ice/ice_rxtx.c             |   4 +-
 examples/ptpclient/ptpclient.c         | 284 +++++++++++++++++++++++--
 lib/ethdev/ethdev_driver.h             |   5 +
 lib/ethdev/ethdev_trace.h              |   9 +
 lib/ethdev/ethdev_trace_points.c       |   3 +
 lib/ethdev/rte_ethdev.c                |  18 ++
 lib/ethdev/rte_ethdev.h                |  43 ++++
 lib/ethdev/version.map                 |   3 +
 14 files changed, 515 insertions(+), 68 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v4 1/3] ethdev: add frequency adjustment API
  2024-10-10  9:32     ` [PATCH v4 " Mingjin Ye
@ 2024-10-10  9:32       ` Mingjin Ye
  2024-10-11  2:53         ` [PATCH v5 0/3] add frequency adjustment support for PTP Mingjin Ye
  2024-10-10  9:32       ` [PATCH v4 2/3] net/ice: " Mingjin Ye
  2024-10-10  9:32       ` [PATCH v4 3/3] examples/ptpclient: add frequency adjustment Mingjin Ye
  2 siblings, 1 reply; 49+ messages in thread
From: Mingjin Ye @ 2024-10-10  9:32 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko

This patch adds freq adjustment API for PTP high accuracy.

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
v2: rte_eth_timesync_adjust_freq marked as experimental
---
v3: Add more description for API.
---
 doc/guides/nics/features.rst           |  4 ++-
 doc/guides/rel_notes/release_24_11.rst |  4 +++
 lib/ethdev/ethdev_driver.h             |  5 +++
 lib/ethdev/ethdev_trace.h              |  9 ++++++
 lib/ethdev/ethdev_trace_points.c       |  3 ++
 lib/ethdev/rte_ethdev.c                | 18 +++++++++++
 lib/ethdev/rte_ethdev.h                | 43 ++++++++++++++++++++++++++
 lib/ethdev/version.map                 |  3 ++
 8 files changed, 88 insertions(+), 1 deletion(-)

diff --git a/doc/guides/nics/features.rst b/doc/guides/nics/features.rst
index cd0115ffb3..0508f118fe 100644
--- a/doc/guides/nics/features.rst
+++ b/doc/guides/nics/features.rst
@@ -677,10 +677,12 @@ Supports IEEE1588/802.1AS timestamping.
 
 * **[implements] eth_dev_ops**: ``timesync_enable``, ``timesync_disable``
   ``timesync_read_rx_timestamp``, ``timesync_read_tx_timestamp``,
-  ``timesync_adjust_time``, ``timesync_read_time``, ``timesync_write_time``.
+  ``timesync_adjust_time``, ``timesync_adjust_freq``,
+  ``timesync_read_time``, ``timesync_write_time``.
 * **[related]    API**: ``rte_eth_timesync_enable()``, ``rte_eth_timesync_disable()``,
   ``rte_eth_timesync_read_rx_timestamp()``,
   ``rte_eth_timesync_read_tx_timestamp``, ``rte_eth_timesync_adjust_time()``,
+  ``rte_eth_timesync_adjust_freq()``,
   ``rte_eth_timesync_read_time()``, ``rte_eth_timesync_write_time()``.
 
 
diff --git a/doc/guides/rel_notes/release_24_11.rst b/doc/guides/rel_notes/release_24_11.rst
index 2f78f2d125..1569f2df23 100644
--- a/doc/guides/rel_notes/release_24_11.rst
+++ b/doc/guides/rel_notes/release_24_11.rst
@@ -82,6 +82,10 @@ New Features
 
   The new statistics are useful for debugging and profiling.
 
+* **Added Ethernet device clock frequency adjustment.**
+
+  Added new function ``rte_eth_timesync_adjust_freq`` to adjust the clock
+  frequency for Ethernet devices.
 
 Removed Items
 -------------
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index ae00ead865..ec1a9d1cc7 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -664,6 +664,9 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 /** @internal Function used to adjust the device clock. */
 typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
 
+/** @internal Function used to adjust the clock frequency. */
+typedef int (*eth_timesync_adjust_freq)(struct rte_eth_dev *dev, int64_t);
+
 /** @internal Function used to get time from the device clock. */
 typedef int (*eth_timesync_read_time)(struct rte_eth_dev *dev,
 				      struct timespec *timestamp);
@@ -1378,6 +1381,8 @@ struct eth_dev_ops {
 	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
 	/** Adjust the device clock */
 	eth_timesync_adjust_time   timesync_adjust_time;
+	/** Adjust the clock frequency */
+	eth_timesync_adjust_freq   timesync_adjust_freq;
 	/** Get the device clock time */
 	eth_timesync_read_time     timesync_read_time;
 	/** Set the device clock time */
diff --git a/lib/ethdev/ethdev_trace.h b/lib/ethdev/ethdev_trace.h
index 36a38f718a..598e9f4d35 100644
--- a/lib/ethdev/ethdev_trace.h
+++ b/lib/ethdev/ethdev_trace.h
@@ -2183,6 +2183,15 @@ RTE_TRACE_POINT_FP(
 	rte_trace_point_emit_int(ret);
 )
 
+/* Called in loop in examples/ptpclient */
+RTE_TRACE_POINT_FP(
+	rte_eth_trace_timesync_adjust_freq,
+	RTE_TRACE_POINT_ARGS(uint16_t port_id, int64_t ppm, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_i64(ppm);
+	rte_trace_point_emit_int(ret);
+)
+
 /* Called in loop in app/test-flow-perf */
 RTE_TRACE_POINT_FP(
 	rte_flow_trace_create,
diff --git a/lib/ethdev/ethdev_trace_points.c b/lib/ethdev/ethdev_trace_points.c
index 99e04f5893..a99fec0c1e 100644
--- a/lib/ethdev/ethdev_trace_points.c
+++ b/lib/ethdev/ethdev_trace_points.c
@@ -409,6 +409,9 @@ RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_tx_timestamp,
 RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_time,
 	lib.ethdev.timesync_adjust_time)
 
+RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_freq,
+	lib.ethdev.timesync_adjust_freq)
+
 RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_time,
 	lib.ethdev.timesync_read_time)
 
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index f1c658f49e..660eab2f1e 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -6310,6 +6310,24 @@ rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta)
 	return ret;
 }
 
+int
+rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (*dev->dev_ops->timesync_adjust_freq == NULL)
+		return -ENOTSUP;
+	ret = eth_err(port_id, (*dev->dev_ops->timesync_adjust_freq)(dev, ppm));
+
+	rte_eth_trace_timesync_adjust_freq(port_id, ppm, ret);
+
+	return ret;
+}
+
 int
 rte_eth_timesync_read_time(uint16_t port_id, struct timespec *timestamp)
 {
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index a75e26bf07..c8a9993ad6 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -5293,6 +5293,49 @@ int rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
  */
 int rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta);
 
+/**
+ * Adjust the clock frequency on an Ethernet device.
+ *
+ * Adjusts the base frequency by a specified percentage of ppm (parts per
+ * million). This is usually used in conjunction with other Ethdev timesync
+ * functions to synchronize the device time using the IEEE1588/802.1AS
+ * protocol.
+ *
+ * The clock is subject to frequency deviation and rate of change drift due to
+ * the environment. The upper layer APP calculates the frequency compensation
+ * value of the slave clock relative to the master clock via a servo algorithm
+ * and adjusts the device clock frequency via "rte_eth_timesync_adjust_freq()".
+ * Commonly used servo algorithms are pi/linreg/ntpshm, for implementation
+ * see: https://github.com/nxp-archive/openil_linuxptp.git.
+ *
+ * The adjustment value obtained by the servo algorithm is usually in
+ * ppb (parts per billion). For consistency with the kernel driver .adjfine,
+ * the tuning values are in ppm. Note that 1 ppb is approximately 65.536 scaled
+ * ppm, see Linux kernel upstream commit 1060707e3809 (‘ptp: introduce helpers
+ * to adjust by scaled parts per million’).
+ *
+ * In addition, the device reference frequency is usually also the stepping
+ * threshold for the servo algorithm, and the frequency up and down adjustment
+ * range is limited by the device. The device clock frequency should be
+ * adjusted with "rte_eth_timesync_adjust_freq()" every time the clock is
+ * synchronised. Also use ‘rte_eth_timesync_adjust_time()’ to update the device
+ * clock only if the absolute value of the master/slave clock offset is greater than
+ * or equal to the step threshold.
+ *
+ * @param port_id
+ *  The port identifier of the Ethernet device.
+ * @param ppm
+ *  Parts per million with 16-bit fractional field
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+__rte_experimental
+int rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm);
+
 /**
  * Read the time from the timesync clock on an Ethernet device.
  *
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 1669055ca5..199480ef07 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -325,6 +325,9 @@ EXPERIMENTAL {
 	rte_flow_template_table_resizable;
 	rte_flow_template_table_resize;
 	rte_flow_template_table_resize_complete;
+
+	# added in 24.11
+	rte_eth_timesync_adjust_freq;
 };
 
 INTERNAL {
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v4 2/3] net/ice: add frequency adjustment support for PTP
  2024-10-10  9:32     ` [PATCH v4 " Mingjin Ye
  2024-10-10  9:32       ` [PATCH v4 1/3] ethdev: add frequency adjustment API Mingjin Ye
@ 2024-10-10  9:32       ` Mingjin Ye
  2024-10-10 10:34         ` Bruce Richardson
  2024-10-10  9:32       ` [PATCH v4 3/3] examples/ptpclient: add frequency adjustment Mingjin Ye
  2 siblings, 1 reply; 49+ messages in thread
From: Mingjin Ye @ 2024-10-10  9:32 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su

Add ice support for new ethdev API to adjust frequency for IEEE1588
PTP. Also, this patch reworks code for converting software update
to hardware update.

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
 doc/guides/nics/ice.rst      |  15 +++
 drivers/net/ice/ice_ethdev.c | 177 ++++++++++++++++++++++++++---------
 drivers/net/ice/ice_ethdev.h |   2 +
 drivers/net/ice/ice_rxtx.c   |   4 +-
 4 files changed, 153 insertions(+), 45 deletions(-)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index ae975d19ad..51fab743ef 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -328,6 +328,21 @@ Forward Error Correction (FEC)
 
 Supports get/set FEC mode and get FEC capability.
 
+Time Synchronisation
+~~~~~~~~~~~~~~~~~~~~
+
+The system operator can run a PTP (Precision Time Protocol) client application
+to synchronise the time on the network card (and optionally the time on the
+system) to the PTP master.
+
+ICE PMD supports PTP client applications that use the DPDK IEEE1588 API to
+communicate with the PTP master clock. Note that the PTP client application
+needs to run on the PF and vector mode needs to be disabled.
+
+.. code-block:: console
+
+    examples/dpdk-ptpclient -c f -n 3 -a 0000:ec:00.1 -- -T 1 -p 0x1 -c 1
+
 Generic Flow Support
 ~~~~~~~~~~~~~~~~~~~~
 
diff --git a/drivers/net/ice/ice_ethdev.c b/drivers/net/ice/ice_ethdev.c
index 7b1bd163a2..99d39849c4 100644
--- a/drivers/net/ice/ice_ethdev.c
+++ b/drivers/net/ice/ice_ethdev.c
@@ -10,6 +10,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <math.h>
 
 #include <rte_tailq.h>
 #include <rte_os_shim.h>
@@ -176,6 +177,7 @@ static int ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 static int ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					  struct timespec *timestamp);
 static int ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
+static int ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm);
 static int ice_timesync_read_time(struct rte_eth_dev *dev,
 				  struct timespec *timestamp);
 static int ice_timesync_write_time(struct rte_eth_dev *dev,
@@ -307,6 +309,7 @@ static const struct eth_dev_ops ice_eth_dev_ops = {
 	.timesync_read_rx_timestamp   = ice_timesync_read_rx_timestamp,
 	.timesync_read_tx_timestamp   = ice_timesync_read_tx_timestamp,
 	.timesync_adjust_time         = ice_timesync_adjust_time,
+	.timesync_adjust_freq         = ice_timesync_adjust_freq,
 	.timesync_read_time           = ice_timesync_read_time,
 	.timesync_write_time          = ice_timesync_write_time,
 	.timesync_disable             = ice_timesync_disable,
@@ -2332,6 +2335,34 @@ ice_get_supported_rxdid(struct ice_hw *hw)
 	return supported_rxdid;
 }
 
+static void ice_ptp_init_info(struct rte_eth_dev *dev)
+{
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ice_adapter *ad =
+		ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+
+	switch (hw->phy_model) {
+	case ICE_PHY_ETH56G:
+		ad->ptp_tx_block = hw->pf_id;
+		ad->ptp_tx_index = 0;
+		break;
+	case ICE_PHY_E810:
+	/* fallthrough */
+	case ICE_PHY_E830:
+		ad->ptp_tx_block = hw->port_info->lport;
+		ad->ptp_tx_index = 0;
+		break;
+	case ICE_PHY_E822:
+		ad->ptp_tx_block = hw->pf_id / ICE_PORTS_PER_QUAD;
+		ad->ptp_tx_index = (hw->pf_id % ICE_PORTS_PER_QUAD) *
+				ICE_PORTS_PER_PHY_E822 * ICE_QUADS_PER_PHY_E822;
+		break;
+	default:
+		PMD_DRV_LOG(WARNING, "Unsupported PHY model");
+		break;
+	}
+}
+
 static int
 ice_dev_init(struct rte_eth_dev *dev)
 {
@@ -2499,6 +2530,9 @@ ice_dev_init(struct rte_eth_dev *dev)
 	/* Initialize PHY model */
 	ice_ptp_init_phy_model(hw);
 
+	/* Initialize PTP info */
+	ice_ptp_init_info(dev);
+
 	if (hw->phy_model == ICE_PHY_E822) {
 		ret = ice_start_phy_timer_e822(hw, hw->pf_id);
 		if (ret)
@@ -6466,23 +6500,6 @@ ice_timesync_enable(struct rte_eth_dev *dev)
 		}
 	}
 
-	/* Initialize cycle counters for system time/RX/TX timestamp */
-	memset(&ad->systime_tc, 0, sizeof(struct rte_timecounter));
-	memset(&ad->rx_tstamp_tc, 0, sizeof(struct rte_timecounter));
-	memset(&ad->tx_tstamp_tc, 0, sizeof(struct rte_timecounter));
-
-	ad->systime_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->systime_tc.cc_shift = 0;
-	ad->systime_tc.nsec_mask = 0;
-
-	ad->rx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->rx_tstamp_tc.cc_shift = 0;
-	ad->rx_tstamp_tc.nsec_mask = 0;
-
-	ad->tx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->tx_tstamp_tc.cc_shift = 0;
-	ad->tx_tstamp_tc.nsec_mask = 0;
-
 	ad->ptp_ena = 1;
 
 	return 0;
@@ -6497,14 +6514,13 @@ ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	struct ice_rx_queue *rxq;
 	uint32_t ts_high;
-	uint64_t ts_ns, ns;
+	uint64_t ts_ns;
 
 	rxq = dev->data->rx_queues[flags];
 
 	ts_high = rxq->time_high;
 	ts_ns = ice_tstamp_convert_32b_64b(hw, ad, 1, ts_high);
-	ns = rte_timecounter_update(&ad->rx_tstamp_tc, ts_ns);
-	*timestamp = rte_ns_to_timespec(ns);
+	*timestamp = rte_ns_to_timespec(ts_ns);
 
 	return 0;
 }
@@ -6516,22 +6532,18 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	struct ice_adapter *ad =
 			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-	uint8_t lport;
-	uint64_t ts_ns, ns, tstamp;
+	uint64_t ts_ns, tstamp;
 	const uint64_t mask = 0xFFFFFFFF;
 	int ret;
 
-	lport = hw->port_info->lport;
-
-	ret = ice_read_phy_tstamp(hw, lport, 0, &tstamp);
-	if (ret) {
+	ret = ice_read_phy_tstamp(hw, ad->ptp_tx_block, ad->ptp_tx_index, &tstamp);
+	if (ret || tstamp == 0) {
 		PMD_DRV_LOG(ERR, "Failed to read phy timestamp");
 		return -1;
 	}
 
 	ts_ns = ice_tstamp_convert_32b_64b(hw, ad, 1, (tstamp >> 8) & mask);
-	ns = rte_timecounter_update(&ad->tx_tstamp_tc, ts_ns);
-	*timestamp = rte_ns_to_timespec(ns);
+	*timestamp = rte_ns_to_timespec(ts_ns);
 
 	return 0;
 }
@@ -6539,28 +6551,108 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 static int
 ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
 {
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint8_t tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc;
+	uint32_t lo, lo2, hi;
+	uint64_t time, ns;
+	int ret;
+
+	if (delta > INT32_MAX || delta < INT32_MIN) {
+		lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+		hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
+		lo2 = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+
+		if (lo2 < lo) {
+			lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+			hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
+		}
+
+		time = ((uint64_t)hi << 32) | lo;
+		ns = time + delta;
+
+		wr32(hw, GLTSYN_SHTIME_L(tmr_idx), ICE_LO_DWORD(ns));
+		wr32(hw, GLTSYN_SHTIME_H(tmr_idx), ICE_HI_DWORD(ns));
+		wr32(hw, GLTSYN_SHTIME_0(tmr_idx), 0);
+
+		ret = ice_ptp_init_time(hw, ns, true);
+		if (ret) {
+			PMD_DRV_LOG(ERR, "PTP init time failed, err %d", ret);
+			return -1;
+		}
+		return 0;
+	}
+
+	ret = ice_ptp_adj_clock(hw, delta, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP adj clock failed, err %d", ret);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	int64_t incval, diff = 0;
+	bool negative = false;
+	uint64_t div, rem;
+	uint64_t divisor = 1000000ULL << 16;
+	int shift;
+	int ret;
+
+	incval = ice_get_base_incval(hw, ICE_SRC_TMR_MODE_NANOSECONDS);
+
+	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;
 
-	ad->systime_tc.nsec += delta;
-	ad->rx_tstamp_tc.nsec += delta;
-	ad->tx_tstamp_tc.nsec += delta;
+		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 = ice_ptp_write_incval_locked(hw, incval, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+		return -1;
+	}
 	return 0;
 }
 
 static int
 ice_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
 {
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint64_t ns;
+	int ret;
 
 	ns = rte_timespec_to_ns(ts);
-
-	ad->systime_tc.nsec = ns;
-	ad->rx_tstamp_tc.nsec = ns;
-	ad->tx_tstamp_tc.nsec = ns;
+	ret = ice_ptp_init_time(hw, ns, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP init time failed, err %d", ret);
+		return -1;
+	}
 
 	return 0;
 }
@@ -6569,11 +6661,9 @@ static int
 ice_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
 {
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	uint8_t tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc;
 	uint32_t hi, lo, lo2;
-	uint64_t time, ns;
+	uint64_t time;
 
 	lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
 	hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
@@ -6585,8 +6675,7 @@ ice_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
 	}
 
 	time = ((uint64_t)hi << 32) | lo;
-	ns = rte_timecounter_update(&ad->systime_tc, time);
-	*ts = rte_ns_to_timespec(ns);
+	*ts = rte_ns_to_timespec(time);
 
 	return 0;
 }
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index 3ea9f37dc8..db192e76fe 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -614,6 +614,8 @@ struct ice_adapter {
 	struct rte_timecounter systime_tc;
 	struct rte_timecounter rx_tstamp_tc;
 	struct rte_timecounter tx_tstamp_tc;
+	uint8_t ptp_tx_block;
+	uint8_t ptp_tx_index;
 	bool ptp_ena;
 	uint64_t time_hw;
 	struct ice_fdir_prof_info fdir_prof_info[ICE_MAX_PTGS];
diff --git a/drivers/net/ice/ice_rxtx.c b/drivers/net/ice/ice_rxtx.c
index acd7539b5e..d2f9edc221 100644
--- a/drivers/net/ice/ice_rxtx.c
+++ b/drivers/net/ice/ice_rxtx.c
@@ -3066,7 +3066,9 @@ ice_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
 			else if (ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST)
 				cd_type_cmd_tso_mss |=
 					((uint64_t)ICE_TX_CTX_DESC_TSYN <<
-					ICE_TXD_CTX_QW1_CMD_S);
+					ICE_TXD_CTX_QW1_CMD_S) |
+					 (((uint64_t)txq->vsi->adapter->ptp_tx_index <<
+					 ICE_TXD_CTX_QW1_TSYN_S) & ICE_TXD_CTX_QW1_TSYN_M);
 
 			ctx_txd->tunneling_params =
 				rte_cpu_to_le_32(cd_tunneling_params);
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v4 3/3] examples/ptpclient: add frequency adjustment
  2024-10-10  9:32     ` [PATCH v4 " Mingjin Ye
  2024-10-10  9:32       ` [PATCH v4 1/3] ethdev: add frequency adjustment API Mingjin Ye
  2024-10-10  9:32       ` [PATCH v4 2/3] net/ice: " Mingjin Ye
@ 2024-10-10  9:32       ` Mingjin Ye
  2 siblings, 0 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-10-10  9:32 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su, Wenjun Wu, Kirill Rybalchenko

This patch adds PI servo controller to support frequency
adjustment API for IEEE1588 PTP.

For example, the command for starting ptpclient with PI controller is:
dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1 -c 1

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
v2: Add doc.
---
 doc/guides/sample_app_ug/ptpclient.rst |  12 +-
 examples/ptpclient/ptpclient.c         | 284 +++++++++++++++++++++++--
 2 files changed, 274 insertions(+), 22 deletions(-)

diff --git a/doc/guides/sample_app_ug/ptpclient.rst b/doc/guides/sample_app_ug/ptpclient.rst
index d47e942738..89fe575b5f 100644
--- a/doc/guides/sample_app_ug/ptpclient.rst
+++ b/doc/guides/sample_app_ug/ptpclient.rst
@@ -50,6 +50,10 @@ The adjustment for slave can be represented as:
 If the command line parameter ``-T 1`` is used the application also
 synchronizes the PTP PHC clock with the Linux kernel clock.
 
+If the command line parameter ``-c 1`` is used, the application will also
+use the servo of the local clock. Only one type of servo is currently
+implemented, the PI controller. Default 0 (not used).
+
 Compiling the Application
 -------------------------
 
@@ -65,7 +69,7 @@ To run the example in a ``linux`` environment:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-ptpclient -l 1 -n 4 -- -p 0x1 -T 0
+    ./<build_dir>/examples/dpdk-ptpclient -l 1 -n 4 -- -p 0x1 -T 0 -c 1
 
 Refer to *DPDK Getting Started Guide* for general information on running
 applications and the Environment Abstraction Layer (EAL) options.
@@ -73,7 +77,13 @@ applications and the Environment Abstraction Layer (EAL) options.
 * ``-p portmask``: Hexadecimal portmask.
 * ``-T 0``: Update only the PTP slave clock.
 * ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the PTP clock.
+* ``-c 0``: Not used clock servo controller.
+* ``-c 1``: The clock servo PI controller is used and the log will print information
+            about "master offset".
 
+Also, by adding ``-T 1`` and ``-c 1`` , the ``master offset`` value printed in the
+log will slowly converge and eventually stabilise at the nanosecond level. The
+synchronisation accuracy is much higher compared to not using a servo controller.
 
 Code Explanation
 ----------------
diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
index afb61bba51..dea8d9d54a 100644
--- a/examples/ptpclient/ptpclient.c
+++ b/examples/ptpclient/ptpclient.c
@@ -46,6 +46,35 @@ static volatile bool force_quit;
 #define KERNEL_TIME_ADJUST_LIMIT  20000
 #define PTP_PROTOCOL             0x88F7
 
+#define KP 0.7
+#define KI 0.3
+#define FREQ_EST_MARGIN 0.001
+
+enum servo_state {
+	SERVO_UNLOCKED,
+	SERVO_JUMP,
+	SERVO_LOCKED,
+};
+
+struct pi_servo {
+	double offset[2];
+	double local[2];
+	double drift;
+	double last_freq;
+	int count;
+
+	double max_frequency;
+	double step_threshold;
+	double first_step_threshold;
+	int first_update;
+};
+
+enum controller_mode {
+	MODE_NONE,
+	MODE_PI,
+	MAX_ALL
+} mode = MODE_NONE;
+
 struct rte_mempool *mbuf_pool;
 uint32_t ptp_enabled_port_mask;
 uint8_t ptp_enabled_port_nb;
@@ -135,6 +164,9 @@ struct ptpv2_data_slave_ordinary {
 	uint8_t ptpset;
 	uint8_t kernel_time_set;
 	uint16_t current_ptp_port;
+	int64_t master_offset;
+	int64_t path_delay;
+	struct pi_servo *servo;
 };
 
 static struct ptpv2_data_slave_ordinary ptp_data;
@@ -293,36 +325,44 @@ print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
 			ptp_data->tstamp3.tv_sec,
 			(ptp_data->tstamp3.tv_nsec));
 
-	printf("\nT4 - Master Clock.  %lds %ldns ",
+	printf("\nT4 - Master Clock.  %lds %ldns\n",
 			ptp_data->tstamp4.tv_sec,
 			(ptp_data->tstamp4.tv_nsec));
 
-	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+	if (mode == MODE_NONE) {
+		printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
 			ptp_data->delta);
 
-	clock_gettime(CLOCK_REALTIME, &sys_time);
-	rte_eth_timesync_read_time(ptp_data->current_ptp_port, &net_time);
+		clock_gettime(CLOCK_REALTIME, &sys_time);
+		rte_eth_timesync_read_time(ptp_data->current_ptp_port,
+					   &net_time);
 
-	time_t ts = net_time.tv_sec;
+		time_t ts = net_time.tv_sec;
 
-	printf("\n\nComparison between Linux kernel Time and PTP:");
+		printf("\n\nComparison between Linux kernel Time and PTP:");
 
-	printf("\nCurrent PTP Time: %.24s %.9ld ns",
+		printf("\nCurrent PTP Time: %.24s %.9ld ns",
 			ctime(&ts), net_time.tv_nsec);
 
-	nsec = (int64_t)timespec64_to_ns(&net_time) -
+		nsec = (int64_t)timespec64_to_ns(&net_time) -
 			(int64_t)timespec64_to_ns(&sys_time);
-	ptp_data->new_adj = ns_to_timeval(nsec);
+		ptp_data->new_adj = ns_to_timeval(nsec);
 
-	gettimeofday(&ptp_data->new_adj, NULL);
+		gettimeofday(&ptp_data->new_adj, NULL);
 
-	time_t tp = ptp_data->new_adj.tv_sec;
+		time_t tp = ptp_data->new_adj.tv_sec;
 
-	printf("\nCurrent SYS Time: %.24s %.6ld ns",
-				ctime(&tp), ptp_data->new_adj.tv_usec);
+		printf("\nCurrent SYS Time: %.24s %.6ld ns",
+			ctime(&tp), ptp_data->new_adj.tv_usec);
 
-	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
-				nsec);
+		printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
+			nsec);
+	}
+
+	if (mode == MODE_PI) {
+		printf("path delay: %"PRId64"ns\n", ptp_data->path_delay);
+		printf("master offset: %"PRId64"ns\n", ptp_data->master_offset);
+	}
 
 	printf("[Ctrl+C to quit]\n");
 
@@ -529,6 +569,149 @@ update_kernel_time(void)
 
 }
 
+static void
+clock_path_delay(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns, t3_ns, t4_ns;
+	int64_t pd, diff;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	t3_ns = timespec64_to_ns(&ptp_data->tstamp3);
+	t4_ns = timespec64_to_ns(&ptp_data->tstamp4);
+
+	pd = (t2_ns - t3_ns) + (t4_ns - t1_ns);
+	diff = t3_ns - t2_ns;
+	if (diff <= INT32_MAX && diff >= INT32_MIN)
+		ptp_data->path_delay = pd / 2;
+	else
+		ptp_data->path_delay = 0;
+}
+
+static double
+pi_sample(struct pi_servo *s, int64_t offset, double local_ts,
+	  enum servo_state *state)
+{
+	double ki_term, ppb = s->last_freq;
+	double freq_est_interval, localdiff;
+
+	switch (s->count) {
+	case 0:
+		s->offset[0] = offset;
+		s->local[0] = local_ts;
+		*state = SERVO_UNLOCKED;
+		s->count = 1;
+		break;
+	case 1:
+		s->offset[1] = offset;
+		s->local[1] = local_ts;
+
+		/* Make sure the first sample is older than the second. */
+		if (s->local[0] >= s->local[1]) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		/* Wait long enough before estimating the frequency offset. */
+		localdiff = (s->local[1] - s->local[0]) / 1e9;
+		localdiff += localdiff * FREQ_EST_MARGIN;
+		freq_est_interval = 0.016 / KI;
+		if (freq_est_interval > 1000.0)
+			freq_est_interval = 1000.0;
+
+		if (localdiff < freq_est_interval) {
+			*state = SERVO_UNLOCKED;
+			break;
+		}
+
+		/* Adjust drift by the measured frequency offset. */
+		s->drift += (1e9 - s->drift) * (s->offset[1] - s->offset[0]) /
+						(s->local[1] - s->local[0]);
+
+		if (s->drift < -s->max_frequency)
+			s->drift = -s->max_frequency;
+		else if (s->drift > s->max_frequency)
+			s->drift = s->max_frequency;
+
+		if ((s->first_update &&
+		     s->first_step_threshold &&
+		     s->first_step_threshold < llabs(offset)) ||
+		    (s->step_threshold &&
+		     s->step_threshold < llabs(offset)))
+			*state = SERVO_JUMP;
+		else
+			*state = SERVO_LOCKED;
+
+		ppb = s->drift;
+		s->count = 2;
+		break;
+	case 2:
+		/*
+		 * reset the clock servo when offset is greater than the max
+		 * offset value. Note that the clock jump will be performed in
+		 * step 1, so it is not necessary to have clock jump
+		 * immediately. This allows re-calculating drift as in initial
+		 * clock startup.
+		 */
+		if (s->step_threshold &&
+		    s->step_threshold < llabs(offset)) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		ki_term = KI * offset;
+		ppb = KP * offset + s->drift + ki_term;
+		if (ppb < -s->max_frequency)
+			ppb = -s->max_frequency;
+		else if (ppb > s->max_frequency)
+			ppb = s->max_frequency;
+		else
+			s->drift += ki_term;
+
+		*state = SERVO_LOCKED;
+		break;
+	}
+
+	s->last_freq = ppb;
+	return ppb;
+}
+
+static void
+ptp_adjust_servo(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns;
+	double adj_freq;
+	enum servo_state state = SERVO_UNLOCKED;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	ptp_data->master_offset = t2_ns - t1_ns - ptp_data->path_delay;
+	if (!ptp_data->path_delay)
+		return;
+
+	adj_freq = pi_sample(ptp_data->servo, ptp_data->master_offset, t2_ns,
+		     &state);
+
+	switch (state) {
+	case SERVO_UNLOCKED:
+		break;
+	case SERVO_JUMP:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+						-(long)(adj_freq * 65.536));
+		rte_eth_timesync_adjust_time(ptp_data->portid,
+					     -ptp_data->master_offset);
+		break;
+	case SERVO_LOCKED:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+					     -(long)(adj_freq * 65.536));
+		break;
+	}
+}
+
 /*
  * Parse the DELAY_RESP message.
  */
@@ -553,11 +736,16 @@ parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
 				((uint64_t)ntohl(rx_tstamp->sec_lsb)) |
 				(((uint64_t)ntohs(rx_tstamp->sec_msb)) << 32);
 
-			/* Evaluate the delta for adjustment. */
-			ptp_data->delta = delta_eval(ptp_data);
+			if (mode == MODE_PI) {
+				clock_path_delay(ptp_data);
+				ptp_adjust_servo(ptp_data);
+			} else {
+				/* Evaluate the delta for adjustment. */
+				ptp_data->delta = delta_eval(ptp_data);
 
-			rte_eth_timesync_adjust_time(ptp_data->portid,
-						     ptp_data->delta);
+				rte_eth_timesync_adjust_time(ptp_data->portid,
+								ptp_data->delta);
+			}
 
 			ptp_data->current_ptp_port = ptp_data->portid;
 
@@ -652,7 +840,9 @@ print_usage(const char *prgname)
 	printf("%s [EAL options] -- -p PORTMASK -T VALUE\n"
 		" -T VALUE: 0 - Disable, 1 - Enable Linux Clock"
 		" Synchronization (0 default)\n"
-		" -p PORTMASK: hexadecimal bitmask of ports to configure\n",
+		" -p PORTMASK: hexadecimal bitmask of ports to configure\n"
+		" -c CONTROLLER: 0 - Not used, 1 - PI. The servo which is"
+		" used to synchronize the local clock. (0 default)\n",
 		prgname);
 }
 
@@ -688,6 +878,36 @@ parse_ptp_kernel(const char *param)
 	return 1;
 }
 
+static int
+parse_ptp_servo_mode(const char *param)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* Parse the hexadecimal string. */
+	pm = strtoul(param, &end, 10);
+
+	if ((param[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	return pm;
+}
+
+static void
+servo_init(struct pi_servo *servo)
+{
+	memset(servo, 0x00, sizeof(*servo));
+
+	servo->drift = 100000000;
+	servo->last_freq = 100000000;
+	servo->count = 0;
+
+	servo->max_frequency = 100000000;
+	servo->step_threshold = 0.1 * NSEC_PER_SEC;
+	servo->first_step_threshold = 0.00002 * NSEC_PER_SEC;
+	servo->first_update = 1;
+}
+
 /* Parse the commandline arguments. */
 static int
 ptp_parse_args(int argc, char **argv)
@@ -700,7 +920,7 @@ ptp_parse_args(int argc, char **argv)
 
 	argvopt = argv;
 
-	while ((opt = getopt_long(argc, argvopt, "p:T:",
+	while ((opt = getopt_long(argc, argvopt, "p:T:c:",
 				  lgopts, &option_index)) != EOF) {
 
 		switch (opt) {
@@ -724,6 +944,17 @@ ptp_parse_args(int argc, char **argv)
 
 			ptp_data.kernel_time_set = ret;
 			break;
+		case 'c':
+			ret = parse_ptp_servo_mode(optarg);
+			if (ret == 0) {
+				mode = MODE_NONE;
+			} else if (ret == 1) {
+				mode = MODE_PI;
+			} else {
+				print_usage(prgname);
+				return -1;
+			}
+			break;
 
 		default:
 			print_usage(prgname);
@@ -778,6 +1009,14 @@ main(int argc, char *argv[])
 		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
 	/* >8 End of parsing specific arguments. */
 
+	if (mode == MODE_PI) {
+		ptp_data.servo = malloc(sizeof(*(ptp_data.servo)));
+		if (!ptp_data.servo)
+			rte_exit(EXIT_FAILURE, "no memory for servo\n");
+
+		servo_init(ptp_data.servo);
+	}
+
 	/* Check that there is an even number of ports to send/receive on. */
 	nb_ports = rte_eth_dev_count_avail();
 
@@ -831,6 +1070,9 @@ main(int argc, char *argv[])
 		rte_eth_dev_close(portid);
 	}
 
+	if (mode == MODE_PI)
+		free(ptp_data.servo);
+
 	/* clean up the EAL */
 	rte_eal_cleanup();
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v4 2/3] net/ice: add frequency adjustment support for PTP
  2024-10-10  9:32       ` [PATCH v4 2/3] net/ice: " Mingjin Ye
@ 2024-10-10 10:34         ` Bruce Richardson
  0 siblings, 0 replies; 49+ messages in thread
From: Bruce Richardson @ 2024-10-10 10:34 UTC (permalink / raw)
  To: Mingjin Ye; +Cc: dev, Simei Su

On Thu, Oct 10, 2024 at 09:32:20AM +0000, Mingjin Ye wrote:
> Add ice support for new ethdev API to adjust frequency for IEEE1588
> PTP. Also, this patch reworks code for converting software update
> to hardware update.
> 
> Signed-off-by: Simei Su <simei.su@intel.com>
> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
> ---

Hi Simei, Mingjin,

some review comments inline below.

thanks,
/Bruce

>  doc/guides/nics/ice.rst      |  15 +++
>  drivers/net/ice/ice_ethdev.c | 177 ++++++++++++++++++++++++++---------
>  drivers/net/ice/ice_ethdev.h |   2 +
>  drivers/net/ice/ice_rxtx.c   |   4 +-
>  4 files changed, 153 insertions(+), 45 deletions(-)
> 
> diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
> index ae975d19ad..51fab743ef 100644
> --- a/doc/guides/nics/ice.rst
> +++ b/doc/guides/nics/ice.rst
> @@ -328,6 +328,21 @@ Forward Error Correction (FEC)
>  
>  Supports get/set FEC mode and get FEC capability.
>  
> +Time Synchronisation
> +~~~~~~~~~~~~~~~~~~~~
> +
> +The system operator can run a PTP (Precision Time Protocol) client application
> +to synchronise the time on the network card (and optionally the time on the
> +system) to the PTP master.
> +
> +ICE PMD supports PTP client applications that use the DPDK IEEE1588 API to
> +communicate with the PTP master clock. Note that the PTP client application
> +needs to run on the PF and vector mode needs to be disabled.
> +
> +.. code-block:: console
> +
> +    examples/dpdk-ptpclient -c f -n 3 -a 0000:ec:00.1 -- -T 1 -p 0x1 -c 1
> +

Couple of questions about this documentation:

* After running the example given, does the time on the network card remain
  synchronised when the user runs their own app? If so, this text is ok,
  but, if not,  I think we better clarify that the PTP client functionality
  can be integrated into their own app.
* I think we need more detail on "vector mode needs to be disabled". Does
  this happen automatically when we go to use PTP, or does the app or user
  need to deal with it? For example, does the dpdk-ptpclient app disable
  vector mode itself? If not, we should include in the command the
  "force-max-simd-bitwidth=64" parameter to disable vector mode.

>  Generic Flow Support
>  ~~~~~~~~~~~~~~~~~~~~
>  
> diff --git a/drivers/net/ice/ice_ethdev.c b/drivers/net/ice/ice_ethdev.c
> index 7b1bd163a2..99d39849c4 100644
> --- a/drivers/net/ice/ice_ethdev.c
> +++ b/drivers/net/ice/ice_ethdev.c
> @@ -10,6 +10,7 @@
>  #include <sys/types.h>
>  #include <sys/stat.h>
>  #include <unistd.h>
> +#include <math.h>
>  
>  #include <rte_tailq.h>
>  #include <rte_os_shim.h>
> @@ -176,6 +177,7 @@ static int ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
>  static int ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
>  					  struct timespec *timestamp);
>  static int ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
> +static int ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm);
>  static int ice_timesync_read_time(struct rte_eth_dev *dev,
>  				  struct timespec *timestamp);
>  static int ice_timesync_write_time(struct rte_eth_dev *dev,
> @@ -307,6 +309,7 @@ static const struct eth_dev_ops ice_eth_dev_ops = {
>  	.timesync_read_rx_timestamp   = ice_timesync_read_rx_timestamp,
>  	.timesync_read_tx_timestamp   = ice_timesync_read_tx_timestamp,
>  	.timesync_adjust_time         = ice_timesync_adjust_time,
> +	.timesync_adjust_freq         = ice_timesync_adjust_freq,
>  	.timesync_read_time           = ice_timesync_read_time,
>  	.timesync_write_time          = ice_timesync_write_time,
>  	.timesync_disable             = ice_timesync_disable,
> @@ -2332,6 +2335,34 @@ ice_get_supported_rxdid(struct ice_hw *hw)
>  	return supported_rxdid;
>  }
>  
> +static void ice_ptp_init_info(struct rte_eth_dev *dev)
> +{
> +	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
> +	struct ice_adapter *ad =
> +		ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
> +
> +	switch (hw->phy_model) {
> +	case ICE_PHY_ETH56G:
> +		ad->ptp_tx_block = hw->pf_id;
> +		ad->ptp_tx_index = 0;
> +		break;
> +	case ICE_PHY_E810:
> +	/* fallthrough */

I don't believe this fallthrough is necessary. It should only be needed
when we have fallthrough after some code. Just having multiple case labels
is fine without annotation.

> +	case ICE_PHY_E830:
> +		ad->ptp_tx_block = hw->port_info->lport;
> +		ad->ptp_tx_index = 0;
> +		break;
> +	case ICE_PHY_E822:
> +		ad->ptp_tx_block = hw->pf_id / ICE_PORTS_PER_QUAD;
> +		ad->ptp_tx_index = (hw->pf_id % ICE_PORTS_PER_QUAD) *
> +				ICE_PORTS_PER_PHY_E822 * ICE_QUADS_PER_PHY_E822;
> +		break;
> +	default:
> +		PMD_DRV_LOG(WARNING, "Unsupported PHY model");
> +		break;
> +	}
> +}
> +
>  static int
>  ice_dev_init(struct rte_eth_dev *dev)
>  {
> @@ -2499,6 +2530,9 @@ ice_dev_init(struct rte_eth_dev *dev)
>  	/* Initialize PHY model */
>  	ice_ptp_init_phy_model(hw);
>  
> +	/* Initialize PTP info */
> +	ice_ptp_init_info(dev);
> +
>  	if (hw->phy_model == ICE_PHY_E822) {
>  		ret = ice_start_phy_timer_e822(hw, hw->pf_id);
>  		if (ret)
> @@ -6466,23 +6500,6 @@ ice_timesync_enable(struct rte_eth_dev *dev)
>  		}
>  	}
>  
> -	/* Initialize cycle counters for system time/RX/TX timestamp */
> -	memset(&ad->systime_tc, 0, sizeof(struct rte_timecounter));
> -	memset(&ad->rx_tstamp_tc, 0, sizeof(struct rte_timecounter));
> -	memset(&ad->tx_tstamp_tc, 0, sizeof(struct rte_timecounter));
> -
> -	ad->systime_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
> -	ad->systime_tc.cc_shift = 0;
> -	ad->systime_tc.nsec_mask = 0;
> -

I see lots of removals of ad->systime_tc from the code in the diff  Are
there any references to that left in the code? If not, please remove the
variable from the adapter structure. Same with the other values below.

> -	ad->rx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
> -	ad->rx_tstamp_tc.cc_shift = 0;
> -	ad->rx_tstamp_tc.nsec_mask = 0;
> -
> -	ad->tx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
> -	ad->tx_tstamp_tc.cc_shift = 0;
> -	ad->tx_tstamp_tc.nsec_mask = 0;
> -
>  	ad->ptp_ena = 1;
>  
>  	return 0;
> @@ -6497,14 +6514,13 @@ ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
>  			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
>  	struct ice_rx_queue *rxq;
>  	uint32_t ts_high;
> -	uint64_t ts_ns, ns;
> +	uint64_t ts_ns;
>  
<snip>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v5 0/3] add frequency adjustment support for PTP
  2024-10-10  9:32       ` [PATCH v4 1/3] ethdev: add frequency adjustment API Mingjin Ye
@ 2024-10-11  2:53         ` Mingjin Ye
  2024-10-11  2:53           ` [PATCH v5 1/3] ethdev: add frequency adjustment API Mingjin Ye
                             ` (3 more replies)
  0 siblings, 4 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-10-11  2:53 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye

[1/3] ethdev: add frequency adjustment API
[2/3] net/ice: add frequency adjustment support for PTP
[3/3] examples/ptpclient: add frequency adjustment
---
v2: rte_eth_timesync_adjust_freq marked as experimental.
---
v3: Add more description for API.
---
v4: Documentation for adding a ptpclient.
---
v5: Fixes in net/ice.

Mingjin Ye (3):
  ethdev: add frequency adjustment API
  net/ice: add frequency adjustment support for PTP
  examples/ptpclient: add frequency adjustment

 doc/guides/nics/features.rst           |   4 +-
 doc/guides/nics/ice.rst                |  16 ++
 doc/guides/rel_notes/release_24_11.rst |   4 +
 doc/guides/sample_app_ug/ptpclient.rst |  12 +-
 drivers/net/ice/ice_ethdev.c           | 176 +++++++++++----
 drivers/net/ice/ice_ethdev.h           |   5 +-
 drivers/net/ice/ice_rxtx.c             |   4 +-
 examples/ptpclient/ptpclient.c         | 284 +++++++++++++++++++++++--
 lib/ethdev/ethdev_driver.h             |   5 +
 lib/ethdev/ethdev_trace.h              |   9 +
 lib/ethdev/ethdev_trace_points.c       |   3 +
 lib/ethdev/rte_ethdev.c                |  18 ++
 lib/ethdev/rte_ethdev.h                |  43 ++++
 lib/ethdev/version.map                 |   3 +
 14 files changed, 515 insertions(+), 71 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v5 1/3] ethdev: add frequency adjustment API
  2024-10-11  2:53         ` [PATCH v5 0/3] add frequency adjustment support for PTP Mingjin Ye
@ 2024-10-11  2:53           ` Mingjin Ye
  2024-10-11  2:53           ` [PATCH v5 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
                             ` (2 subsequent siblings)
  3 siblings, 0 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-10-11  2:53 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko

This patch adds freq adjustment API for PTP high accuracy.

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
v2: rte_eth_timesync_adjust_freq marked as experimental
---
v3: Add more description for API.
---
 doc/guides/nics/features.rst           |  4 ++-
 doc/guides/rel_notes/release_24_11.rst |  4 +++
 lib/ethdev/ethdev_driver.h             |  5 +++
 lib/ethdev/ethdev_trace.h              |  9 ++++++
 lib/ethdev/ethdev_trace_points.c       |  3 ++
 lib/ethdev/rte_ethdev.c                | 18 +++++++++++
 lib/ethdev/rte_ethdev.h                | 43 ++++++++++++++++++++++++++
 lib/ethdev/version.map                 |  3 ++
 8 files changed, 88 insertions(+), 1 deletion(-)

diff --git a/doc/guides/nics/features.rst b/doc/guides/nics/features.rst
index cd0115ffb3..0508f118fe 100644
--- a/doc/guides/nics/features.rst
+++ b/doc/guides/nics/features.rst
@@ -677,10 +677,12 @@ Supports IEEE1588/802.1AS timestamping.
 
 * **[implements] eth_dev_ops**: ``timesync_enable``, ``timesync_disable``
   ``timesync_read_rx_timestamp``, ``timesync_read_tx_timestamp``,
-  ``timesync_adjust_time``, ``timesync_read_time``, ``timesync_write_time``.
+  ``timesync_adjust_time``, ``timesync_adjust_freq``,
+  ``timesync_read_time``, ``timesync_write_time``.
 * **[related]    API**: ``rte_eth_timesync_enable()``, ``rte_eth_timesync_disable()``,
   ``rte_eth_timesync_read_rx_timestamp()``,
   ``rte_eth_timesync_read_tx_timestamp``, ``rte_eth_timesync_adjust_time()``,
+  ``rte_eth_timesync_adjust_freq()``,
   ``rte_eth_timesync_read_time()``, ``rte_eth_timesync_write_time()``.
 
 
diff --git a/doc/guides/rel_notes/release_24_11.rst b/doc/guides/rel_notes/release_24_11.rst
index 2f78f2d125..1569f2df23 100644
--- a/doc/guides/rel_notes/release_24_11.rst
+++ b/doc/guides/rel_notes/release_24_11.rst
@@ -82,6 +82,10 @@ New Features
 
   The new statistics are useful for debugging and profiling.
 
+* **Added Ethernet device clock frequency adjustment.**
+
+  Added new function ``rte_eth_timesync_adjust_freq`` to adjust the clock
+  frequency for Ethernet devices.
 
 Removed Items
 -------------
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index ae00ead865..ec1a9d1cc7 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -664,6 +664,9 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 /** @internal Function used to adjust the device clock. */
 typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
 
+/** @internal Function used to adjust the clock frequency. */
+typedef int (*eth_timesync_adjust_freq)(struct rte_eth_dev *dev, int64_t);
+
 /** @internal Function used to get time from the device clock. */
 typedef int (*eth_timesync_read_time)(struct rte_eth_dev *dev,
 				      struct timespec *timestamp);
@@ -1378,6 +1381,8 @@ struct eth_dev_ops {
 	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
 	/** Adjust the device clock */
 	eth_timesync_adjust_time   timesync_adjust_time;
+	/** Adjust the clock frequency */
+	eth_timesync_adjust_freq   timesync_adjust_freq;
 	/** Get the device clock time */
 	eth_timesync_read_time     timesync_read_time;
 	/** Set the device clock time */
diff --git a/lib/ethdev/ethdev_trace.h b/lib/ethdev/ethdev_trace.h
index 36a38f718a..598e9f4d35 100644
--- a/lib/ethdev/ethdev_trace.h
+++ b/lib/ethdev/ethdev_trace.h
@@ -2183,6 +2183,15 @@ RTE_TRACE_POINT_FP(
 	rte_trace_point_emit_int(ret);
 )
 
+/* Called in loop in examples/ptpclient */
+RTE_TRACE_POINT_FP(
+	rte_eth_trace_timesync_adjust_freq,
+	RTE_TRACE_POINT_ARGS(uint16_t port_id, int64_t ppm, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_i64(ppm);
+	rte_trace_point_emit_int(ret);
+)
+
 /* Called in loop in app/test-flow-perf */
 RTE_TRACE_POINT_FP(
 	rte_flow_trace_create,
diff --git a/lib/ethdev/ethdev_trace_points.c b/lib/ethdev/ethdev_trace_points.c
index 99e04f5893..a99fec0c1e 100644
--- a/lib/ethdev/ethdev_trace_points.c
+++ b/lib/ethdev/ethdev_trace_points.c
@@ -409,6 +409,9 @@ RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_tx_timestamp,
 RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_time,
 	lib.ethdev.timesync_adjust_time)
 
+RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_freq,
+	lib.ethdev.timesync_adjust_freq)
+
 RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_time,
 	lib.ethdev.timesync_read_time)
 
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index f1c658f49e..660eab2f1e 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -6310,6 +6310,24 @@ rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta)
 	return ret;
 }
 
+int
+rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (*dev->dev_ops->timesync_adjust_freq == NULL)
+		return -ENOTSUP;
+	ret = eth_err(port_id, (*dev->dev_ops->timesync_adjust_freq)(dev, ppm));
+
+	rte_eth_trace_timesync_adjust_freq(port_id, ppm, ret);
+
+	return ret;
+}
+
 int
 rte_eth_timesync_read_time(uint16_t port_id, struct timespec *timestamp)
 {
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index a75e26bf07..c8a9993ad6 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -5293,6 +5293,49 @@ int rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
  */
 int rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta);
 
+/**
+ * Adjust the clock frequency on an Ethernet device.
+ *
+ * Adjusts the base frequency by a specified percentage of ppm (parts per
+ * million). This is usually used in conjunction with other Ethdev timesync
+ * functions to synchronize the device time using the IEEE1588/802.1AS
+ * protocol.
+ *
+ * The clock is subject to frequency deviation and rate of change drift due to
+ * the environment. The upper layer APP calculates the frequency compensation
+ * value of the slave clock relative to the master clock via a servo algorithm
+ * and adjusts the device clock frequency via "rte_eth_timesync_adjust_freq()".
+ * Commonly used servo algorithms are pi/linreg/ntpshm, for implementation
+ * see: https://github.com/nxp-archive/openil_linuxptp.git.
+ *
+ * The adjustment value obtained by the servo algorithm is usually in
+ * ppb (parts per billion). For consistency with the kernel driver .adjfine,
+ * the tuning values are in ppm. Note that 1 ppb is approximately 65.536 scaled
+ * ppm, see Linux kernel upstream commit 1060707e3809 (‘ptp: introduce helpers
+ * to adjust by scaled parts per million’).
+ *
+ * In addition, the device reference frequency is usually also the stepping
+ * threshold for the servo algorithm, and the frequency up and down adjustment
+ * range is limited by the device. The device clock frequency should be
+ * adjusted with "rte_eth_timesync_adjust_freq()" every time the clock is
+ * synchronised. Also use ‘rte_eth_timesync_adjust_time()’ to update the device
+ * clock only if the absolute value of the master/slave clock offset is greater than
+ * or equal to the step threshold.
+ *
+ * @param port_id
+ *  The port identifier of the Ethernet device.
+ * @param ppm
+ *  Parts per million with 16-bit fractional field
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+__rte_experimental
+int rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm);
+
 /**
  * Read the time from the timesync clock on an Ethernet device.
  *
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 1669055ca5..199480ef07 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -325,6 +325,9 @@ EXPERIMENTAL {
 	rte_flow_template_table_resizable;
 	rte_flow_template_table_resize;
 	rte_flow_template_table_resize_complete;
+
+	# added in 24.11
+	rte_eth_timesync_adjust_freq;
 };
 
 INTERNAL {
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v5 2/3] net/ice: add frequency adjustment support for PTP
  2024-10-11  2:53         ` [PATCH v5 0/3] add frequency adjustment support for PTP Mingjin Ye
  2024-10-11  2:53           ` [PATCH v5 1/3] ethdev: add frequency adjustment API Mingjin Ye
@ 2024-10-11  2:53           ` Mingjin Ye
  2024-10-11  2:53           ` [PATCH v5 3/3] examples/ptpclient: add frequency adjustment Mingjin Ye
  2024-10-11  6:34           ` [PATCH v6 0/3] add frequency adjustment support for PTP Mingjin Ye
  3 siblings, 0 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-10-11  2:53 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su

Add ice support for new ethdev API to adjust frequency for IEEE1588
PTP. Also, this patch reworks code for converting software update
to hardware update.

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
v2: Modify documents and delete old variables.
---
 doc/guides/nics/ice.rst      |  16 ++++
 drivers/net/ice/ice_ethdev.c | 176 ++++++++++++++++++++++++++---------
 drivers/net/ice/ice_ethdev.h |   5 +-
 drivers/net/ice/ice_rxtx.c   |   4 +-
 4 files changed, 153 insertions(+), 48 deletions(-)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index ae975d19ad..061c8c7a20 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -328,6 +328,22 @@ Forward Error Correction (FEC)
 
 Supports get/set FEC mode and get FEC capability.
 
+Time Synchronisation
+~~~~~~~~~~~~~~~~~~~~
+
+The system operator can run a PTP (Precision Time Protocol) client application
+to synchronise the time on the network card (and optionally the time on the
+system) to the PTP master.
+
+ICE PMD supports PTP client applications that use the DPDK IEEE1588 API to
+communicate with the PTP master clock. Note that PTP client application needs
+to run on PF and add the ``--force-max-simd-bitwidth=64`` startup parameter to
+disable vector mode.
+
+.. code-block:: console
+
+    examples/dpdk-ptpclient -c f -n 3 -a 0000:ec:00.1 --force-max-simd-bitwidth=64 -- -T 1 -p 0x1 -c 1
+
 Generic Flow Support
 ~~~~~~~~~~~~~~~~~~~~
 
diff --git a/drivers/net/ice/ice_ethdev.c b/drivers/net/ice/ice_ethdev.c
index 7b1bd163a2..0e1d332ef3 100644
--- a/drivers/net/ice/ice_ethdev.c
+++ b/drivers/net/ice/ice_ethdev.c
@@ -10,6 +10,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <math.h>
 
 #include <rte_tailq.h>
 #include <rte_os_shim.h>
@@ -176,6 +177,7 @@ static int ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 static int ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					  struct timespec *timestamp);
 static int ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
+static int ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm);
 static int ice_timesync_read_time(struct rte_eth_dev *dev,
 				  struct timespec *timestamp);
 static int ice_timesync_write_time(struct rte_eth_dev *dev,
@@ -307,6 +309,7 @@ static const struct eth_dev_ops ice_eth_dev_ops = {
 	.timesync_read_rx_timestamp   = ice_timesync_read_rx_timestamp,
 	.timesync_read_tx_timestamp   = ice_timesync_read_tx_timestamp,
 	.timesync_adjust_time         = ice_timesync_adjust_time,
+	.timesync_adjust_freq         = ice_timesync_adjust_freq,
 	.timesync_read_time           = ice_timesync_read_time,
 	.timesync_write_time          = ice_timesync_write_time,
 	.timesync_disable             = ice_timesync_disable,
@@ -2332,6 +2335,33 @@ ice_get_supported_rxdid(struct ice_hw *hw)
 	return supported_rxdid;
 }
 
+static void ice_ptp_init_info(struct rte_eth_dev *dev)
+{
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ice_adapter *ad =
+		ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+
+	switch (hw->phy_model) {
+	case ICE_PHY_ETH56G:
+		ad->ptp_tx_block = hw->pf_id;
+		ad->ptp_tx_index = 0;
+		break;
+	case ICE_PHY_E810:
+	case ICE_PHY_E830:
+		ad->ptp_tx_block = hw->port_info->lport;
+		ad->ptp_tx_index = 0;
+		break;
+	case ICE_PHY_E822:
+		ad->ptp_tx_block = hw->pf_id / ICE_PORTS_PER_QUAD;
+		ad->ptp_tx_index = (hw->pf_id % ICE_PORTS_PER_QUAD) *
+				ICE_PORTS_PER_PHY_E822 * ICE_QUADS_PER_PHY_E822;
+		break;
+	default:
+		PMD_DRV_LOG(WARNING, "Unsupported PHY model");
+		break;
+	}
+}
+
 static int
 ice_dev_init(struct rte_eth_dev *dev)
 {
@@ -2499,6 +2529,9 @@ ice_dev_init(struct rte_eth_dev *dev)
 	/* Initialize PHY model */
 	ice_ptp_init_phy_model(hw);
 
+	/* Initialize PTP info */
+	ice_ptp_init_info(dev);
+
 	if (hw->phy_model == ICE_PHY_E822) {
 		ret = ice_start_phy_timer_e822(hw, hw->pf_id);
 		if (ret)
@@ -6466,23 +6499,6 @@ ice_timesync_enable(struct rte_eth_dev *dev)
 		}
 	}
 
-	/* Initialize cycle counters for system time/RX/TX timestamp */
-	memset(&ad->systime_tc, 0, sizeof(struct rte_timecounter));
-	memset(&ad->rx_tstamp_tc, 0, sizeof(struct rte_timecounter));
-	memset(&ad->tx_tstamp_tc, 0, sizeof(struct rte_timecounter));
-
-	ad->systime_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->systime_tc.cc_shift = 0;
-	ad->systime_tc.nsec_mask = 0;
-
-	ad->rx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->rx_tstamp_tc.cc_shift = 0;
-	ad->rx_tstamp_tc.nsec_mask = 0;
-
-	ad->tx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->tx_tstamp_tc.cc_shift = 0;
-	ad->tx_tstamp_tc.nsec_mask = 0;
-
 	ad->ptp_ena = 1;
 
 	return 0;
@@ -6497,14 +6513,13 @@ ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	struct ice_rx_queue *rxq;
 	uint32_t ts_high;
-	uint64_t ts_ns, ns;
+	uint64_t ts_ns;
 
 	rxq = dev->data->rx_queues[flags];
 
 	ts_high = rxq->time_high;
 	ts_ns = ice_tstamp_convert_32b_64b(hw, ad, 1, ts_high);
-	ns = rte_timecounter_update(&ad->rx_tstamp_tc, ts_ns);
-	*timestamp = rte_ns_to_timespec(ns);
+	*timestamp = rte_ns_to_timespec(ts_ns);
 
 	return 0;
 }
@@ -6516,22 +6531,18 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	struct ice_adapter *ad =
 			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-	uint8_t lport;
-	uint64_t ts_ns, ns, tstamp;
+	uint64_t ts_ns, tstamp;
 	const uint64_t mask = 0xFFFFFFFF;
 	int ret;
 
-	lport = hw->port_info->lport;
-
-	ret = ice_read_phy_tstamp(hw, lport, 0, &tstamp);
-	if (ret) {
+	ret = ice_read_phy_tstamp(hw, ad->ptp_tx_block, ad->ptp_tx_index, &tstamp);
+	if (ret || tstamp == 0) {
 		PMD_DRV_LOG(ERR, "Failed to read phy timestamp");
 		return -1;
 	}
 
 	ts_ns = ice_tstamp_convert_32b_64b(hw, ad, 1, (tstamp >> 8) & mask);
-	ns = rte_timecounter_update(&ad->tx_tstamp_tc, ts_ns);
-	*timestamp = rte_ns_to_timespec(ns);
+	*timestamp = rte_ns_to_timespec(ts_ns);
 
 	return 0;
 }
@@ -6539,28 +6550,108 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 static int
 ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
 {
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint8_t tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc;
+	uint32_t lo, lo2, hi;
+	uint64_t time, ns;
+	int ret;
+
+	if (delta > INT32_MAX || delta < INT32_MIN) {
+		lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+		hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
+		lo2 = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+
+		if (lo2 < lo) {
+			lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+			hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
+		}
+
+		time = ((uint64_t)hi << 32) | lo;
+		ns = time + delta;
+
+		wr32(hw, GLTSYN_SHTIME_L(tmr_idx), ICE_LO_DWORD(ns));
+		wr32(hw, GLTSYN_SHTIME_H(tmr_idx), ICE_HI_DWORD(ns));
+		wr32(hw, GLTSYN_SHTIME_0(tmr_idx), 0);
+
+		ret = ice_ptp_init_time(hw, ns, true);
+		if (ret) {
+			PMD_DRV_LOG(ERR, "PTP init time failed, err %d", ret);
+			return -1;
+		}
+		return 0;
+	}
+
+	ret = ice_ptp_adj_clock(hw, delta, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP adj clock failed, err %d", ret);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	int64_t incval, diff = 0;
+	bool negative = false;
+	uint64_t div, rem;
+	uint64_t divisor = 1000000ULL << 16;
+	int shift;
+	int ret;
+
+	incval = ice_get_base_incval(hw, ICE_SRC_TMR_MODE_NANOSECONDS);
+
+	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;
 
-	ad->systime_tc.nsec += delta;
-	ad->rx_tstamp_tc.nsec += delta;
-	ad->tx_tstamp_tc.nsec += delta;
+		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 = ice_ptp_write_incval_locked(hw, incval, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+		return -1;
+	}
 	return 0;
 }
 
 static int
 ice_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
 {
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint64_t ns;
+	int ret;
 
 	ns = rte_timespec_to_ns(ts);
-
-	ad->systime_tc.nsec = ns;
-	ad->rx_tstamp_tc.nsec = ns;
-	ad->tx_tstamp_tc.nsec = ns;
+	ret = ice_ptp_init_time(hw, ns, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP init time failed, err %d", ret);
+		return -1;
+	}
 
 	return 0;
 }
@@ -6569,11 +6660,9 @@ static int
 ice_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
 {
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	uint8_t tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc;
 	uint32_t hi, lo, lo2;
-	uint64_t time, ns;
+	uint64_t time;
 
 	lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
 	hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
@@ -6585,8 +6674,7 @@ ice_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
 	}
 
 	time = ((uint64_t)hi << 32) | lo;
-	ns = rte_timecounter_update(&ad->systime_tc, time);
-	*ts = rte_ns_to_timespec(ns);
+	*ts = rte_ns_to_timespec(time);
 
 	return 0;
 }
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index 3ea9f37dc8..174ab0c937 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -611,9 +611,8 @@ struct ice_adapter {
 	/* For vector PMD */
 	eth_rx_burst_t tx_pkt_burst;
 	/* For PTP */
-	struct rte_timecounter systime_tc;
-	struct rte_timecounter rx_tstamp_tc;
-	struct rte_timecounter tx_tstamp_tc;
+	uint8_t ptp_tx_block;
+	uint8_t ptp_tx_index;
 	bool ptp_ena;
 	uint64_t time_hw;
 	struct ice_fdir_prof_info fdir_prof_info[ICE_MAX_PTGS];
diff --git a/drivers/net/ice/ice_rxtx.c b/drivers/net/ice/ice_rxtx.c
index acd7539b5e..d2f9edc221 100644
--- a/drivers/net/ice/ice_rxtx.c
+++ b/drivers/net/ice/ice_rxtx.c
@@ -3066,7 +3066,9 @@ ice_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
 			else if (ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST)
 				cd_type_cmd_tso_mss |=
 					((uint64_t)ICE_TX_CTX_DESC_TSYN <<
-					ICE_TXD_CTX_QW1_CMD_S);
+					ICE_TXD_CTX_QW1_CMD_S) |
+					 (((uint64_t)txq->vsi->adapter->ptp_tx_index <<
+					 ICE_TXD_CTX_QW1_TSYN_S) & ICE_TXD_CTX_QW1_TSYN_M);
 
 			ctx_txd->tunneling_params =
 				rte_cpu_to_le_32(cd_tunneling_params);
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v5 3/3] examples/ptpclient: add frequency adjustment
  2024-10-11  2:53         ` [PATCH v5 0/3] add frequency adjustment support for PTP Mingjin Ye
  2024-10-11  2:53           ` [PATCH v5 1/3] ethdev: add frequency adjustment API Mingjin Ye
  2024-10-11  2:53           ` [PATCH v5 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
@ 2024-10-11  2:53           ` Mingjin Ye
  2024-10-11  6:34           ` [PATCH v6 0/3] add frequency adjustment support for PTP Mingjin Ye
  3 siblings, 0 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-10-11  2:53 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su, Wenjun Wu, Kirill Rybalchenko

This patch adds PI servo controller to support frequency
adjustment API for IEEE1588 PTP.

For example, the command for starting ptpclient with PI controller is:
dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1 -c 1

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
v2: Add doc.
---
 doc/guides/sample_app_ug/ptpclient.rst |  12 +-
 examples/ptpclient/ptpclient.c         | 284 +++++++++++++++++++++++--
 2 files changed, 274 insertions(+), 22 deletions(-)

diff --git a/doc/guides/sample_app_ug/ptpclient.rst b/doc/guides/sample_app_ug/ptpclient.rst
index d47e942738..89fe575b5f 100644
--- a/doc/guides/sample_app_ug/ptpclient.rst
+++ b/doc/guides/sample_app_ug/ptpclient.rst
@@ -50,6 +50,10 @@ The adjustment for slave can be represented as:
 If the command line parameter ``-T 1`` is used the application also
 synchronizes the PTP PHC clock with the Linux kernel clock.
 
+If the command line parameter ``-c 1`` is used, the application will also
+use the servo of the local clock. Only one type of servo is currently
+implemented, the PI controller. Default 0 (not used).
+
 Compiling the Application
 -------------------------
 
@@ -65,7 +69,7 @@ To run the example in a ``linux`` environment:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-ptpclient -l 1 -n 4 -- -p 0x1 -T 0
+    ./<build_dir>/examples/dpdk-ptpclient -l 1 -n 4 -- -p 0x1 -T 0 -c 1
 
 Refer to *DPDK Getting Started Guide* for general information on running
 applications and the Environment Abstraction Layer (EAL) options.
@@ -73,7 +77,13 @@ applications and the Environment Abstraction Layer (EAL) options.
 * ``-p portmask``: Hexadecimal portmask.
 * ``-T 0``: Update only the PTP slave clock.
 * ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the PTP clock.
+* ``-c 0``: Not used clock servo controller.
+* ``-c 1``: The clock servo PI controller is used and the log will print information
+            about "master offset".
 
+Also, by adding ``-T 1`` and ``-c 1`` , the ``master offset`` value printed in the
+log will slowly converge and eventually stabilise at the nanosecond level. The
+synchronisation accuracy is much higher compared to not using a servo controller.
 
 Code Explanation
 ----------------
diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
index afb61bba51..dea8d9d54a 100644
--- a/examples/ptpclient/ptpclient.c
+++ b/examples/ptpclient/ptpclient.c
@@ -46,6 +46,35 @@ static volatile bool force_quit;
 #define KERNEL_TIME_ADJUST_LIMIT  20000
 #define PTP_PROTOCOL             0x88F7
 
+#define KP 0.7
+#define KI 0.3
+#define FREQ_EST_MARGIN 0.001
+
+enum servo_state {
+	SERVO_UNLOCKED,
+	SERVO_JUMP,
+	SERVO_LOCKED,
+};
+
+struct pi_servo {
+	double offset[2];
+	double local[2];
+	double drift;
+	double last_freq;
+	int count;
+
+	double max_frequency;
+	double step_threshold;
+	double first_step_threshold;
+	int first_update;
+};
+
+enum controller_mode {
+	MODE_NONE,
+	MODE_PI,
+	MAX_ALL
+} mode = MODE_NONE;
+
 struct rte_mempool *mbuf_pool;
 uint32_t ptp_enabled_port_mask;
 uint8_t ptp_enabled_port_nb;
@@ -135,6 +164,9 @@ struct ptpv2_data_slave_ordinary {
 	uint8_t ptpset;
 	uint8_t kernel_time_set;
 	uint16_t current_ptp_port;
+	int64_t master_offset;
+	int64_t path_delay;
+	struct pi_servo *servo;
 };
 
 static struct ptpv2_data_slave_ordinary ptp_data;
@@ -293,36 +325,44 @@ print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
 			ptp_data->tstamp3.tv_sec,
 			(ptp_data->tstamp3.tv_nsec));
 
-	printf("\nT4 - Master Clock.  %lds %ldns ",
+	printf("\nT4 - Master Clock.  %lds %ldns\n",
 			ptp_data->tstamp4.tv_sec,
 			(ptp_data->tstamp4.tv_nsec));
 
-	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+	if (mode == MODE_NONE) {
+		printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
 			ptp_data->delta);
 
-	clock_gettime(CLOCK_REALTIME, &sys_time);
-	rte_eth_timesync_read_time(ptp_data->current_ptp_port, &net_time);
+		clock_gettime(CLOCK_REALTIME, &sys_time);
+		rte_eth_timesync_read_time(ptp_data->current_ptp_port,
+					   &net_time);
 
-	time_t ts = net_time.tv_sec;
+		time_t ts = net_time.tv_sec;
 
-	printf("\n\nComparison between Linux kernel Time and PTP:");
+		printf("\n\nComparison between Linux kernel Time and PTP:");
 
-	printf("\nCurrent PTP Time: %.24s %.9ld ns",
+		printf("\nCurrent PTP Time: %.24s %.9ld ns",
 			ctime(&ts), net_time.tv_nsec);
 
-	nsec = (int64_t)timespec64_to_ns(&net_time) -
+		nsec = (int64_t)timespec64_to_ns(&net_time) -
 			(int64_t)timespec64_to_ns(&sys_time);
-	ptp_data->new_adj = ns_to_timeval(nsec);
+		ptp_data->new_adj = ns_to_timeval(nsec);
 
-	gettimeofday(&ptp_data->new_adj, NULL);
+		gettimeofday(&ptp_data->new_adj, NULL);
 
-	time_t tp = ptp_data->new_adj.tv_sec;
+		time_t tp = ptp_data->new_adj.tv_sec;
 
-	printf("\nCurrent SYS Time: %.24s %.6ld ns",
-				ctime(&tp), ptp_data->new_adj.tv_usec);
+		printf("\nCurrent SYS Time: %.24s %.6ld ns",
+			ctime(&tp), ptp_data->new_adj.tv_usec);
 
-	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
-				nsec);
+		printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
+			nsec);
+	}
+
+	if (mode == MODE_PI) {
+		printf("path delay: %"PRId64"ns\n", ptp_data->path_delay);
+		printf("master offset: %"PRId64"ns\n", ptp_data->master_offset);
+	}
 
 	printf("[Ctrl+C to quit]\n");
 
@@ -529,6 +569,149 @@ update_kernel_time(void)
 
 }
 
+static void
+clock_path_delay(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns, t3_ns, t4_ns;
+	int64_t pd, diff;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	t3_ns = timespec64_to_ns(&ptp_data->tstamp3);
+	t4_ns = timespec64_to_ns(&ptp_data->tstamp4);
+
+	pd = (t2_ns - t3_ns) + (t4_ns - t1_ns);
+	diff = t3_ns - t2_ns;
+	if (diff <= INT32_MAX && diff >= INT32_MIN)
+		ptp_data->path_delay = pd / 2;
+	else
+		ptp_data->path_delay = 0;
+}
+
+static double
+pi_sample(struct pi_servo *s, int64_t offset, double local_ts,
+	  enum servo_state *state)
+{
+	double ki_term, ppb = s->last_freq;
+	double freq_est_interval, localdiff;
+
+	switch (s->count) {
+	case 0:
+		s->offset[0] = offset;
+		s->local[0] = local_ts;
+		*state = SERVO_UNLOCKED;
+		s->count = 1;
+		break;
+	case 1:
+		s->offset[1] = offset;
+		s->local[1] = local_ts;
+
+		/* Make sure the first sample is older than the second. */
+		if (s->local[0] >= s->local[1]) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		/* Wait long enough before estimating the frequency offset. */
+		localdiff = (s->local[1] - s->local[0]) / 1e9;
+		localdiff += localdiff * FREQ_EST_MARGIN;
+		freq_est_interval = 0.016 / KI;
+		if (freq_est_interval > 1000.0)
+			freq_est_interval = 1000.0;
+
+		if (localdiff < freq_est_interval) {
+			*state = SERVO_UNLOCKED;
+			break;
+		}
+
+		/* Adjust drift by the measured frequency offset. */
+		s->drift += (1e9 - s->drift) * (s->offset[1] - s->offset[0]) /
+						(s->local[1] - s->local[0]);
+
+		if (s->drift < -s->max_frequency)
+			s->drift = -s->max_frequency;
+		else if (s->drift > s->max_frequency)
+			s->drift = s->max_frequency;
+
+		if ((s->first_update &&
+		     s->first_step_threshold &&
+		     s->first_step_threshold < llabs(offset)) ||
+		    (s->step_threshold &&
+		     s->step_threshold < llabs(offset)))
+			*state = SERVO_JUMP;
+		else
+			*state = SERVO_LOCKED;
+
+		ppb = s->drift;
+		s->count = 2;
+		break;
+	case 2:
+		/*
+		 * reset the clock servo when offset is greater than the max
+		 * offset value. Note that the clock jump will be performed in
+		 * step 1, so it is not necessary to have clock jump
+		 * immediately. This allows re-calculating drift as in initial
+		 * clock startup.
+		 */
+		if (s->step_threshold &&
+		    s->step_threshold < llabs(offset)) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		ki_term = KI * offset;
+		ppb = KP * offset + s->drift + ki_term;
+		if (ppb < -s->max_frequency)
+			ppb = -s->max_frequency;
+		else if (ppb > s->max_frequency)
+			ppb = s->max_frequency;
+		else
+			s->drift += ki_term;
+
+		*state = SERVO_LOCKED;
+		break;
+	}
+
+	s->last_freq = ppb;
+	return ppb;
+}
+
+static void
+ptp_adjust_servo(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns;
+	double adj_freq;
+	enum servo_state state = SERVO_UNLOCKED;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	ptp_data->master_offset = t2_ns - t1_ns - ptp_data->path_delay;
+	if (!ptp_data->path_delay)
+		return;
+
+	adj_freq = pi_sample(ptp_data->servo, ptp_data->master_offset, t2_ns,
+		     &state);
+
+	switch (state) {
+	case SERVO_UNLOCKED:
+		break;
+	case SERVO_JUMP:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+						-(long)(adj_freq * 65.536));
+		rte_eth_timesync_adjust_time(ptp_data->portid,
+					     -ptp_data->master_offset);
+		break;
+	case SERVO_LOCKED:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+					     -(long)(adj_freq * 65.536));
+		break;
+	}
+}
+
 /*
  * Parse the DELAY_RESP message.
  */
@@ -553,11 +736,16 @@ parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
 				((uint64_t)ntohl(rx_tstamp->sec_lsb)) |
 				(((uint64_t)ntohs(rx_tstamp->sec_msb)) << 32);
 
-			/* Evaluate the delta for adjustment. */
-			ptp_data->delta = delta_eval(ptp_data);
+			if (mode == MODE_PI) {
+				clock_path_delay(ptp_data);
+				ptp_adjust_servo(ptp_data);
+			} else {
+				/* Evaluate the delta for adjustment. */
+				ptp_data->delta = delta_eval(ptp_data);
 
-			rte_eth_timesync_adjust_time(ptp_data->portid,
-						     ptp_data->delta);
+				rte_eth_timesync_adjust_time(ptp_data->portid,
+								ptp_data->delta);
+			}
 
 			ptp_data->current_ptp_port = ptp_data->portid;
 
@@ -652,7 +840,9 @@ print_usage(const char *prgname)
 	printf("%s [EAL options] -- -p PORTMASK -T VALUE\n"
 		" -T VALUE: 0 - Disable, 1 - Enable Linux Clock"
 		" Synchronization (0 default)\n"
-		" -p PORTMASK: hexadecimal bitmask of ports to configure\n",
+		" -p PORTMASK: hexadecimal bitmask of ports to configure\n"
+		" -c CONTROLLER: 0 - Not used, 1 - PI. The servo which is"
+		" used to synchronize the local clock. (0 default)\n",
 		prgname);
 }
 
@@ -688,6 +878,36 @@ parse_ptp_kernel(const char *param)
 	return 1;
 }
 
+static int
+parse_ptp_servo_mode(const char *param)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* Parse the hexadecimal string. */
+	pm = strtoul(param, &end, 10);
+
+	if ((param[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	return pm;
+}
+
+static void
+servo_init(struct pi_servo *servo)
+{
+	memset(servo, 0x00, sizeof(*servo));
+
+	servo->drift = 100000000;
+	servo->last_freq = 100000000;
+	servo->count = 0;
+
+	servo->max_frequency = 100000000;
+	servo->step_threshold = 0.1 * NSEC_PER_SEC;
+	servo->first_step_threshold = 0.00002 * NSEC_PER_SEC;
+	servo->first_update = 1;
+}
+
 /* Parse the commandline arguments. */
 static int
 ptp_parse_args(int argc, char **argv)
@@ -700,7 +920,7 @@ ptp_parse_args(int argc, char **argv)
 
 	argvopt = argv;
 
-	while ((opt = getopt_long(argc, argvopt, "p:T:",
+	while ((opt = getopt_long(argc, argvopt, "p:T:c:",
 				  lgopts, &option_index)) != EOF) {
 
 		switch (opt) {
@@ -724,6 +944,17 @@ ptp_parse_args(int argc, char **argv)
 
 			ptp_data.kernel_time_set = ret;
 			break;
+		case 'c':
+			ret = parse_ptp_servo_mode(optarg);
+			if (ret == 0) {
+				mode = MODE_NONE;
+			} else if (ret == 1) {
+				mode = MODE_PI;
+			} else {
+				print_usage(prgname);
+				return -1;
+			}
+			break;
 
 		default:
 			print_usage(prgname);
@@ -778,6 +1009,14 @@ main(int argc, char *argv[])
 		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
 	/* >8 End of parsing specific arguments. */
 
+	if (mode == MODE_PI) {
+		ptp_data.servo = malloc(sizeof(*(ptp_data.servo)));
+		if (!ptp_data.servo)
+			rte_exit(EXIT_FAILURE, "no memory for servo\n");
+
+		servo_init(ptp_data.servo);
+	}
+
 	/* Check that there is an even number of ports to send/receive on. */
 	nb_ports = rte_eth_dev_count_avail();
 
@@ -831,6 +1070,9 @@ main(int argc, char *argv[])
 		rte_eth_dev_close(portid);
 	}
 
+	if (mode == MODE_PI)
+		free(ptp_data.servo);
+
 	/* clean up the EAL */
 	rte_eal_cleanup();
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v6 0/3] add frequency adjustment support for PTP
  2024-10-11  2:53         ` [PATCH v5 0/3] add frequency adjustment support for PTP Mingjin Ye
                             ` (2 preceding siblings ...)
  2024-10-11  2:53           ` [PATCH v5 3/3] examples/ptpclient: add frequency adjustment Mingjin Ye
@ 2024-10-11  6:34           ` Mingjin Ye
  2024-10-11  6:34             ` [PATCH v6 1/3] ethdev: add frequency adjustment API Mingjin Ye
                               ` (3 more replies)
  3 siblings, 4 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-10-11  6:34 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye

[1/3] ethdev: add frequency adjustment API
[2/3] net/ice: add frequency adjustment support for PTP
[3/3] examples/ptpclient: add frequency adjustment
---
v2: rte_eth_timesync_adjust_freq marked as experimental.
---
v3: Add more description for API.
---
v4: Documentation for adding a ptpclient.
---
v5: Fixes in ice.
---
v6: Fix git commit conflict.

Mingjin Ye (3):
  ethdev: add frequency adjustment API
  net/ice: add frequency adjustment support for PTP
  examples/ptpclient: add frequency adjustment

 doc/guides/nics/features.rst           |   4 +-
 doc/guides/nics/ice.rst                |  16 ++
 doc/guides/rel_notes/release_24_11.rst |   5 +
 doc/guides/sample_app_ug/ptpclient.rst |  12 +-
 drivers/net/ice/ice_ethdev.c           | 176 +++++++++++----
 drivers/net/ice/ice_ethdev.h           |   5 +-
 drivers/net/ice/ice_rxtx.c             |   4 +-
 examples/ptpclient/ptpclient.c         | 284 +++++++++++++++++++++++--
 lib/ethdev/ethdev_driver.h             |   5 +
 lib/ethdev/ethdev_trace.h              |   9 +
 lib/ethdev/ethdev_trace_points.c       |   3 +
 lib/ethdev/rte_ethdev.c                |  18 ++
 lib/ethdev/rte_ethdev.h                |  43 ++++
 lib/ethdev/version.map                 |   3 +
 14 files changed, 516 insertions(+), 71 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v6 1/3] ethdev: add frequency adjustment API
  2024-10-11  6:34           ` [PATCH v6 0/3] add frequency adjustment support for PTP Mingjin Ye
@ 2024-10-11  6:34             ` Mingjin Ye
  2024-10-11 23:44               ` Ferruh Yigit
  2024-10-11  6:34             ` [PATCH v6 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
                               ` (2 subsequent siblings)
  3 siblings, 1 reply; 49+ messages in thread
From: Mingjin Ye @ 2024-10-11  6:34 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su, Thomas Monjalon, Ferruh Yigit, Andrew Rybchenko

This patch adds freq adjustment API for PTP high accuracy.

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
v2: rte_eth_timesync_adjust_freq marked as experimental
---
v3: Add more description for API.
---
V4: Fix git commit conflict.
---
 doc/guides/nics/features.rst           |  4 ++-
 doc/guides/rel_notes/release_24_11.rst |  5 +++
 lib/ethdev/ethdev_driver.h             |  5 +++
 lib/ethdev/ethdev_trace.h              |  9 ++++++
 lib/ethdev/ethdev_trace_points.c       |  3 ++
 lib/ethdev/rte_ethdev.c                | 18 +++++++++++
 lib/ethdev/rte_ethdev.h                | 43 ++++++++++++++++++++++++++
 lib/ethdev/version.map                 |  3 ++
 8 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/doc/guides/nics/features.rst b/doc/guides/nics/features.rst
index cd0115ffb3..0508f118fe 100644
--- a/doc/guides/nics/features.rst
+++ b/doc/guides/nics/features.rst
@@ -677,10 +677,12 @@ Supports IEEE1588/802.1AS timestamping.
 
 * **[implements] eth_dev_ops**: ``timesync_enable``, ``timesync_disable``
   ``timesync_read_rx_timestamp``, ``timesync_read_tx_timestamp``,
-  ``timesync_adjust_time``, ``timesync_read_time``, ``timesync_write_time``.
+  ``timesync_adjust_time``, ``timesync_adjust_freq``,
+  ``timesync_read_time``, ``timesync_write_time``.
 * **[related]    API**: ``rte_eth_timesync_enable()``, ``rte_eth_timesync_disable()``,
   ``rte_eth_timesync_read_rx_timestamp()``,
   ``rte_eth_timesync_read_tx_timestamp``, ``rte_eth_timesync_adjust_time()``,
+  ``rte_eth_timesync_adjust_freq()``,
   ``rte_eth_timesync_read_time()``, ``rte_eth_timesync_write_time()``.
 
 
diff --git a/doc/guides/rel_notes/release_24_11.rst b/doc/guides/rel_notes/release_24_11.rst
index 53d8661365..7e9b37f636 100644
--- a/doc/guides/rel_notes/release_24_11.rst
+++ b/doc/guides/rel_notes/release_24_11.rst
@@ -142,6 +142,11 @@ New Features
   Applications should use ``RTE_EVENT_PORT_CFG_INDEPENDENT_ENQ`` to enable
   the feature if the capability ``RTE_EVENT_DEV_CAP_INDEPENDENT_ENQ`` exists.
 
+* **Added Ethernet device clock frequency adjustment.**
+
+  Added new function ``rte_eth_timesync_adjust_freq`` to adjust the clock
+  frequency for Ethernet devices.
+
 * **Updated DLB2 event driver.**
 
   * Added independent enqueue feature.
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index ae00ead865..ec1a9d1cc7 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -664,6 +664,9 @@ typedef int (*eth_timesync_read_tx_timestamp_t)(struct rte_eth_dev *dev,
 /** @internal Function used to adjust the device clock. */
 typedef int (*eth_timesync_adjust_time)(struct rte_eth_dev *dev, int64_t);
 
+/** @internal Function used to adjust the clock frequency. */
+typedef int (*eth_timesync_adjust_freq)(struct rte_eth_dev *dev, int64_t);
+
 /** @internal Function used to get time from the device clock. */
 typedef int (*eth_timesync_read_time)(struct rte_eth_dev *dev,
 				      struct timespec *timestamp);
@@ -1378,6 +1381,8 @@ struct eth_dev_ops {
 	eth_timesync_read_tx_timestamp_t timesync_read_tx_timestamp;
 	/** Adjust the device clock */
 	eth_timesync_adjust_time   timesync_adjust_time;
+	/** Adjust the clock frequency */
+	eth_timesync_adjust_freq   timesync_adjust_freq;
 	/** Get the device clock time */
 	eth_timesync_read_time     timesync_read_time;
 	/** Set the device clock time */
diff --git a/lib/ethdev/ethdev_trace.h b/lib/ethdev/ethdev_trace.h
index 36a38f718a..598e9f4d35 100644
--- a/lib/ethdev/ethdev_trace.h
+++ b/lib/ethdev/ethdev_trace.h
@@ -2183,6 +2183,15 @@ RTE_TRACE_POINT_FP(
 	rte_trace_point_emit_int(ret);
 )
 
+/* Called in loop in examples/ptpclient */
+RTE_TRACE_POINT_FP(
+	rte_eth_trace_timesync_adjust_freq,
+	RTE_TRACE_POINT_ARGS(uint16_t port_id, int64_t ppm, int ret),
+	rte_trace_point_emit_u16(port_id);
+	rte_trace_point_emit_i64(ppm);
+	rte_trace_point_emit_int(ret);
+)
+
 /* Called in loop in app/test-flow-perf */
 RTE_TRACE_POINT_FP(
 	rte_flow_trace_create,
diff --git a/lib/ethdev/ethdev_trace_points.c b/lib/ethdev/ethdev_trace_points.c
index 99e04f5893..a99fec0c1e 100644
--- a/lib/ethdev/ethdev_trace_points.c
+++ b/lib/ethdev/ethdev_trace_points.c
@@ -409,6 +409,9 @@ RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_tx_timestamp,
 RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_time,
 	lib.ethdev.timesync_adjust_time)
 
+RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_adjust_freq,
+	lib.ethdev.timesync_adjust_freq)
+
 RTE_TRACE_POINT_REGISTER(rte_eth_trace_timesync_read_time,
 	lib.ethdev.timesync_read_time)
 
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index f1c658f49e..660eab2f1e 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -6310,6 +6310,24 @@ rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta)
 	return ret;
 }
 
+int
+rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm)
+{
+	struct rte_eth_dev *dev;
+	int ret;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (*dev->dev_ops->timesync_adjust_freq == NULL)
+		return -ENOTSUP;
+	ret = eth_err(port_id, (*dev->dev_ops->timesync_adjust_freq)(dev, ppm));
+
+	rte_eth_trace_timesync_adjust_freq(port_id, ppm, ret);
+
+	return ret;
+}
+
 int
 rte_eth_timesync_read_time(uint16_t port_id, struct timespec *timestamp)
 {
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index a75e26bf07..c8a9993ad6 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -5293,6 +5293,49 @@ int rte_eth_timesync_read_tx_timestamp(uint16_t port_id,
  */
 int rte_eth_timesync_adjust_time(uint16_t port_id, int64_t delta);
 
+/**
+ * Adjust the clock frequency on an Ethernet device.
+ *
+ * Adjusts the base frequency by a specified percentage of ppm (parts per
+ * million). This is usually used in conjunction with other Ethdev timesync
+ * functions to synchronize the device time using the IEEE1588/802.1AS
+ * protocol.
+ *
+ * The clock is subject to frequency deviation and rate of change drift due to
+ * the environment. The upper layer APP calculates the frequency compensation
+ * value of the slave clock relative to the master clock via a servo algorithm
+ * and adjusts the device clock frequency via "rte_eth_timesync_adjust_freq()".
+ * Commonly used servo algorithms are pi/linreg/ntpshm, for implementation
+ * see: https://github.com/nxp-archive/openil_linuxptp.git.
+ *
+ * The adjustment value obtained by the servo algorithm is usually in
+ * ppb (parts per billion). For consistency with the kernel driver .adjfine,
+ * the tuning values are in ppm. Note that 1 ppb is approximately 65.536 scaled
+ * ppm, see Linux kernel upstream commit 1060707e3809 (‘ptp: introduce helpers
+ * to adjust by scaled parts per million’).
+ *
+ * In addition, the device reference frequency is usually also the stepping
+ * threshold for the servo algorithm, and the frequency up and down adjustment
+ * range is limited by the device. The device clock frequency should be
+ * adjusted with "rte_eth_timesync_adjust_freq()" every time the clock is
+ * synchronised. Also use ‘rte_eth_timesync_adjust_time()’ to update the device
+ * clock only if the absolute value of the master/slave clock offset is greater than
+ * or equal to the step threshold.
+ *
+ * @param port_id
+ *  The port identifier of the Ethernet device.
+ * @param ppm
+ *  Parts per million with 16-bit fractional field
+ *
+ * @return
+ *   - 0: Success.
+ *   - -ENODEV: The port ID is invalid.
+ *   - -EIO: if device is removed.
+ *   - -ENOTSUP: The function is not supported by the Ethernet driver.
+ */
+__rte_experimental
+int rte_eth_timesync_adjust_freq(uint16_t port_id, int64_t ppm);
+
 /**
  * Read the time from the timesync clock on an Ethernet device.
  *
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 1669055ca5..199480ef07 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -325,6 +325,9 @@ EXPERIMENTAL {
 	rte_flow_template_table_resizable;
 	rte_flow_template_table_resize;
 	rte_flow_template_table_resize_complete;
+
+	# added in 24.11
+	rte_eth_timesync_adjust_freq;
 };
 
 INTERNAL {
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v6 2/3] net/ice: add frequency adjustment support for PTP
  2024-10-11  6:34           ` [PATCH v6 0/3] add frequency adjustment support for PTP Mingjin Ye
  2024-10-11  6:34             ` [PATCH v6 1/3] ethdev: add frequency adjustment API Mingjin Ye
@ 2024-10-11  6:34             ` Mingjin Ye
  2024-10-11  8:02               ` Bruce Richardson
  2024-10-11  6:34             ` [PATCH v6 3/3] examples/ptpclient: add frequency adjustment Mingjin Ye
  2024-10-11 23:44             ` [PATCH v6 0/3] add frequency adjustment support for PTP Ferruh Yigit
  3 siblings, 1 reply; 49+ messages in thread
From: Mingjin Ye @ 2024-10-11  6:34 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su

Add ice support for new ethdev API to adjust frequency for IEEE1588
PTP. Also, this patch reworks code for converting software update
to hardware update.

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
 doc/guides/nics/ice.rst      |  16 ++++
 drivers/net/ice/ice_ethdev.c | 176 ++++++++++++++++++++++++++---------
 drivers/net/ice/ice_ethdev.h |   5 +-
 drivers/net/ice/ice_rxtx.c   |   4 +-
 4 files changed, 153 insertions(+), 48 deletions(-)

diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
index ae975d19ad..061c8c7a20 100644
--- a/doc/guides/nics/ice.rst
+++ b/doc/guides/nics/ice.rst
@@ -328,6 +328,22 @@ Forward Error Correction (FEC)
 
 Supports get/set FEC mode and get FEC capability.
 
+Time Synchronisation
+~~~~~~~~~~~~~~~~~~~~
+
+The system operator can run a PTP (Precision Time Protocol) client application
+to synchronise the time on the network card (and optionally the time on the
+system) to the PTP master.
+
+ICE PMD supports PTP client applications that use the DPDK IEEE1588 API to
+communicate with the PTP master clock. Note that PTP client application needs
+to run on PF and add the ``--force-max-simd-bitwidth=64`` startup parameter to
+disable vector mode.
+
+.. code-block:: console
+
+    examples/dpdk-ptpclient -c f -n 3 -a 0000:ec:00.1 --force-max-simd-bitwidth=64 -- -T 1 -p 0x1 -c 1
+
 Generic Flow Support
 ~~~~~~~~~~~~~~~~~~~~
 
diff --git a/drivers/net/ice/ice_ethdev.c b/drivers/net/ice/ice_ethdev.c
index 7b1bd163a2..0e1d332ef3 100644
--- a/drivers/net/ice/ice_ethdev.c
+++ b/drivers/net/ice/ice_ethdev.c
@@ -10,6 +10,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <math.h>
 
 #include <rte_tailq.h>
 #include <rte_os_shim.h>
@@ -176,6 +177,7 @@ static int ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 static int ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					  struct timespec *timestamp);
 static int ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta);
+static int ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm);
 static int ice_timesync_read_time(struct rte_eth_dev *dev,
 				  struct timespec *timestamp);
 static int ice_timesync_write_time(struct rte_eth_dev *dev,
@@ -307,6 +309,7 @@ static const struct eth_dev_ops ice_eth_dev_ops = {
 	.timesync_read_rx_timestamp   = ice_timesync_read_rx_timestamp,
 	.timesync_read_tx_timestamp   = ice_timesync_read_tx_timestamp,
 	.timesync_adjust_time         = ice_timesync_adjust_time,
+	.timesync_adjust_freq         = ice_timesync_adjust_freq,
 	.timesync_read_time           = ice_timesync_read_time,
 	.timesync_write_time          = ice_timesync_write_time,
 	.timesync_disable             = ice_timesync_disable,
@@ -2332,6 +2335,33 @@ ice_get_supported_rxdid(struct ice_hw *hw)
 	return supported_rxdid;
 }
 
+static void ice_ptp_init_info(struct rte_eth_dev *dev)
+{
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ice_adapter *ad =
+		ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+
+	switch (hw->phy_model) {
+	case ICE_PHY_ETH56G:
+		ad->ptp_tx_block = hw->pf_id;
+		ad->ptp_tx_index = 0;
+		break;
+	case ICE_PHY_E810:
+	case ICE_PHY_E830:
+		ad->ptp_tx_block = hw->port_info->lport;
+		ad->ptp_tx_index = 0;
+		break;
+	case ICE_PHY_E822:
+		ad->ptp_tx_block = hw->pf_id / ICE_PORTS_PER_QUAD;
+		ad->ptp_tx_index = (hw->pf_id % ICE_PORTS_PER_QUAD) *
+				ICE_PORTS_PER_PHY_E822 * ICE_QUADS_PER_PHY_E822;
+		break;
+	default:
+		PMD_DRV_LOG(WARNING, "Unsupported PHY model");
+		break;
+	}
+}
+
 static int
 ice_dev_init(struct rte_eth_dev *dev)
 {
@@ -2499,6 +2529,9 @@ ice_dev_init(struct rte_eth_dev *dev)
 	/* Initialize PHY model */
 	ice_ptp_init_phy_model(hw);
 
+	/* Initialize PTP info */
+	ice_ptp_init_info(dev);
+
 	if (hw->phy_model == ICE_PHY_E822) {
 		ret = ice_start_phy_timer_e822(hw, hw->pf_id);
 		if (ret)
@@ -6466,23 +6499,6 @@ ice_timesync_enable(struct rte_eth_dev *dev)
 		}
 	}
 
-	/* Initialize cycle counters for system time/RX/TX timestamp */
-	memset(&ad->systime_tc, 0, sizeof(struct rte_timecounter));
-	memset(&ad->rx_tstamp_tc, 0, sizeof(struct rte_timecounter));
-	memset(&ad->tx_tstamp_tc, 0, sizeof(struct rte_timecounter));
-
-	ad->systime_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->systime_tc.cc_shift = 0;
-	ad->systime_tc.nsec_mask = 0;
-
-	ad->rx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->rx_tstamp_tc.cc_shift = 0;
-	ad->rx_tstamp_tc.nsec_mask = 0;
-
-	ad->tx_tstamp_tc.cc_mask = ICE_CYCLECOUNTER_MASK;
-	ad->tx_tstamp_tc.cc_shift = 0;
-	ad->tx_tstamp_tc.nsec_mask = 0;
-
 	ad->ptp_ena = 1;
 
 	return 0;
@@ -6497,14 +6513,13 @@ ice_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	struct ice_rx_queue *rxq;
 	uint32_t ts_high;
-	uint64_t ts_ns, ns;
+	uint64_t ts_ns;
 
 	rxq = dev->data->rx_queues[flags];
 
 	ts_high = rxq->time_high;
 	ts_ns = ice_tstamp_convert_32b_64b(hw, ad, 1, ts_high);
-	ns = rte_timecounter_update(&ad->rx_tstamp_tc, ts_ns);
-	*timestamp = rte_ns_to_timespec(ns);
+	*timestamp = rte_ns_to_timespec(ts_ns);
 
 	return 0;
 }
@@ -6516,22 +6531,18 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	struct ice_adapter *ad =
 			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
-	uint8_t lport;
-	uint64_t ts_ns, ns, tstamp;
+	uint64_t ts_ns, tstamp;
 	const uint64_t mask = 0xFFFFFFFF;
 	int ret;
 
-	lport = hw->port_info->lport;
-
-	ret = ice_read_phy_tstamp(hw, lport, 0, &tstamp);
-	if (ret) {
+	ret = ice_read_phy_tstamp(hw, ad->ptp_tx_block, ad->ptp_tx_index, &tstamp);
+	if (ret || tstamp == 0) {
 		PMD_DRV_LOG(ERR, "Failed to read phy timestamp");
 		return -1;
 	}
 
 	ts_ns = ice_tstamp_convert_32b_64b(hw, ad, 1, (tstamp >> 8) & mask);
-	ns = rte_timecounter_update(&ad->tx_tstamp_tc, ts_ns);
-	*timestamp = rte_ns_to_timespec(ns);
+	*timestamp = rte_ns_to_timespec(ts_ns);
 
 	return 0;
 }
@@ -6539,28 +6550,108 @@ ice_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 static int
 ice_timesync_adjust_time(struct rte_eth_dev *dev, int64_t delta)
 {
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint8_t tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc;
+	uint32_t lo, lo2, hi;
+	uint64_t time, ns;
+	int ret;
+
+	if (delta > INT32_MAX || delta < INT32_MIN) {
+		lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+		hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
+		lo2 = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+
+		if (lo2 < lo) {
+			lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
+			hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
+		}
+
+		time = ((uint64_t)hi << 32) | lo;
+		ns = time + delta;
+
+		wr32(hw, GLTSYN_SHTIME_L(tmr_idx), ICE_LO_DWORD(ns));
+		wr32(hw, GLTSYN_SHTIME_H(tmr_idx), ICE_HI_DWORD(ns));
+		wr32(hw, GLTSYN_SHTIME_0(tmr_idx), 0);
+
+		ret = ice_ptp_init_time(hw, ns, true);
+		if (ret) {
+			PMD_DRV_LOG(ERR, "PTP init time failed, err %d", ret);
+			return -1;
+		}
+		return 0;
+	}
+
+	ret = ice_ptp_adj_clock(hw, delta, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP adj clock failed, err %d", ret);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+ice_timesync_adjust_freq(struct rte_eth_dev *dev, int64_t ppm)
+{
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	int64_t incval, diff = 0;
+	bool negative = false;
+	uint64_t div, rem;
+	uint64_t divisor = 1000000ULL << 16;
+	int shift;
+	int ret;
+
+	incval = ice_get_base_incval(hw, ICE_SRC_TMR_MODE_NANOSECONDS);
+
+	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;
 
-	ad->systime_tc.nsec += delta;
-	ad->rx_tstamp_tc.nsec += delta;
-	ad->tx_tstamp_tc.nsec += delta;
+		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 = ice_ptp_write_incval_locked(hw, incval, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP failed to set incval, err %d", ret);
+		return -1;
+	}
 	return 0;
 }
 
 static int
 ice_timesync_write_time(struct rte_eth_dev *dev, const struct timespec *ts)
 {
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	uint64_t ns;
+	int ret;
 
 	ns = rte_timespec_to_ns(ts);
-
-	ad->systime_tc.nsec = ns;
-	ad->rx_tstamp_tc.nsec = ns;
-	ad->tx_tstamp_tc.nsec = ns;
+	ret = ice_ptp_init_time(hw, ns, true);
+	if (ret) {
+		PMD_DRV_LOG(ERR, "PTP init time failed, err %d", ret);
+		return -1;
+	}
 
 	return 0;
 }
@@ -6569,11 +6660,9 @@ static int
 ice_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
 {
 	struct ice_hw *hw = ICE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-	struct ice_adapter *ad =
-			ICE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	uint8_t tmr_idx = hw->func_caps.ts_func_info.tmr_index_assoc;
 	uint32_t hi, lo, lo2;
-	uint64_t time, ns;
+	uint64_t time;
 
 	lo = ICE_READ_REG(hw, GLTSYN_TIME_L(tmr_idx));
 	hi = ICE_READ_REG(hw, GLTSYN_TIME_H(tmr_idx));
@@ -6585,8 +6674,7 @@ ice_timesync_read_time(struct rte_eth_dev *dev, struct timespec *ts)
 	}
 
 	time = ((uint64_t)hi << 32) | lo;
-	ns = rte_timecounter_update(&ad->systime_tc, time);
-	*ts = rte_ns_to_timespec(ns);
+	*ts = rte_ns_to_timespec(time);
 
 	return 0;
 }
diff --git a/drivers/net/ice/ice_ethdev.h b/drivers/net/ice/ice_ethdev.h
index 3ea9f37dc8..174ab0c937 100644
--- a/drivers/net/ice/ice_ethdev.h
+++ b/drivers/net/ice/ice_ethdev.h
@@ -611,9 +611,8 @@ struct ice_adapter {
 	/* For vector PMD */
 	eth_rx_burst_t tx_pkt_burst;
 	/* For PTP */
-	struct rte_timecounter systime_tc;
-	struct rte_timecounter rx_tstamp_tc;
-	struct rte_timecounter tx_tstamp_tc;
+	uint8_t ptp_tx_block;
+	uint8_t ptp_tx_index;
 	bool ptp_ena;
 	uint64_t time_hw;
 	struct ice_fdir_prof_info fdir_prof_info[ICE_MAX_PTGS];
diff --git a/drivers/net/ice/ice_rxtx.c b/drivers/net/ice/ice_rxtx.c
index acd7539b5e..d2f9edc221 100644
--- a/drivers/net/ice/ice_rxtx.c
+++ b/drivers/net/ice/ice_rxtx.c
@@ -3066,7 +3066,9 @@ ice_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
 			else if (ol_flags & RTE_MBUF_F_TX_IEEE1588_TMST)
 				cd_type_cmd_tso_mss |=
 					((uint64_t)ICE_TX_CTX_DESC_TSYN <<
-					ICE_TXD_CTX_QW1_CMD_S);
+					ICE_TXD_CTX_QW1_CMD_S) |
+					 (((uint64_t)txq->vsi->adapter->ptp_tx_index <<
+					 ICE_TXD_CTX_QW1_TSYN_S) & ICE_TXD_CTX_QW1_TSYN_M);
 
 			ctx_txd->tunneling_params =
 				rte_cpu_to_le_32(cd_tunneling_params);
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v6 3/3] examples/ptpclient: add frequency adjustment
  2024-10-11  6:34           ` [PATCH v6 0/3] add frequency adjustment support for PTP Mingjin Ye
  2024-10-11  6:34             ` [PATCH v6 1/3] ethdev: add frequency adjustment API Mingjin Ye
  2024-10-11  6:34             ` [PATCH v6 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
@ 2024-10-11  6:34             ` Mingjin Ye
  2024-10-11 19:37               ` Ferruh Yigit
  2024-10-15  8:22               ` [PATCH v7] " Mingjin Ye
  2024-10-11 23:44             ` [PATCH v6 0/3] add frequency adjustment support for PTP Ferruh Yigit
  3 siblings, 2 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-10-11  6:34 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su, Wenjun Wu, Kirill Rybalchenko

This patch adds PI servo controller to support frequency
adjustment API for IEEE1588 PTP.

For example, the command for starting ptpclient with PI controller is:
dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1 -c 1

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
 doc/guides/sample_app_ug/ptpclient.rst |  12 +-
 examples/ptpclient/ptpclient.c         | 284 +++++++++++++++++++++++--
 2 files changed, 274 insertions(+), 22 deletions(-)

diff --git a/doc/guides/sample_app_ug/ptpclient.rst b/doc/guides/sample_app_ug/ptpclient.rst
index d47e942738..89fe575b5f 100644
--- a/doc/guides/sample_app_ug/ptpclient.rst
+++ b/doc/guides/sample_app_ug/ptpclient.rst
@@ -50,6 +50,10 @@ The adjustment for slave can be represented as:
 If the command line parameter ``-T 1`` is used the application also
 synchronizes the PTP PHC clock with the Linux kernel clock.
 
+If the command line parameter ``-c 1`` is used, the application will also
+use the servo of the local clock. Only one type of servo is currently
+implemented, the PI controller. Default 0 (not used).
+
 Compiling the Application
 -------------------------
 
@@ -65,7 +69,7 @@ To run the example in a ``linux`` environment:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-ptpclient -l 1 -n 4 -- -p 0x1 -T 0
+    ./<build_dir>/examples/dpdk-ptpclient -l 1 -n 4 -- -p 0x1 -T 0 -c 1
 
 Refer to *DPDK Getting Started Guide* for general information on running
 applications and the Environment Abstraction Layer (EAL) options.
@@ -73,7 +77,13 @@ applications and the Environment Abstraction Layer (EAL) options.
 * ``-p portmask``: Hexadecimal portmask.
 * ``-T 0``: Update only the PTP slave clock.
 * ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the PTP clock.
+* ``-c 0``: Not used clock servo controller.
+* ``-c 1``: The clock servo PI controller is used and the log will print information
+            about "master offset".
 
+Also, by adding ``-T 1`` and ``-c 1`` , the ``master offset`` value printed in the
+log will slowly converge and eventually stabilise at the nanosecond level. The
+synchronisation accuracy is much higher compared to not using a servo controller.
 
 Code Explanation
 ----------------
diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
index afb61bba51..dea8d9d54a 100644
--- a/examples/ptpclient/ptpclient.c
+++ b/examples/ptpclient/ptpclient.c
@@ -46,6 +46,35 @@ static volatile bool force_quit;
 #define KERNEL_TIME_ADJUST_LIMIT  20000
 #define PTP_PROTOCOL             0x88F7
 
+#define KP 0.7
+#define KI 0.3
+#define FREQ_EST_MARGIN 0.001
+
+enum servo_state {
+	SERVO_UNLOCKED,
+	SERVO_JUMP,
+	SERVO_LOCKED,
+};
+
+struct pi_servo {
+	double offset[2];
+	double local[2];
+	double drift;
+	double last_freq;
+	int count;
+
+	double max_frequency;
+	double step_threshold;
+	double first_step_threshold;
+	int first_update;
+};
+
+enum controller_mode {
+	MODE_NONE,
+	MODE_PI,
+	MAX_ALL
+} mode = MODE_NONE;
+
 struct rte_mempool *mbuf_pool;
 uint32_t ptp_enabled_port_mask;
 uint8_t ptp_enabled_port_nb;
@@ -135,6 +164,9 @@ struct ptpv2_data_slave_ordinary {
 	uint8_t ptpset;
 	uint8_t kernel_time_set;
 	uint16_t current_ptp_port;
+	int64_t master_offset;
+	int64_t path_delay;
+	struct pi_servo *servo;
 };
 
 static struct ptpv2_data_slave_ordinary ptp_data;
@@ -293,36 +325,44 @@ print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
 			ptp_data->tstamp3.tv_sec,
 			(ptp_data->tstamp3.tv_nsec));
 
-	printf("\nT4 - Master Clock.  %lds %ldns ",
+	printf("\nT4 - Master Clock.  %lds %ldns\n",
 			ptp_data->tstamp4.tv_sec,
 			(ptp_data->tstamp4.tv_nsec));
 
-	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+	if (mode == MODE_NONE) {
+		printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
 			ptp_data->delta);
 
-	clock_gettime(CLOCK_REALTIME, &sys_time);
-	rte_eth_timesync_read_time(ptp_data->current_ptp_port, &net_time);
+		clock_gettime(CLOCK_REALTIME, &sys_time);
+		rte_eth_timesync_read_time(ptp_data->current_ptp_port,
+					   &net_time);
 
-	time_t ts = net_time.tv_sec;
+		time_t ts = net_time.tv_sec;
 
-	printf("\n\nComparison between Linux kernel Time and PTP:");
+		printf("\n\nComparison between Linux kernel Time and PTP:");
 
-	printf("\nCurrent PTP Time: %.24s %.9ld ns",
+		printf("\nCurrent PTP Time: %.24s %.9ld ns",
 			ctime(&ts), net_time.tv_nsec);
 
-	nsec = (int64_t)timespec64_to_ns(&net_time) -
+		nsec = (int64_t)timespec64_to_ns(&net_time) -
 			(int64_t)timespec64_to_ns(&sys_time);
-	ptp_data->new_adj = ns_to_timeval(nsec);
+		ptp_data->new_adj = ns_to_timeval(nsec);
 
-	gettimeofday(&ptp_data->new_adj, NULL);
+		gettimeofday(&ptp_data->new_adj, NULL);
 
-	time_t tp = ptp_data->new_adj.tv_sec;
+		time_t tp = ptp_data->new_adj.tv_sec;
 
-	printf("\nCurrent SYS Time: %.24s %.6ld ns",
-				ctime(&tp), ptp_data->new_adj.tv_usec);
+		printf("\nCurrent SYS Time: %.24s %.6ld ns",
+			ctime(&tp), ptp_data->new_adj.tv_usec);
 
-	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
-				nsec);
+		printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
+			nsec);
+	}
+
+	if (mode == MODE_PI) {
+		printf("path delay: %"PRId64"ns\n", ptp_data->path_delay);
+		printf("master offset: %"PRId64"ns\n", ptp_data->master_offset);
+	}
 
 	printf("[Ctrl+C to quit]\n");
 
@@ -529,6 +569,149 @@ update_kernel_time(void)
 
 }
 
+static void
+clock_path_delay(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns, t3_ns, t4_ns;
+	int64_t pd, diff;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	t3_ns = timespec64_to_ns(&ptp_data->tstamp3);
+	t4_ns = timespec64_to_ns(&ptp_data->tstamp4);
+
+	pd = (t2_ns - t3_ns) + (t4_ns - t1_ns);
+	diff = t3_ns - t2_ns;
+	if (diff <= INT32_MAX && diff >= INT32_MIN)
+		ptp_data->path_delay = pd / 2;
+	else
+		ptp_data->path_delay = 0;
+}
+
+static double
+pi_sample(struct pi_servo *s, int64_t offset, double local_ts,
+	  enum servo_state *state)
+{
+	double ki_term, ppb = s->last_freq;
+	double freq_est_interval, localdiff;
+
+	switch (s->count) {
+	case 0:
+		s->offset[0] = offset;
+		s->local[0] = local_ts;
+		*state = SERVO_UNLOCKED;
+		s->count = 1;
+		break;
+	case 1:
+		s->offset[1] = offset;
+		s->local[1] = local_ts;
+
+		/* Make sure the first sample is older than the second. */
+		if (s->local[0] >= s->local[1]) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		/* Wait long enough before estimating the frequency offset. */
+		localdiff = (s->local[1] - s->local[0]) / 1e9;
+		localdiff += localdiff * FREQ_EST_MARGIN;
+		freq_est_interval = 0.016 / KI;
+		if (freq_est_interval > 1000.0)
+			freq_est_interval = 1000.0;
+
+		if (localdiff < freq_est_interval) {
+			*state = SERVO_UNLOCKED;
+			break;
+		}
+
+		/* Adjust drift by the measured frequency offset. */
+		s->drift += (1e9 - s->drift) * (s->offset[1] - s->offset[0]) /
+						(s->local[1] - s->local[0]);
+
+		if (s->drift < -s->max_frequency)
+			s->drift = -s->max_frequency;
+		else if (s->drift > s->max_frequency)
+			s->drift = s->max_frequency;
+
+		if ((s->first_update &&
+		     s->first_step_threshold &&
+		     s->first_step_threshold < llabs(offset)) ||
+		    (s->step_threshold &&
+		     s->step_threshold < llabs(offset)))
+			*state = SERVO_JUMP;
+		else
+			*state = SERVO_LOCKED;
+
+		ppb = s->drift;
+		s->count = 2;
+		break;
+	case 2:
+		/*
+		 * reset the clock servo when offset is greater than the max
+		 * offset value. Note that the clock jump will be performed in
+		 * step 1, so it is not necessary to have clock jump
+		 * immediately. This allows re-calculating drift as in initial
+		 * clock startup.
+		 */
+		if (s->step_threshold &&
+		    s->step_threshold < llabs(offset)) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		ki_term = KI * offset;
+		ppb = KP * offset + s->drift + ki_term;
+		if (ppb < -s->max_frequency)
+			ppb = -s->max_frequency;
+		else if (ppb > s->max_frequency)
+			ppb = s->max_frequency;
+		else
+			s->drift += ki_term;
+
+		*state = SERVO_LOCKED;
+		break;
+	}
+
+	s->last_freq = ppb;
+	return ppb;
+}
+
+static void
+ptp_adjust_servo(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns;
+	double adj_freq;
+	enum servo_state state = SERVO_UNLOCKED;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	ptp_data->master_offset = t2_ns - t1_ns - ptp_data->path_delay;
+	if (!ptp_data->path_delay)
+		return;
+
+	adj_freq = pi_sample(ptp_data->servo, ptp_data->master_offset, t2_ns,
+		     &state);
+
+	switch (state) {
+	case SERVO_UNLOCKED:
+		break;
+	case SERVO_JUMP:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+						-(long)(adj_freq * 65.536));
+		rte_eth_timesync_adjust_time(ptp_data->portid,
+					     -ptp_data->master_offset);
+		break;
+	case SERVO_LOCKED:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+					     -(long)(adj_freq * 65.536));
+		break;
+	}
+}
+
 /*
  * Parse the DELAY_RESP message.
  */
@@ -553,11 +736,16 @@ parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
 				((uint64_t)ntohl(rx_tstamp->sec_lsb)) |
 				(((uint64_t)ntohs(rx_tstamp->sec_msb)) << 32);
 
-			/* Evaluate the delta for adjustment. */
-			ptp_data->delta = delta_eval(ptp_data);
+			if (mode == MODE_PI) {
+				clock_path_delay(ptp_data);
+				ptp_adjust_servo(ptp_data);
+			} else {
+				/* Evaluate the delta for adjustment. */
+				ptp_data->delta = delta_eval(ptp_data);
 
-			rte_eth_timesync_adjust_time(ptp_data->portid,
-						     ptp_data->delta);
+				rte_eth_timesync_adjust_time(ptp_data->portid,
+								ptp_data->delta);
+			}
 
 			ptp_data->current_ptp_port = ptp_data->portid;
 
@@ -652,7 +840,9 @@ print_usage(const char *prgname)
 	printf("%s [EAL options] -- -p PORTMASK -T VALUE\n"
 		" -T VALUE: 0 - Disable, 1 - Enable Linux Clock"
 		" Synchronization (0 default)\n"
-		" -p PORTMASK: hexadecimal bitmask of ports to configure\n",
+		" -p PORTMASK: hexadecimal bitmask of ports to configure\n"
+		" -c CONTROLLER: 0 - Not used, 1 - PI. The servo which is"
+		" used to synchronize the local clock. (0 default)\n",
 		prgname);
 }
 
@@ -688,6 +878,36 @@ parse_ptp_kernel(const char *param)
 	return 1;
 }
 
+static int
+parse_ptp_servo_mode(const char *param)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* Parse the hexadecimal string. */
+	pm = strtoul(param, &end, 10);
+
+	if ((param[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	return pm;
+}
+
+static void
+servo_init(struct pi_servo *servo)
+{
+	memset(servo, 0x00, sizeof(*servo));
+
+	servo->drift = 100000000;
+	servo->last_freq = 100000000;
+	servo->count = 0;
+
+	servo->max_frequency = 100000000;
+	servo->step_threshold = 0.1 * NSEC_PER_SEC;
+	servo->first_step_threshold = 0.00002 * NSEC_PER_SEC;
+	servo->first_update = 1;
+}
+
 /* Parse the commandline arguments. */
 static int
 ptp_parse_args(int argc, char **argv)
@@ -700,7 +920,7 @@ ptp_parse_args(int argc, char **argv)
 
 	argvopt = argv;
 
-	while ((opt = getopt_long(argc, argvopt, "p:T:",
+	while ((opt = getopt_long(argc, argvopt, "p:T:c:",
 				  lgopts, &option_index)) != EOF) {
 
 		switch (opt) {
@@ -724,6 +944,17 @@ ptp_parse_args(int argc, char **argv)
 
 			ptp_data.kernel_time_set = ret;
 			break;
+		case 'c':
+			ret = parse_ptp_servo_mode(optarg);
+			if (ret == 0) {
+				mode = MODE_NONE;
+			} else if (ret == 1) {
+				mode = MODE_PI;
+			} else {
+				print_usage(prgname);
+				return -1;
+			}
+			break;
 
 		default:
 			print_usage(prgname);
@@ -778,6 +1009,14 @@ main(int argc, char *argv[])
 		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
 	/* >8 End of parsing specific arguments. */
 
+	if (mode == MODE_PI) {
+		ptp_data.servo = malloc(sizeof(*(ptp_data.servo)));
+		if (!ptp_data.servo)
+			rte_exit(EXIT_FAILURE, "no memory for servo\n");
+
+		servo_init(ptp_data.servo);
+	}
+
 	/* Check that there is an even number of ports to send/receive on. */
 	nb_ports = rte_eth_dev_count_avail();
 
@@ -831,6 +1070,9 @@ main(int argc, char *argv[])
 		rte_eth_dev_close(portid);
 	}
 
+	if (mode == MODE_PI)
+		free(ptp_data.servo);
+
 	/* clean up the EAL */
 	rte_eal_cleanup();
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v6 2/3] net/ice: add frequency adjustment support for PTP
  2024-10-11  6:34             ` [PATCH v6 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
@ 2024-10-11  8:02               ` Bruce Richardson
  2024-10-11  9:28                 ` Ye, MingjinX
  2024-10-11 23:44                 ` Ferruh Yigit
  0 siblings, 2 replies; 49+ messages in thread
From: Bruce Richardson @ 2024-10-11  8:02 UTC (permalink / raw)
  To: Mingjin Ye; +Cc: dev, Simei Su

On Fri, Oct 11, 2024 at 06:34:06AM +0000, Mingjin Ye wrote:
> Add ice support for new ethdev API to adjust frequency for IEEE1588
> PTP. Also, this patch reworks code for converting software update
> to hardware update.
> 
> Signed-off-by: Simei Su <simei.su@intel.com>
> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
> ---
>  doc/guides/nics/ice.rst      |  16 ++++
>  drivers/net/ice/ice_ethdev.c | 176 ++++++++++++++++++++++++++---------
>  drivers/net/ice/ice_ethdev.h |   5 +-
>  drivers/net/ice/ice_rxtx.c   |   4 +-
>  4 files changed, 153 insertions(+), 48 deletions(-)
> 
> diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
> index ae975d19ad..061c8c7a20 100644
> --- a/doc/guides/nics/ice.rst
> +++ b/doc/guides/nics/ice.rst
> @@ -328,6 +328,22 @@ Forward Error Correction (FEC)
>  
>  Supports get/set FEC mode and get FEC capability.
>  
> +Time Synchronisation
> +~~~~~~~~~~~~~~~~~~~~
> +
> +The system operator can run a PTP (Precision Time Protocol) client application
> +to synchronise the time on the network card (and optionally the time on the
> +system) to the PTP master.
> +
> +ICE PMD supports PTP client applications that use the DPDK IEEE1588 API to
> +communicate with the PTP master clock. Note that PTP client application needs
> +to run on PF and add the ``--force-max-simd-bitwidth=64`` startup parameter to
> +disable vector mode.
> +
> +.. code-block:: console
> +
> +    examples/dpdk-ptpclient -c f -n 3 -a 0000:ec:00.1 --force-max-simd-bitwidth=64 -- -T 1 -p 0x1 -c 1
> +

It's a pity that the vector disabling doesn't happen automatically
somewhere and that we have to ask the user to pass in the flag. Maybe see
if that can be improved for RC2 or next release?

Anyway, for this patch:

Acked-by: Bruce Richardson <bruce.richardson@intel.com>


^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v6 2/3] net/ice: add frequency adjustment support for PTP
  2024-10-11  8:02               ` Bruce Richardson
@ 2024-10-11  9:28                 ` Ye, MingjinX
  2024-10-11 23:44                 ` Ferruh Yigit
  1 sibling, 0 replies; 49+ messages in thread
From: Ye, MingjinX @ 2024-10-11  9:28 UTC (permalink / raw)
  To: Richardson, Bruce; +Cc: dev, Su, Simei



> -----Original Message-----
> From: Richardson, Bruce <bruce.richardson@intel.com>
> Sent: Friday, October 11, 2024 4:03 PM
> To: Ye, MingjinX <mingjinx.ye@intel.com>
> Cc: dev@dpdk.org; Su, Simei <simei.su@intel.com>
> Subject: Re: [PATCH v6 2/3] net/ice: add frequency adjustment support for
> PTP
> 
> On Fri, Oct 11, 2024 at 06:34:06AM +0000, Mingjin Ye wrote:
> > Add ice support for new ethdev API to adjust frequency for IEEE1588
> > PTP. Also, this patch reworks code for converting software update to
> > hardware update.
> >
> > Signed-off-by: Simei Su <simei.su@intel.com>
> > Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
> > ---
> >  doc/guides/nics/ice.rst      |  16 ++++
> >  drivers/net/ice/ice_ethdev.c | 176 ++++++++++++++++++++++++++------
> ---
> >  drivers/net/ice/ice_ethdev.h |   5 +-
> >  drivers/net/ice/ice_rxtx.c   |   4 +-
> >  4 files changed, 153 insertions(+), 48 deletions(-)
> >
> > diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst index
> > ae975d19ad..061c8c7a20 100644
> > --- a/doc/guides/nics/ice.rst
> > +++ b/doc/guides/nics/ice.rst
> > @@ -328,6 +328,22 @@ Forward Error Correction (FEC)
> >
> >  Supports get/set FEC mode and get FEC capability.
> >
> > +Time Synchronisation
> > +~~~~~~~~~~~~~~~~~~~~
> > +
> > +The system operator can run a PTP (Precision Time Protocol) client
> > +application to synchronise the time on the network card (and
> > +optionally the time on the
> > +system) to the PTP master.
> > +
> > +ICE PMD supports PTP client applications that use the DPDK IEEE1588
> > +API to communicate with the PTP master clock. Note that PTP client
> > +application needs to run on PF and add the
> > +``--force-max-simd-bitwidth=64`` startup parameter to disable vector
> mode.
> > +
> > +.. code-block:: console
> > +
> > +    examples/dpdk-ptpclient -c f -n 3 -a 0000:ec:00.1
> > + --force-max-simd-bitwidth=64 -- -T 1 -p 0x1 -c 1
> > +
> 
> It's a pity that the vector disabling doesn't happen automatically somewhere
> and that we have to ask the user to pass in the flag. Maybe see if that can be
> improved for RC2 or next release?

Currently, hardware timestamping is enabled by marking RTE_MBUF_F_TX_IEEEE1588_TMST
in mbuf->ol_flags. Therefore, automatic routing is not possible.

There are no suitable uninstall flags to choose from when selecting the tx path.
This would be easily achieved by adding a macro of type 'RTE_ETH_TX_IEEEE1588_TMST'
after lib/ethdev/rte_ethdev.h#1624.

Is this feasible? Is the scope of the impact on the large side.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v6 3/3] examples/ptpclient: add frequency adjustment
  2024-10-11  6:34             ` [PATCH v6 3/3] examples/ptpclient: add frequency adjustment Mingjin Ye
@ 2024-10-11 19:37               ` Ferruh Yigit
  2024-10-15  8:22               ` [PATCH v7] " Mingjin Ye
  1 sibling, 0 replies; 49+ messages in thread
From: Ferruh Yigit @ 2024-10-11 19:37 UTC (permalink / raw)
  To: Mingjin Ye, dev; +Cc: Simei Su, Wenjun Wu, Kirill Rybalchenko

On 10/11/2024 7:34 AM, Mingjin Ye wrote:
> This patch adds PI servo controller to support frequency
> adjustment API for IEEE1588 PTP.
> 
> For example, the command for starting ptpclient with PI controller is:
> dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1 -c 1
> 
> Signed-off-by: Simei Su <simei.su@intel.com>
> Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
> ---
>  doc/guides/sample_app_ug/ptpclient.rst |  12 +-
>  examples/ptpclient/ptpclient.c         | 284 +++++++++++++++++++++++--
>  2 files changed, 274 insertions(+), 22 deletions(-)
> 
> diff --git a/doc/guides/sample_app_ug/ptpclient.rst b/doc/guides/sample_app_ug/ptpclient.rst
> index d47e942738..89fe575b5f 100644
> --- a/doc/guides/sample_app_ug/ptpclient.rst
> +++ b/doc/guides/sample_app_ug/ptpclient.rst
> @@ -50,6 +50,10 @@ The adjustment for slave can be represented as:
>  If the command line parameter ``-T 1`` is used the application also
>  synchronizes the PTP PHC clock with the Linux kernel clock.
>  
> +If the command line parameter ``-c 1`` is used, the application will also
> +use the servo of the local clock. Only one type of servo is currently
> +implemented, the PI controller. Default 0 (not used).
> +
>  Compiling the Application
>  -------------------------
>  
> @@ -65,7 +69,7 @@ To run the example in a ``linux`` environment:
>  
>  .. code-block:: console
>  
> -    ./<build_dir>/examples/dpdk-ptpclient -l 1 -n 4 -- -p 0x1 -T 0
> +    ./<build_dir>/examples/dpdk-ptpclient -l 1 -n 4 -- -p 0x1 -T 0 -c 1
>  
>  Refer to *DPDK Getting Started Guide* for general information on running
>  applications and the Environment Abstraction Layer (EAL) options.
> @@ -73,7 +77,13 @@ applications and the Environment Abstraction Layer (EAL) options.
>  * ``-p portmask``: Hexadecimal portmask.
>  * ``-T 0``: Update only the PTP slave clock.
>  * ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the PTP clock.
> +* ``-c 0``: Not used clock servo controller.
> +* ``-c 1``: The clock servo PI controller is used and the log will print information
> +            about "master offset".
>  
> +Also, by adding ``-T 1`` and ``-c 1`` , the ``master offset`` value printed in the
> +log will slowly converge and eventually stabilise at the nanosecond level. The
> +synchronisation accuracy is much higher compared to not using a servo controller.
>  

What is this new feature / argument dependency to the new
'rte_eth_timesync_adjust_freq()' API?

Right now only one PMD supports it, should application check the support
of the API when -c parameter provided, and fail it if is not supported?


>  Code Explanation
>  ----------------
> diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
> index afb61bba51..dea8d9d54a 100644
> --- a/examples/ptpclient/ptpclient.c
> +++ b/examples/ptpclient/ptpclient.c
> @@ -46,6 +46,35 @@ static volatile bool force_quit;
>  #define KERNEL_TIME_ADJUST_LIMIT  20000
>  #define PTP_PROTOCOL             0x88F7
>  
> +#define KP 0.7
> +#define KI 0.3
> +#define FREQ_EST_MARGIN 0.001
> +
> +enum servo_state {
> +	SERVO_UNLOCKED,
> +	SERVO_JUMP,
> +	SERVO_LOCKED,
> +};
> +
> +struct pi_servo {
> +	double offset[2];
> +	double local[2];
> +	double drift;
> +	double last_freq;
> +	int count;
> +
> +	double max_frequency;
> +	double step_threshold;
> +	double first_step_threshold;
> +	int first_update;
> +};
> +
> +enum controller_mode {
> +	MODE_NONE,
> +	MODE_PI,
> +	MAX_ALL
> +} mode = MODE_NONE;
> +
>  struct rte_mempool *mbuf_pool;
>  uint32_t ptp_enabled_port_mask;
>  uint8_t ptp_enabled_port_nb;
> @@ -135,6 +164,9 @@ struct ptpv2_data_slave_ordinary {
>  	uint8_t ptpset;
>  	uint8_t kernel_time_set;
>  	uint16_t current_ptp_port;
> +	int64_t master_offset;
> +	int64_t path_delay;
> +	struct pi_servo *servo;
>  };
>  
>  static struct ptpv2_data_slave_ordinary ptp_data;
> @@ -293,36 +325,44 @@ print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
>  			ptp_data->tstamp3.tv_sec,
>  			(ptp_data->tstamp3.tv_nsec));
>  
> -	printf("\nT4 - Master Clock.  %lds %ldns ",
> +	printf("\nT4 - Master Clock.  %lds %ldns\n",
>  			ptp_data->tstamp4.tv_sec,
>  			(ptp_data->tstamp4.tv_nsec));
>  
> -	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
> +	if (mode == MODE_NONE) {
> +		printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
>  			ptp_data->delta);
>  
> -	clock_gettime(CLOCK_REALTIME, &sys_time);
> -	rte_eth_timesync_read_time(ptp_data->current_ptp_port, &net_time);
> +		clock_gettime(CLOCK_REALTIME, &sys_time);
> +		rte_eth_timesync_read_time(ptp_data->current_ptp_port,
> +					   &net_time);
>  
> -	time_t ts = net_time.tv_sec;
> +		time_t ts = net_time.tv_sec;
>  
> -	printf("\n\nComparison between Linux kernel Time and PTP:");
> +		printf("\n\nComparison between Linux kernel Time and PTP:");
>  
> -	printf("\nCurrent PTP Time: %.24s %.9ld ns",
> +		printf("\nCurrent PTP Time: %.24s %.9ld ns",
>  			ctime(&ts), net_time.tv_nsec);
>  
> -	nsec = (int64_t)timespec64_to_ns(&net_time) -
> +		nsec = (int64_t)timespec64_to_ns(&net_time) -
>  			(int64_t)timespec64_to_ns(&sys_time);
> -	ptp_data->new_adj = ns_to_timeval(nsec);
> +		ptp_data->new_adj = ns_to_timeval(nsec);
>  
> -	gettimeofday(&ptp_data->new_adj, NULL);
> +		gettimeofday(&ptp_data->new_adj, NULL);
>  
> -	time_t tp = ptp_data->new_adj.tv_sec;
> +		time_t tp = ptp_data->new_adj.tv_sec;
>  
> -	printf("\nCurrent SYS Time: %.24s %.6ld ns",
> -				ctime(&tp), ptp_data->new_adj.tv_usec);
> +		printf("\nCurrent SYS Time: %.24s %.6ld ns",
> +			ctime(&tp), ptp_data->new_adj.tv_usec);
>  
> -	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
> -				nsec);
> +		printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
> +			nsec);
> +	}
> +
> +	if (mode == MODE_PI) {
> +		printf("path delay: %"PRId64"ns\n", ptp_data->path_delay);
> +		printf("master offset: %"PRId64"ns\n", ptp_data->master_offset);
> +	}
>  
>  	printf("[Ctrl+C to quit]\n");
>  
> @@ -529,6 +569,149 @@ update_kernel_time(void)
>  
>  }
>  
> +static void
> +clock_path_delay(struct ptpv2_data_slave_ordinary *ptp_data)
> +{
> +	uint64_t t1_ns, t2_ns, t3_ns, t4_ns;
> +	int64_t pd, diff;
> +
> +	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
> +	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
> +	t3_ns = timespec64_to_ns(&ptp_data->tstamp3);
> +	t4_ns = timespec64_to_ns(&ptp_data->tstamp4);
> +
> +	pd = (t2_ns - t3_ns) + (t4_ns - t1_ns);
> +	diff = t3_ns - t2_ns;
> +	if (diff <= INT32_MAX && diff >= INT32_MIN)
> +		ptp_data->path_delay = pd / 2;
> +	else
> +		ptp_data->path_delay = 0;
> +}
> +
> +static double
> +pi_sample(struct pi_servo *s, int64_t offset, double local_ts,
> +	  enum servo_state *state)
> +{
> +	double ki_term, ppb = s->last_freq;
> +	double freq_est_interval, localdiff;
> +
> +	switch (s->count) {
> +	case 0:
> +		s->offset[0] = offset;
> +		s->local[0] = local_ts;
> +		*state = SERVO_UNLOCKED;
> +		s->count = 1;
> +		break;
> +	case 1:
> +		s->offset[1] = offset;
> +		s->local[1] = local_ts;
> +
> +		/* Make sure the first sample is older than the second. */
> +		if (s->local[0] >= s->local[1]) {
> +			*state = SERVO_UNLOCKED;
> +			s->count = 0;
> +			break;
> +		}
> +
> +		/* Wait long enough before estimating the frequency offset. */
> +		localdiff = (s->local[1] - s->local[0]) / 1e9;
> +		localdiff += localdiff * FREQ_EST_MARGIN;
> +		freq_est_interval = 0.016 / KI;
> +		if (freq_est_interval > 1000.0)
> +			freq_est_interval = 1000.0;
> +
> +		if (localdiff < freq_est_interval) {
> +			*state = SERVO_UNLOCKED;
> +			break;
> +		}
> +
> +		/* Adjust drift by the measured frequency offset. */
> +		s->drift += (1e9 - s->drift) * (s->offset[1] - s->offset[0]) /
> +						(s->local[1] - s->local[0]);
> +
> +		if (s->drift < -s->max_frequency)
> +			s->drift = -s->max_frequency;
> +		else if (s->drift > s->max_frequency)
> +			s->drift = s->max_frequency;
> +
> +		if ((s->first_update &&
> +		     s->first_step_threshold &&
> +		     s->first_step_threshold < llabs(offset)) ||
> +		    (s->step_threshold &&
> +		     s->step_threshold < llabs(offset)))
> +			*state = SERVO_JUMP;
> +		else
> +			*state = SERVO_LOCKED;
> +
> +		ppb = s->drift;
> +		s->count = 2;
> +		break;
> +	case 2:
> +		/*
> +		 * reset the clock servo when offset is greater than the max
> +		 * offset value. Note that the clock jump will be performed in
> +		 * step 1, so it is not necessary to have clock jump
> +		 * immediately. This allows re-calculating drift as in initial
> +		 * clock startup.
> +		 */
> +		if (s->step_threshold &&
> +		    s->step_threshold < llabs(offset)) {
> +			*state = SERVO_UNLOCKED;
> +			s->count = 0;
> +			break;
> +		}
> +
> +		ki_term = KI * offset;
> +		ppb = KP * offset + s->drift + ki_term;
> +		if (ppb < -s->max_frequency)
> +			ppb = -s->max_frequency;
> +		else if (ppb > s->max_frequency)
> +			ppb = s->max_frequency;
> +		else
> +			s->drift += ki_term;
> +
> +		*state = SERVO_LOCKED;
> +		break;
> +	}
> +
> +	s->last_freq = ppb;
> +	return ppb;
> +}
> +
> +static void
> +ptp_adjust_servo(struct ptpv2_data_slave_ordinary *ptp_data)
> +{
> +	uint64_t t1_ns, t2_ns;
> +	double adj_freq;
> +	enum servo_state state = SERVO_UNLOCKED;
> +
> +	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
> +	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
> +	ptp_data->master_offset = t2_ns - t1_ns - ptp_data->path_delay;
> +	if (!ptp_data->path_delay)
> +		return;
> +
> +	adj_freq = pi_sample(ptp_data->servo, ptp_data->master_offset, t2_ns,
> +		     &state);
> +
> +	switch (state) {
> +	case SERVO_UNLOCKED:
> +		break;
> +	case SERVO_JUMP:
> +		ptp_data->servo->first_update = 0;
> +		rte_eth_timesync_adjust_freq(ptp_data->portid,
> +						-(long)(adj_freq * 65.536));
> +		rte_eth_timesync_adjust_time(ptp_data->portid,
> +					     -ptp_data->master_offset);
> +		break;
> +	case SERVO_LOCKED:
> +		ptp_data->servo->first_update = 0;
> +		rte_eth_timesync_adjust_freq(ptp_data->portid,
> +					     -(long)(adj_freq * 65.536));
> +		break;
> +	}
> +}
> +
>  /*
>   * Parse the DELAY_RESP message.
>   */
> @@ -553,11 +736,16 @@ parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
>  				((uint64_t)ntohl(rx_tstamp->sec_lsb)) |
>  				(((uint64_t)ntohs(rx_tstamp->sec_msb)) << 32);
>  
> -			/* Evaluate the delta for adjustment. */
> -			ptp_data->delta = delta_eval(ptp_data);
> +			if (mode == MODE_PI) {
> +				clock_path_delay(ptp_data);
> +				ptp_adjust_servo(ptp_data);
> +			} else {
> +				/* Evaluate the delta for adjustment. */
> +				ptp_data->delta = delta_eval(ptp_data);
>  
> -			rte_eth_timesync_adjust_time(ptp_data->portid,
> -						     ptp_data->delta);
> +				rte_eth_timesync_adjust_time(ptp_data->portid,
> +								ptp_data->delta);
> +			}
>  
>  			ptp_data->current_ptp_port = ptp_data->portid;
>  
> @@ -652,7 +840,9 @@ print_usage(const char *prgname)
>  	printf("%s [EAL options] -- -p PORTMASK -T VALUE\n"
>  		" -T VALUE: 0 - Disable, 1 - Enable Linux Clock"
>  		" Synchronization (0 default)\n"
> -		" -p PORTMASK: hexadecimal bitmask of ports to configure\n",
> +		" -p PORTMASK: hexadecimal bitmask of ports to configure\n"
> +		" -c CONTROLLER: 0 - Not used, 1 - PI. The servo which is"
> +		" used to synchronize the local clock. (0 default)\n",
>  		prgname);
>  }
>  
> @@ -688,6 +878,36 @@ parse_ptp_kernel(const char *param)
>  	return 1;
>  }
>  
> +static int
> +parse_ptp_servo_mode(const char *param)
> +{
> +	char *end = NULL;
> +	unsigned long pm;
> +
> +	/* Parse the hexadecimal string. */
> +	pm = strtoul(param, &end, 10);
> +
> +	if ((param[0] == '\0') || (end == NULL) || (*end != '\0'))
> +		return -1;
> +
> +	return pm;
> +}
> +
> +static void
> +servo_init(struct pi_servo *servo)
> +{
> +	memset(servo, 0x00, sizeof(*servo));
> +
> +	servo->drift = 100000000;
> +	servo->last_freq = 100000000;
> +	servo->count = 0;
> +
> +	servo->max_frequency = 100000000;
> +	servo->step_threshold = 0.1 * NSEC_PER_SEC;
> +	servo->first_step_threshold = 0.00002 * NSEC_PER_SEC;
> +	servo->first_update = 1;
> +}
> +
>  /* Parse the commandline arguments. */
>  static int
>  ptp_parse_args(int argc, char **argv)
> @@ -700,7 +920,7 @@ ptp_parse_args(int argc, char **argv)
>  
>  	argvopt = argv;
>  
> -	while ((opt = getopt_long(argc, argvopt, "p:T:",
> +	while ((opt = getopt_long(argc, argvopt, "p:T:c:",
>  				  lgopts, &option_index)) != EOF) {
>  
>  		switch (opt) {
> @@ -724,6 +944,17 @@ ptp_parse_args(int argc, char **argv)
>  
>  			ptp_data.kernel_time_set = ret;
>  			break;
> +		case 'c':
> +			ret = parse_ptp_servo_mode(optarg);
> +			if (ret == 0) {
> +				mode = MODE_NONE;
> +			} else if (ret == 1) {
> +				mode = MODE_PI;
> +			} else {
> +				print_usage(prgname);
> +				return -1;
> +			}
> +			break;
>  
>  		default:
>  			print_usage(prgname);
> @@ -778,6 +1009,14 @@ main(int argc, char *argv[])
>  		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
>  	/* >8 End of parsing specific arguments. */
>  
> +	if (mode == MODE_PI) {
> +		ptp_data.servo = malloc(sizeof(*(ptp_data.servo)));
> +		if (!ptp_data.servo)
> +			rte_exit(EXIT_FAILURE, "no memory for servo\n");
> +
> +		servo_init(ptp_data.servo);
> +	}
> +
>  	/* Check that there is an even number of ports to send/receive on. */
>  	nb_ports = rte_eth_dev_count_avail();
>  
> @@ -831,6 +1070,9 @@ main(int argc, char *argv[])
>  		rte_eth_dev_close(portid);
>  	}
>  
> +	if (mode == MODE_PI)
> +		free(ptp_data.servo);
> +
>  	/* clean up the EAL */
>  	rte_eal_cleanup();
>  


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v6 1/3] ethdev: add frequency adjustment API
  2024-10-11  6:34             ` [PATCH v6 1/3] ethdev: add frequency adjustment API Mingjin Ye
@ 2024-10-11 23:44               ` Ferruh Yigit
  0 siblings, 0 replies; 49+ messages in thread
From: Ferruh Yigit @ 2024-10-11 23:44 UTC (permalink / raw)
  To: Mingjin Ye, dev; +Cc: Simei Su, Thomas Monjalon, Andrew Rybchenko

On 10/11/2024 7:34 AM, Mingjin Ye wrote:
> This patch adds freq adjustment API for PTP high accuracy.
> 
> Signed-off-by: Simei Su <simei.su@intel.com>
> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
>

Reviewed-by: Ferruh Yigit <ferruh.yigit@amd.com>

Applied to dpdk-next-net/main, thanks.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v6 2/3] net/ice: add frequency adjustment support for PTP
  2024-10-11  8:02               ` Bruce Richardson
  2024-10-11  9:28                 ` Ye, MingjinX
@ 2024-10-11 23:44                 ` Ferruh Yigit
  1 sibling, 0 replies; 49+ messages in thread
From: Ferruh Yigit @ 2024-10-11 23:44 UTC (permalink / raw)
  To: Bruce Richardson, Mingjin Ye; +Cc: dev, Simei Su

On 10/11/2024 9:02 AM, Bruce Richardson wrote:
> On Fri, Oct 11, 2024 at 06:34:06AM +0000, Mingjin Ye wrote:
>> Add ice support for new ethdev API to adjust frequency for IEEE1588
>> PTP. Also, this patch reworks code for converting software update
>> to hardware update.
>>
>> Signed-off-by: Simei Su <simei.su@intel.com>
>> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
>> ---
>>  doc/guides/nics/ice.rst      |  16 ++++
>>  drivers/net/ice/ice_ethdev.c | 176 ++++++++++++++++++++++++++---------
>>  drivers/net/ice/ice_ethdev.h |   5 +-
>>  drivers/net/ice/ice_rxtx.c   |   4 +-
>>  4 files changed, 153 insertions(+), 48 deletions(-)
>>
>> diff --git a/doc/guides/nics/ice.rst b/doc/guides/nics/ice.rst
>> index ae975d19ad..061c8c7a20 100644
>> --- a/doc/guides/nics/ice.rst
>> +++ b/doc/guides/nics/ice.rst
>> @@ -328,6 +328,22 @@ Forward Error Correction (FEC)
>>  
>>  Supports get/set FEC mode and get FEC capability.
>>  
>> +Time Synchronisation
>> +~~~~~~~~~~~~~~~~~~~~
>> +
>> +The system operator can run a PTP (Precision Time Protocol) client application
>> +to synchronise the time on the network card (and optionally the time on the
>> +system) to the PTP master.
>> +
>> +ICE PMD supports PTP client applications that use the DPDK IEEE1588 API to
>> +communicate with the PTP master clock. Note that PTP client application needs
>> +to run on PF and add the ``--force-max-simd-bitwidth=64`` startup parameter to
>> +disable vector mode.
>> +
>> +.. code-block:: console
>> +
>> +    examples/dpdk-ptpclient -c f -n 3 -a 0000:ec:00.1 --force-max-simd-bitwidth=64 -- -T 1 -p 0x1 -c 1
>> +
> 
> It's a pity that the vector disabling doesn't happen automatically
> somewhere and that we have to ask the user to pass in the flag. Maybe see
> if that can be improved for RC2 or next release?
> 
> Anyway, for this patch:
> 
> Acked-by: Bruce Richardson <bruce.richardson@intel.com>
> 

Applied to dpdk-next-net/main, thanks.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v6 0/3] add frequency adjustment support for PTP
  2024-10-11  6:34           ` [PATCH v6 0/3] add frequency adjustment support for PTP Mingjin Ye
                               ` (2 preceding siblings ...)
  2024-10-11  6:34             ` [PATCH v6 3/3] examples/ptpclient: add frequency adjustment Mingjin Ye
@ 2024-10-11 23:44             ` Ferruh Yigit
  3 siblings, 0 replies; 49+ messages in thread
From: Ferruh Yigit @ 2024-10-11 23:44 UTC (permalink / raw)
  To: Mingjin Ye, dev

On 10/11/2024 7:34 AM, Mingjin Ye wrote:
> [1/3] ethdev: add frequency adjustment API
> [2/3] net/ice: add frequency adjustment support for PTP
> [3/3] examples/ptpclient: add frequency adjustment
> ---
> v2: rte_eth_timesync_adjust_freq marked as experimental.
> ---
> v3: Add more description for API.
> ---
> v4: Documentation for adding a ptpclient.
> ---
> v5: Fixes in ice.
> ---
> v6: Fix git commit conflict.
> 
> Mingjin Ye (3):
>   ethdev: add frequency adjustment API
>   net/ice: add frequency adjustment support for PTP
>   examples/ptpclient: add frequency adjustment
> 

Merged this patch series partially, 1/3 & 2/3 merged to be able to get
them to -rc1.

Discussions on sample application, patch 3/3, can continue.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v7] examples/ptpclient: add frequency adjustment
  2024-10-11  6:34             ` [PATCH v6 3/3] examples/ptpclient: add frequency adjustment Mingjin Ye
  2024-10-11 19:37               ` Ferruh Yigit
@ 2024-10-15  8:22               ` Mingjin Ye
  2024-10-15 17:43                 ` Stephen Hemminger
  2024-11-04  9:30                 ` Rybalchenko, Kirill
  1 sibling, 2 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-10-15  8:22 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye, Simei Su, Wenjun Wu, Kirill Rybalchenko

This patch adds PI servo controller to support frequency
adjustment API for IEEE1588 PTP.

For example, the command for starting ptpclient with PI controller is:
dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1 -c 1

Signed-off-by: Simei Su <simei.su@intel.com>
Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
---
v2: Add doc.
---
v3: Enable The servo controller needs to support frequency adjustment.
---
v2: Enable servo controller, PMD must
				support ‘rte_eth_timesync_adjust_freq()’ API.
---
 doc/guides/sample_app_ug/ptpclient.rst |  15 +-
 examples/ptpclient/ptpclient.c         | 297 +++++++++++++++++++++++--
 2 files changed, 289 insertions(+), 23 deletions(-)

diff --git a/doc/guides/sample_app_ug/ptpclient.rst b/doc/guides/sample_app_ug/ptpclient.rst
index d47e942738..014d5f477c 100644
--- a/doc/guides/sample_app_ug/ptpclient.rst
+++ b/doc/guides/sample_app_ug/ptpclient.rst
@@ -50,6 +50,10 @@ The adjustment for slave can be represented as:
 If the command line parameter ``-T 1`` is used the application also
 synchronizes the PTP PHC clock with the Linux kernel clock.
 
+If the command line parameter ``-c 1`` is used, the application will also
+use the servo of the local clock. Only one type of servo is currently
+implemented, the PI controller. Default 0 (not used).
+
 Compiling the Application
 -------------------------
 
@@ -65,7 +69,7 @@ To run the example in a ``linux`` environment:
 
 .. code-block:: console
 
-    ./<build_dir>/examples/dpdk-ptpclient -l 1 -n 4 -- -p 0x1 -T 0
+    ./<build_dir>/examples/dpdk-ptpclient -l 1 -n 4 -- -p 0x1 -T 0 -c 1
 
 Refer to *DPDK Getting Started Guide* for general information on running
 applications and the Environment Abstraction Layer (EAL) options.
@@ -73,7 +77,14 @@ applications and the Environment Abstraction Layer (EAL) options.
 * ``-p portmask``: Hexadecimal portmask.
 * ``-T 0``: Update only the PTP slave clock.
 * ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the PTP clock.
-
+* ``-c 0``: Not used clock servo controller.
+* ``-c 1``: The clock servo PI controller is used and the log will print information
+            about "master offset". Note that the PMD needs to support
+            the ``rte_eth_timesync_adjust_freq()`` API to enable the servo controller.
+
+Also, by adding ``-T 1`` and ``-c 1`` , the ``master offset`` value printed in the
+log will slowly converge and eventually stabilise at the nanosecond level. The
+synchronisation accuracy is much higher compared to not using a servo controller.
 
 Code Explanation
 ----------------
diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
index afb61bba51..e8f6f95f18 100644
--- a/examples/ptpclient/ptpclient.c
+++ b/examples/ptpclient/ptpclient.c
@@ -46,6 +46,35 @@ static volatile bool force_quit;
 #define KERNEL_TIME_ADJUST_LIMIT  20000
 #define PTP_PROTOCOL             0x88F7
 
+#define KP 0.7
+#define KI 0.3
+#define FREQ_EST_MARGIN 0.001
+
+enum servo_state {
+	SERVO_UNLOCKED,
+	SERVO_JUMP,
+	SERVO_LOCKED,
+};
+
+struct pi_servo {
+	double offset[2];
+	double local[2];
+	double drift;
+	double last_freq;
+	int count;
+
+	double max_frequency;
+	double step_threshold;
+	double first_step_threshold;
+	int first_update;
+};
+
+enum controller_mode {
+	MODE_NONE,
+	MODE_PI,
+	MAX_ALL
+} mode = MODE_NONE;
+
 struct rte_mempool *mbuf_pool;
 uint32_t ptp_enabled_port_mask;
 uint8_t ptp_enabled_port_nb;
@@ -135,6 +164,9 @@ struct ptpv2_data_slave_ordinary {
 	uint8_t ptpset;
 	uint8_t kernel_time_set;
 	uint16_t current_ptp_port;
+	int64_t master_offset;
+	int64_t path_delay;
+	struct pi_servo *servo;
 };
 
 static struct ptpv2_data_slave_ordinary ptp_data;
@@ -262,6 +294,19 @@ port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 		return retval;
 	}
 
+	/*
+	 * If the clock servo controller is enabled, the PMD must support
+	 * adjustment of the clock frequency.
+	 */
+	if (mode != MODE_NONE) {
+		retval = rte_eth_timesync_adjust_freq(port, 0);
+		if (retval == -ENOTSUP) {
+			printf("The servo controller cannot work on devices that"
+					" do not support frequency adjustment.\n");
+			return retval;
+		}
+	}
+
 	return 0;
 }
 
@@ -293,36 +338,44 @@ print_clock_info(struct ptpv2_data_slave_ordinary *ptp_data)
 			ptp_data->tstamp3.tv_sec,
 			(ptp_data->tstamp3.tv_nsec));
 
-	printf("\nT4 - Master Clock.  %lds %ldns ",
+	printf("\nT4 - Master Clock.  %lds %ldns\n",
 			ptp_data->tstamp4.tv_sec,
 			(ptp_data->tstamp4.tv_nsec));
 
-	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
+	if (mode == MODE_NONE) {
+		printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
 			ptp_data->delta);
 
-	clock_gettime(CLOCK_REALTIME, &sys_time);
-	rte_eth_timesync_read_time(ptp_data->current_ptp_port, &net_time);
+		clock_gettime(CLOCK_REALTIME, &sys_time);
+		rte_eth_timesync_read_time(ptp_data->current_ptp_port,
+					   &net_time);
 
-	time_t ts = net_time.tv_sec;
+		time_t ts = net_time.tv_sec;
 
-	printf("\n\nComparison between Linux kernel Time and PTP:");
+		printf("\n\nComparison between Linux kernel Time and PTP:");
 
-	printf("\nCurrent PTP Time: %.24s %.9ld ns",
+		printf("\nCurrent PTP Time: %.24s %.9ld ns",
 			ctime(&ts), net_time.tv_nsec);
 
-	nsec = (int64_t)timespec64_to_ns(&net_time) -
+		nsec = (int64_t)timespec64_to_ns(&net_time) -
 			(int64_t)timespec64_to_ns(&sys_time);
-	ptp_data->new_adj = ns_to_timeval(nsec);
+		ptp_data->new_adj = ns_to_timeval(nsec);
 
-	gettimeofday(&ptp_data->new_adj, NULL);
+		gettimeofday(&ptp_data->new_adj, NULL);
 
-	time_t tp = ptp_data->new_adj.tv_sec;
+		time_t tp = ptp_data->new_adj.tv_sec;
 
-	printf("\nCurrent SYS Time: %.24s %.6ld ns",
-				ctime(&tp), ptp_data->new_adj.tv_usec);
+		printf("\nCurrent SYS Time: %.24s %.6ld ns",
+			ctime(&tp), ptp_data->new_adj.tv_usec);
 
-	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
-				nsec);
+		printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
+			nsec);
+	}
+
+	if (mode == MODE_PI) {
+		printf("path delay: %"PRId64"ns\n", ptp_data->path_delay);
+		printf("master offset: %"PRId64"ns\n", ptp_data->master_offset);
+	}
 
 	printf("[Ctrl+C to quit]\n");
 
@@ -529,6 +582,149 @@ update_kernel_time(void)
 
 }
 
+static void
+clock_path_delay(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns, t3_ns, t4_ns;
+	int64_t pd, diff;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	t3_ns = timespec64_to_ns(&ptp_data->tstamp3);
+	t4_ns = timespec64_to_ns(&ptp_data->tstamp4);
+
+	pd = (t2_ns - t3_ns) + (t4_ns - t1_ns);
+	diff = t3_ns - t2_ns;
+	if (diff <= INT32_MAX && diff >= INT32_MIN)
+		ptp_data->path_delay = pd / 2;
+	else
+		ptp_data->path_delay = 0;
+}
+
+static double
+pi_sample(struct pi_servo *s, int64_t offset, double local_ts,
+	  enum servo_state *state)
+{
+	double ki_term, ppb = s->last_freq;
+	double freq_est_interval, localdiff;
+
+	switch (s->count) {
+	case 0:
+		s->offset[0] = offset;
+		s->local[0] = local_ts;
+		*state = SERVO_UNLOCKED;
+		s->count = 1;
+		break;
+	case 1:
+		s->offset[1] = offset;
+		s->local[1] = local_ts;
+
+		/* Make sure the first sample is older than the second. */
+		if (s->local[0] >= s->local[1]) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		/* Wait long enough before estimating the frequency offset. */
+		localdiff = (s->local[1] - s->local[0]) / 1e9;
+		localdiff += localdiff * FREQ_EST_MARGIN;
+		freq_est_interval = 0.016 / KI;
+		if (freq_est_interval > 1000.0)
+			freq_est_interval = 1000.0;
+
+		if (localdiff < freq_est_interval) {
+			*state = SERVO_UNLOCKED;
+			break;
+		}
+
+		/* Adjust drift by the measured frequency offset. */
+		s->drift += (1e9 - s->drift) * (s->offset[1] - s->offset[0]) /
+						(s->local[1] - s->local[0]);
+
+		if (s->drift < -s->max_frequency)
+			s->drift = -s->max_frequency;
+		else if (s->drift > s->max_frequency)
+			s->drift = s->max_frequency;
+
+		if ((s->first_update &&
+		     s->first_step_threshold &&
+		     s->first_step_threshold < llabs(offset)) ||
+		    (s->step_threshold &&
+		     s->step_threshold < llabs(offset)))
+			*state = SERVO_JUMP;
+		else
+			*state = SERVO_LOCKED;
+
+		ppb = s->drift;
+		s->count = 2;
+		break;
+	case 2:
+		/*
+		 * reset the clock servo when offset is greater than the max
+		 * offset value. Note that the clock jump will be performed in
+		 * step 1, so it is not necessary to have clock jump
+		 * immediately. This allows re-calculating drift as in initial
+		 * clock startup.
+		 */
+		if (s->step_threshold &&
+		    s->step_threshold < llabs(offset)) {
+			*state = SERVO_UNLOCKED;
+			s->count = 0;
+			break;
+		}
+
+		ki_term = KI * offset;
+		ppb = KP * offset + s->drift + ki_term;
+		if (ppb < -s->max_frequency)
+			ppb = -s->max_frequency;
+		else if (ppb > s->max_frequency)
+			ppb = s->max_frequency;
+		else
+			s->drift += ki_term;
+
+		*state = SERVO_LOCKED;
+		break;
+	}
+
+	s->last_freq = ppb;
+	return ppb;
+}
+
+static void
+ptp_adjust_servo(struct ptpv2_data_slave_ordinary *ptp_data)
+{
+	uint64_t t1_ns, t2_ns;
+	double adj_freq;
+	enum servo_state state = SERVO_UNLOCKED;
+
+	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
+	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
+	ptp_data->master_offset = t2_ns - t1_ns - ptp_data->path_delay;
+	if (!ptp_data->path_delay)
+		return;
+
+	adj_freq = pi_sample(ptp_data->servo, ptp_data->master_offset, t2_ns,
+		     &state);
+
+	switch (state) {
+	case SERVO_UNLOCKED:
+		break;
+	case SERVO_JUMP:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+						-(long)(adj_freq * 65.536));
+		rte_eth_timesync_adjust_time(ptp_data->portid,
+					     -ptp_data->master_offset);
+		break;
+	case SERVO_LOCKED:
+		ptp_data->servo->first_update = 0;
+		rte_eth_timesync_adjust_freq(ptp_data->portid,
+					     -(long)(adj_freq * 65.536));
+		break;
+	}
+}
+
 /*
  * Parse the DELAY_RESP message.
  */
@@ -553,11 +749,16 @@ parse_drsp(struct ptpv2_data_slave_ordinary *ptp_data)
 				((uint64_t)ntohl(rx_tstamp->sec_lsb)) |
 				(((uint64_t)ntohs(rx_tstamp->sec_msb)) << 32);
 
-			/* Evaluate the delta for adjustment. */
-			ptp_data->delta = delta_eval(ptp_data);
+			if (mode == MODE_PI) {
+				clock_path_delay(ptp_data);
+				ptp_adjust_servo(ptp_data);
+			} else {
+				/* Evaluate the delta for adjustment. */
+				ptp_data->delta = delta_eval(ptp_data);
 
-			rte_eth_timesync_adjust_time(ptp_data->portid,
-						     ptp_data->delta);
+				rte_eth_timesync_adjust_time(ptp_data->portid,
+								ptp_data->delta);
+			}
 
 			ptp_data->current_ptp_port = ptp_data->portid;
 
@@ -652,7 +853,9 @@ print_usage(const char *prgname)
 	printf("%s [EAL options] -- -p PORTMASK -T VALUE\n"
 		" -T VALUE: 0 - Disable, 1 - Enable Linux Clock"
 		" Synchronization (0 default)\n"
-		" -p PORTMASK: hexadecimal bitmask of ports to configure\n",
+		" -p PORTMASK: hexadecimal bitmask of ports to configure\n"
+		" -c CONTROLLER: 0 - Not used, 1 - PI. The servo which is"
+		" used to synchronize the local clock. (0 default)\n",
 		prgname);
 }
 
@@ -688,6 +891,36 @@ parse_ptp_kernel(const char *param)
 	return 1;
 }
 
+static int
+parse_ptp_servo_mode(const char *param)
+{
+	char *end = NULL;
+	unsigned long pm;
+
+	/* Parse the hexadecimal string. */
+	pm = strtoul(param, &end, 10);
+
+	if ((param[0] == '\0') || (end == NULL) || (*end != '\0'))
+		return -1;
+
+	return pm;
+}
+
+static void
+servo_init(struct pi_servo *servo)
+{
+	memset(servo, 0x00, sizeof(*servo));
+
+	servo->drift = 100000000;
+	servo->last_freq = 100000000;
+	servo->count = 0;
+
+	servo->max_frequency = 100000000;
+	servo->step_threshold = 0.1 * NSEC_PER_SEC;
+	servo->first_step_threshold = 0.00002 * NSEC_PER_SEC;
+	servo->first_update = 1;
+}
+
 /* Parse the commandline arguments. */
 static int
 ptp_parse_args(int argc, char **argv)
@@ -700,7 +933,7 @@ ptp_parse_args(int argc, char **argv)
 
 	argvopt = argv;
 
-	while ((opt = getopt_long(argc, argvopt, "p:T:",
+	while ((opt = getopt_long(argc, argvopt, "p:T:c:",
 				  lgopts, &option_index)) != EOF) {
 
 		switch (opt) {
@@ -724,6 +957,17 @@ ptp_parse_args(int argc, char **argv)
 
 			ptp_data.kernel_time_set = ret;
 			break;
+		case 'c':
+			ret = parse_ptp_servo_mode(optarg);
+			if (ret == 0) {
+				mode = MODE_NONE;
+			} else if (ret == 1) {
+				mode = MODE_PI;
+			} else {
+				print_usage(prgname);
+				return -1;
+			}
+			break;
 
 		default:
 			print_usage(prgname);
@@ -778,6 +1022,14 @@ main(int argc, char *argv[])
 		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
 	/* >8 End of parsing specific arguments. */
 
+	if (mode == MODE_PI) {
+		ptp_data.servo = malloc(sizeof(*(ptp_data.servo)));
+		if (!ptp_data.servo)
+			rte_exit(EXIT_FAILURE, "no memory for servo\n");
+
+		servo_init(ptp_data.servo);
+	}
+
 	/* Check that there is an even number of ports to send/receive on. */
 	nb_ports = rte_eth_dev_count_avail();
 
@@ -831,6 +1083,9 @@ main(int argc, char *argv[])
 		rte_eth_dev_close(portid);
 	}
 
+	if (mode == MODE_PI)
+		free(ptp_data.servo);
+
 	/* clean up the EAL */
 	rte_eal_cleanup();
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v7] examples/ptpclient: add frequency adjustment
  2024-10-15  8:22               ` [PATCH v7] " Mingjin Ye
@ 2024-10-15 17:43                 ` Stephen Hemminger
  2024-10-15 17:57                   ` Ferruh Yigit
  2024-10-16  1:23                   ` Ye, MingjinX
  2024-11-04  9:30                 ` Rybalchenko, Kirill
  1 sibling, 2 replies; 49+ messages in thread
From: Stephen Hemminger @ 2024-10-15 17:43 UTC (permalink / raw)
  To: Mingjin Ye; +Cc: dev, Simei Su, Wenjun Wu, Kirill Rybalchenko

On Tue, 15 Oct 2024 08:22:01 +0000
Mingjin Ye <mingjinx.ye@intel.com> wrote:

> This patch adds PI servo controller to support frequency
> adjustment API for IEEE1588 PTP.
> 
> For example, the command for starting ptpclient with PI controller is:
> dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1 -c 1
> 
> Signed-off-by: Simei Su <simei.su@intel.com>
> Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>

This fails build testing, is it depending on some other patch?

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v7] examples/ptpclient: add frequency adjustment
  2024-10-15 17:43                 ` Stephen Hemminger
@ 2024-10-15 17:57                   ` Ferruh Yigit
  2024-10-16  1:41                     ` Ye, MingjinX
  2024-10-16  1:23                   ` Ye, MingjinX
  1 sibling, 1 reply; 49+ messages in thread
From: Ferruh Yigit @ 2024-10-15 17:57 UTC (permalink / raw)
  To: Stephen Hemminger, Mingjin Ye
  Cc: dev, Simei Su, Wenjun Wu, Kirill Rybalchenko

On 10/15/2024 6:43 PM, Stephen Hemminger wrote:
> On Tue, 15 Oct 2024 08:22:01 +0000
> Mingjin Ye <mingjinx.ye@intel.com> wrote:
> 
>> This patch adds PI servo controller to support frequency
>> adjustment API for IEEE1588 PTP.
>>
>> For example, the command for starting ptpclient with PI controller is:
>> dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1 -c 1
>>
>> Signed-off-by: Simei Su <simei.su@intel.com>
>> Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
>> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
> 
> This fails build testing, is it depending on some other patch?
>

It is based on next-net, and uses a new API in next-net.

We may proceed with this patch after next-net pulled to main.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v7] examples/ptpclient: add frequency adjustment
  2024-10-15 17:43                 ` Stephen Hemminger
  2024-10-15 17:57                   ` Ferruh Yigit
@ 2024-10-16  1:23                   ` Ye, MingjinX
  1 sibling, 0 replies; 49+ messages in thread
From: Ye, MingjinX @ 2024-10-16  1:23 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: dev, Su, Simei, Wenjun Wu, Rybalchenko, Kirill



> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Wednesday, October 16, 2024 1:44 AM
> To: Ye, MingjinX <mingjinx.ye@intel.com>
> Cc: dev@dpdk.org; Su, Simei <simei.su@intel.com>; Wenjun Wu
> <wenjun1.wu@intel.com>; Rybalchenko, Kirill <kirill.rybalchenko@intel.com>
> Subject: Re: [PATCH v7] examples/ptpclient: add frequency adjustment
> 
> On Tue, 15 Oct 2024 08:22:01 +0000
> Mingjin Ye <mingjinx.ye@intel.com> wrote:
> 
> > This patch adds PI servo controller to support frequency adjustment
> > API for IEEE1588 PTP.
> >
> > For example, the command for starting ptpclient with PI controller is:
> > dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1 -c 1
> >
> > Signed-off-by: Simei Su <simei.su@intel.com>
> > Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
> > Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
> 
> This fails build testing, is it depending on some other patch?


The next version of '[v6,3/3] examples/ptpclient: add frequency adjustment'. The current
dependent patch has not been merged into the main branch, and the CI will fail to compile.

V6 patch set: https://patchwork.dpdk.org/project/dpdk/list/?series=33428

^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v7] examples/ptpclient: add frequency adjustment
  2024-10-15 17:57                   ` Ferruh Yigit
@ 2024-10-16  1:41                     ` Ye, MingjinX
  2024-10-16 10:02                       ` Ferruh Yigit
  0 siblings, 1 reply; 49+ messages in thread
From: Ye, MingjinX @ 2024-10-16  1:41 UTC (permalink / raw)
  To: Ferruh Yigit, Stephen Hemminger
  Cc: dev, Su, Simei, Wenjun Wu, Rybalchenko, Kirill

Hi Ferruh Yigit,

The patch is in 'Changes Requested' status. Can you review it for me?

Regards,
Mingjin

> -----Original Message-----
> From: Ferruh Yigit <ferruh.yigit@amd.com>
> Sent: Wednesday, October 16, 2024 1:58 AM
> To: Stephen Hemminger <stephen@networkplumber.org>; Ye, MingjinX
> <mingjinx.ye@intel.com>
> Cc: dev@dpdk.org; Su, Simei <simei.su@intel.com>; Wenjun Wu
> <wenjun1.wu@intel.com>; Rybalchenko, Kirill <kirill.rybalchenko@intel.com>
> Subject: Re: [PATCH v7] examples/ptpclient: add frequency adjustment
> 
> On 10/15/2024 6:43 PM, Stephen Hemminger wrote:
> > On Tue, 15 Oct 2024 08:22:01 +0000
> > Mingjin Ye <mingjinx.ye@intel.com> wrote:
> >
> >> This patch adds PI servo controller to support frequency adjustment
> >> API for IEEE1588 PTP.
> >>
> >> For example, the command for starting ptpclient with PI controller is:
> >> dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1 -c 1
> >>
> >> Signed-off-by: Simei Su <simei.su@intel.com>
> >> Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
> >> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
> >
> > This fails build testing, is it depending on some other patch?
> >
> 
> It is based on next-net, and uses a new API in next-net.
> 
> We may proceed with this patch after next-net pulled to main.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v7] examples/ptpclient: add frequency adjustment
  2024-10-16  1:41                     ` Ye, MingjinX
@ 2024-10-16 10:02                       ` Ferruh Yigit
  2024-11-04  9:47                         ` Ye, MingjinX
  0 siblings, 1 reply; 49+ messages in thread
From: Ferruh Yigit @ 2024-10-16 10:02 UTC (permalink / raw)
  To: Ye, MingjinX, Stephen Hemminger
  Cc: dev, Su, Simei, Wenjun Wu, Rybalchenko, Kirill

On 10/16/2024 2:41 AM, Ye, MingjinX wrote:
> Hi Ferruh Yigit,
> 
> The patch is in 'Changes Requested' status. Can you review it for me?
> 

Hi Mingjin,

I don't remember changing the status to "Changes Requested", there may
be a mistake, I will update it as "New".

At quick look it is good, but can we have a review from Kirill as
maintainer of the sample application.

If review comes timely, I can merge it directly to next-net.

> Regards,
> Mingjin
> 
>> -----Original Message-----
>> From: Ferruh Yigit <ferruh.yigit@amd.com>
>> Sent: Wednesday, October 16, 2024 1:58 AM
>> To: Stephen Hemminger <stephen@networkplumber.org>; Ye, MingjinX
>> <mingjinx.ye@intel.com>
>> Cc: dev@dpdk.org; Su, Simei <simei.su@intel.com>; Wenjun Wu
>> <wenjun1.wu@intel.com>; Rybalchenko, Kirill <kirill.rybalchenko@intel.com>
>> Subject: Re: [PATCH v7] examples/ptpclient: add frequency adjustment
>>
>> On 10/15/2024 6:43 PM, Stephen Hemminger wrote:
>>> On Tue, 15 Oct 2024 08:22:01 +0000
>>> Mingjin Ye <mingjinx.ye@intel.com> wrote:
>>>
>>>> This patch adds PI servo controller to support frequency adjustment
>>>> API for IEEE1588 PTP.
>>>>
>>>> For example, the command for starting ptpclient with PI controller is:
>>>> dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1 -c 1
>>>>
>>>> Signed-off-by: Simei Su <simei.su@intel.com>
>>>> Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
>>>> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
>>>
>>> This fails build testing, is it depending on some other patch?
>>>
>>
>> It is based on next-net, and uses a new API in next-net.
>>
>> We may proceed with this patch after next-net pulled to main.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v7] examples/ptpclient: add frequency adjustment
  2024-10-15  8:22               ` [PATCH v7] " Mingjin Ye
  2024-10-15 17:43                 ` Stephen Hemminger
@ 2024-11-04  9:30                 ` Rybalchenko, Kirill
  1 sibling, 0 replies; 49+ messages in thread
From: Rybalchenko, Kirill @ 2024-11-04  9:30 UTC (permalink / raw)
  To: Ye, MingjinX, dev; +Cc: Su, Simei, Wenjun Wu



> -----Original Message-----
> From: Ye, MingjinX <mingjinx.ye@intel.com>
> Sent: Tuesday 15 October 2024 09:22
> To: dev@dpdk.org
> Cc: Ye, MingjinX <mingjinx.ye@intel.com>; Su, Simei <simei.su@intel.com>;
> Wenjun Wu <wenjun1.wu@intel.com>; Rybalchenko, Kirill
> <kirill.rybalchenko@intel.com>
> Subject: [PATCH v7] examples/ptpclient: add frequency adjustment
> 
> This patch adds PI servo controller to support frequency
> adjustment API for IEEE1588 PTP.
> 
> For example, the command for starting ptpclient with PI controller is:
> dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1 -c 1
> 
> Signed-off-by: Simei Su <simei.su@intel.com>
> Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
Acked-by: Kirill Rybalchenko <kirill.rybalchenko@intel.com>

> ---
> v2: Add doc.
> ---
> v3: Enable The servo controller needs to support frequency adjustment.
> ---
> v2: Enable servo controller, PMD must
> 				support ‘rte_eth_timesync_adjust_freq()’
> API.
> ---
>  doc/guides/sample_app_ug/ptpclient.rst |  15 +-
>  examples/ptpclient/ptpclient.c         | 297 +++++++++++++++++++++++--
>  2 files changed, 289 insertions(+), 23 deletions(-)
> 
> diff --git a/doc/guides/sample_app_ug/ptpclient.rst
> b/doc/guides/sample_app_ug/ptpclient.rst
> index d47e942738..014d5f477c 100644
> --- a/doc/guides/sample_app_ug/ptpclient.rst
> +++ b/doc/guides/sample_app_ug/ptpclient.rst
> @@ -50,6 +50,10 @@ The adjustment for slave can be represented as:
>  If the command line parameter ``-T 1`` is used the application also
>  synchronizes the PTP PHC clock with the Linux kernel clock.
> 
> +If the command line parameter ``-c 1`` is used, the application will also
> +use the servo of the local clock. Only one type of servo is currently
> +implemented, the PI controller. Default 0 (not used).
> +
>  Compiling the Application
>  -------------------------
> 
> @@ -65,7 +69,7 @@ To run the example in a ``linux`` environment:
> 
>  .. code-block:: console
> 
> -    ./<build_dir>/examples/dpdk-ptpclient -l 1 -n 4 -- -p 0x1 -T 0
> +    ./<build_dir>/examples/dpdk-ptpclient -l 1 -n 4 -- -p 0x1 -T 0 -c 1
> 
>  Refer to *DPDK Getting Started Guide* for general information on running
>  applications and the Environment Abstraction Layer (EAL) options.
> @@ -73,7 +77,14 @@ applications and the Environment Abstraction Layer
> (EAL) options.
>  * ``-p portmask``: Hexadecimal portmask.
>  * ``-T 0``: Update only the PTP slave clock.
>  * ``-T 1``: Update the PTP slave clock and synchronize the Linux Kernel to the
> PTP clock.
> -
> +* ``-c 0``: Not used clock servo controller.
> +* ``-c 1``: The clock servo PI controller is used and the log will print
> information
> +            about "master offset". Note that the PMD needs to support
> +            the ``rte_eth_timesync_adjust_freq()`` API to enable the servo
> controller.
> +
> +Also, by adding ``-T 1`` and ``-c 1`` , the ``master offset`` value printed in the
> +log will slowly converge and eventually stabilise at the nanosecond level. The
> +synchronisation accuracy is much higher compared to not using a servo
> controller.
> 
>  Code Explanation
>  ----------------
> diff --git a/examples/ptpclient/ptpclient.c b/examples/ptpclient/ptpclient.c
> index afb61bba51..e8f6f95f18 100644
> --- a/examples/ptpclient/ptpclient.c
> +++ b/examples/ptpclient/ptpclient.c
> @@ -46,6 +46,35 @@ static volatile bool force_quit;
>  #define KERNEL_TIME_ADJUST_LIMIT  20000
>  #define PTP_PROTOCOL             0x88F7
> 
> +#define KP 0.7
> +#define KI 0.3
> +#define FREQ_EST_MARGIN 0.001
> +
> +enum servo_state {
> +	SERVO_UNLOCKED,
> +	SERVO_JUMP,
> +	SERVO_LOCKED,
> +};
> +
> +struct pi_servo {
> +	double offset[2];
> +	double local[2];
> +	double drift;
> +	double last_freq;
> +	int count;
> +
> +	double max_frequency;
> +	double step_threshold;
> +	double first_step_threshold;
> +	int first_update;
> +};
> +
> +enum controller_mode {
> +	MODE_NONE,
> +	MODE_PI,
> +	MAX_ALL
> +} mode = MODE_NONE;
> +
>  struct rte_mempool *mbuf_pool;
>  uint32_t ptp_enabled_port_mask;
>  uint8_t ptp_enabled_port_nb;
> @@ -135,6 +164,9 @@ struct ptpv2_data_slave_ordinary {
>  	uint8_t ptpset;
>  	uint8_t kernel_time_set;
>  	uint16_t current_ptp_port;
> +	int64_t master_offset;
> +	int64_t path_delay;
> +	struct pi_servo *servo;
>  };
> 
>  static struct ptpv2_data_slave_ordinary ptp_data;
> @@ -262,6 +294,19 @@ port_init(uint16_t port, struct rte_mempool
> *mbuf_pool)
>  		return retval;
>  	}
> 
> +	/*
> +	 * If the clock servo controller is enabled, the PMD must support
> +	 * adjustment of the clock frequency.
> +	 */
> +	if (mode != MODE_NONE) {
> +		retval = rte_eth_timesync_adjust_freq(port, 0);
> +		if (retval == -ENOTSUP) {
> +			printf("The servo controller cannot work on devices
> that"
> +					" do not support frequency
> adjustment.\n");
> +			return retval;
> +		}
> +	}
> +
>  	return 0;
>  }
> 
> @@ -293,36 +338,44 @@ print_clock_info(struct ptpv2_data_slave_ordinary
> *ptp_data)
>  			ptp_data->tstamp3.tv_sec,
>  			(ptp_data->tstamp3.tv_nsec));
> 
> -	printf("\nT4 - Master Clock.  %lds %ldns ",
> +	printf("\nT4 - Master Clock.  %lds %ldns\n",
>  			ptp_data->tstamp4.tv_sec,
>  			(ptp_data->tstamp4.tv_nsec));
> 
> -	printf("\nDelta between master and slave clocks:%"PRId64"ns\n",
> +	if (mode == MODE_NONE) {
> +		printf("\nDelta between master and slave
> clocks:%"PRId64"ns\n",
>  			ptp_data->delta);
> 
> -	clock_gettime(CLOCK_REALTIME, &sys_time);
> -	rte_eth_timesync_read_time(ptp_data->current_ptp_port,
> &net_time);
> +		clock_gettime(CLOCK_REALTIME, &sys_time);
> +		rte_eth_timesync_read_time(ptp_data->current_ptp_port,
> +					   &net_time);
> 
> -	time_t ts = net_time.tv_sec;
> +		time_t ts = net_time.tv_sec;
> 
> -	printf("\n\nComparison between Linux kernel Time and PTP:");
> +		printf("\n\nComparison between Linux kernel Time and
> PTP:");
> 
> -	printf("\nCurrent PTP Time: %.24s %.9ld ns",
> +		printf("\nCurrent PTP Time: %.24s %.9ld ns",
>  			ctime(&ts), net_time.tv_nsec);
> 
> -	nsec = (int64_t)timespec64_to_ns(&net_time) -
> +		nsec = (int64_t)timespec64_to_ns(&net_time) -
>  			(int64_t)timespec64_to_ns(&sys_time);
> -	ptp_data->new_adj = ns_to_timeval(nsec);
> +		ptp_data->new_adj = ns_to_timeval(nsec);
> 
> -	gettimeofday(&ptp_data->new_adj, NULL);
> +		gettimeofday(&ptp_data->new_adj, NULL);
> 
> -	time_t tp = ptp_data->new_adj.tv_sec;
> +		time_t tp = ptp_data->new_adj.tv_sec;
> 
> -	printf("\nCurrent SYS Time: %.24s %.6ld ns",
> -				ctime(&tp), ptp_data->new_adj.tv_usec);
> +		printf("\nCurrent SYS Time: %.24s %.6ld ns",
> +			ctime(&tp), ptp_data->new_adj.tv_usec);
> 
> -	printf("\nDelta between PTP and Linux Kernel time:%"PRId64"ns\n",
> -				nsec);
> +		printf("\nDelta between PTP and Linux Kernel
> time:%"PRId64"ns\n",
> +			nsec);
> +	}
> +
> +	if (mode == MODE_PI) {
> +		printf("path delay: %"PRId64"ns\n", ptp_data->path_delay);
> +		printf("master offset: %"PRId64"ns\n", ptp_data-
> >master_offset);
> +	}
> 
>  	printf("[Ctrl+C to quit]\n");
> 
> @@ -529,6 +582,149 @@ update_kernel_time(void)
> 
>  }
> 
> +static void
> +clock_path_delay(struct ptpv2_data_slave_ordinary *ptp_data)
> +{
> +	uint64_t t1_ns, t2_ns, t3_ns, t4_ns;
> +	int64_t pd, diff;
> +
> +	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
> +	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
> +	t3_ns = timespec64_to_ns(&ptp_data->tstamp3);
> +	t4_ns = timespec64_to_ns(&ptp_data->tstamp4);
> +
> +	pd = (t2_ns - t3_ns) + (t4_ns - t1_ns);
> +	diff = t3_ns - t2_ns;
> +	if (diff <= INT32_MAX && diff >= INT32_MIN)
> +		ptp_data->path_delay = pd / 2;
> +	else
> +		ptp_data->path_delay = 0;
> +}
> +
> +static double
> +pi_sample(struct pi_servo *s, int64_t offset, double local_ts,
> +	  enum servo_state *state)
> +{
> +	double ki_term, ppb = s->last_freq;
> +	double freq_est_interval, localdiff;
> +
> +	switch (s->count) {
> +	case 0:
> +		s->offset[0] = offset;
> +		s->local[0] = local_ts;
> +		*state = SERVO_UNLOCKED;
> +		s->count = 1;
> +		break;
> +	case 1:
> +		s->offset[1] = offset;
> +		s->local[1] = local_ts;
> +
> +		/* Make sure the first sample is older than the second. */
> +		if (s->local[0] >= s->local[1]) {
> +			*state = SERVO_UNLOCKED;
> +			s->count = 0;
> +			break;
> +		}
> +
> +		/* Wait long enough before estimating the frequency offset.
> */
> +		localdiff = (s->local[1] - s->local[0]) / 1e9;
> +		localdiff += localdiff * FREQ_EST_MARGIN;
> +		freq_est_interval = 0.016 / KI;
> +		if (freq_est_interval > 1000.0)
> +			freq_est_interval = 1000.0;
> +
> +		if (localdiff < freq_est_interval) {
> +			*state = SERVO_UNLOCKED;
> +			break;
> +		}
> +
> +		/* Adjust drift by the measured frequency offset. */
> +		s->drift += (1e9 - s->drift) * (s->offset[1] - s->offset[0]) /
> +						(s->local[1] - s->local[0]);
> +
> +		if (s->drift < -s->max_frequency)
> +			s->drift = -s->max_frequency;
> +		else if (s->drift > s->max_frequency)
> +			s->drift = s->max_frequency;
> +
> +		if ((s->first_update &&
> +		     s->first_step_threshold &&
> +		     s->first_step_threshold < llabs(offset)) ||
> +		    (s->step_threshold &&
> +		     s->step_threshold < llabs(offset)))
> +			*state = SERVO_JUMP;
> +		else
> +			*state = SERVO_LOCKED;
> +
> +		ppb = s->drift;
> +		s->count = 2;
> +		break;
> +	case 2:
> +		/*
> +		 * reset the clock servo when offset is greater than the max
> +		 * offset value. Note that the clock jump will be performed in
> +		 * step 1, so it is not necessary to have clock jump
> +		 * immediately. This allows re-calculating drift as in initial
> +		 * clock startup.
> +		 */
> +		if (s->step_threshold &&
> +		    s->step_threshold < llabs(offset)) {
> +			*state = SERVO_UNLOCKED;
> +			s->count = 0;
> +			break;
> +		}
> +
> +		ki_term = KI * offset;
> +		ppb = KP * offset + s->drift + ki_term;
> +		if (ppb < -s->max_frequency)
> +			ppb = -s->max_frequency;
> +		else if (ppb > s->max_frequency)
> +			ppb = s->max_frequency;
> +		else
> +			s->drift += ki_term;
> +
> +		*state = SERVO_LOCKED;
> +		break;
> +	}
> +
> +	s->last_freq = ppb;
> +	return ppb;
> +}
> +
> +static void
> +ptp_adjust_servo(struct ptpv2_data_slave_ordinary *ptp_data)
> +{
> +	uint64_t t1_ns, t2_ns;
> +	double adj_freq;
> +	enum servo_state state = SERVO_UNLOCKED;
> +
> +	t1_ns = timespec64_to_ns(&ptp_data->tstamp1);
> +	t2_ns = timespec64_to_ns(&ptp_data->tstamp2);
> +	ptp_data->master_offset = t2_ns - t1_ns - ptp_data->path_delay;
> +	if (!ptp_data->path_delay)
> +		return;
> +
> +	adj_freq = pi_sample(ptp_data->servo, ptp_data->master_offset,
> t2_ns,
> +		     &state);
> +
> +	switch (state) {
> +	case SERVO_UNLOCKED:
> +		break;
> +	case SERVO_JUMP:
> +		ptp_data->servo->first_update = 0;
> +		rte_eth_timesync_adjust_freq(ptp_data->portid,
> +						-(long)(adj_freq * 65.536));
> +		rte_eth_timesync_adjust_time(ptp_data->portid,
> +					     -ptp_data->master_offset);
> +		break;
> +	case SERVO_LOCKED:
> +		ptp_data->servo->first_update = 0;
> +		rte_eth_timesync_adjust_freq(ptp_data->portid,
> +					     -(long)(adj_freq * 65.536));
> +		break;
> +	}
> +}
> +
>  /*
>   * Parse the DELAY_RESP message.
>   */
> @@ -553,11 +749,16 @@ parse_drsp(struct ptpv2_data_slave_ordinary
> *ptp_data)
>  				((uint64_t)ntohl(rx_tstamp->sec_lsb)) |
>  				(((uint64_t)ntohs(rx_tstamp->sec_msb)) <<
> 32);
> 
> -			/* Evaluate the delta for adjustment. */
> -			ptp_data->delta = delta_eval(ptp_data);
> +			if (mode == MODE_PI) {
> +				clock_path_delay(ptp_data);
> +				ptp_adjust_servo(ptp_data);
> +			} else {
> +				/* Evaluate the delta for adjustment. */
> +				ptp_data->delta = delta_eval(ptp_data);
> 
> -			rte_eth_timesync_adjust_time(ptp_data->portid,
> -						     ptp_data->delta);
> +				rte_eth_timesync_adjust_time(ptp_data-
> >portid,
> +								ptp_data-
> >delta);
> +			}
> 
>  			ptp_data->current_ptp_port = ptp_data->portid;
> 
> @@ -652,7 +853,9 @@ print_usage(const char *prgname)
>  	printf("%s [EAL options] -- -p PORTMASK -T VALUE\n"
>  		" -T VALUE: 0 - Disable, 1 - Enable Linux Clock"
>  		" Synchronization (0 default)\n"
> -		" -p PORTMASK: hexadecimal bitmask of ports to
> configure\n",
> +		" -p PORTMASK: hexadecimal bitmask of ports to configure\n"
> +		" -c CONTROLLER: 0 - Not used, 1 - PI. The servo which is"
> +		" used to synchronize the local clock. (0 default)\n",
>  		prgname);
>  }
> 
> @@ -688,6 +891,36 @@ parse_ptp_kernel(const char *param)
>  	return 1;
>  }
> 
> +static int
> +parse_ptp_servo_mode(const char *param)
> +{
> +	char *end = NULL;
> +	unsigned long pm;
> +
> +	/* Parse the hexadecimal string. */
> +	pm = strtoul(param, &end, 10);
> +
> +	if ((param[0] == '\0') || (end == NULL) || (*end != '\0'))
> +		return -1;
> +
> +	return pm;
> +}
> +
> +static void
> +servo_init(struct pi_servo *servo)
> +{
> +	memset(servo, 0x00, sizeof(*servo));
> +
> +	servo->drift = 100000000;
> +	servo->last_freq = 100000000;
> +	servo->count = 0;
> +
> +	servo->max_frequency = 100000000;
> +	servo->step_threshold = 0.1 * NSEC_PER_SEC;
> +	servo->first_step_threshold = 0.00002 * NSEC_PER_SEC;
> +	servo->first_update = 1;
> +}
> +
>  /* Parse the commandline arguments. */
>  static int
>  ptp_parse_args(int argc, char **argv)
> @@ -700,7 +933,7 @@ ptp_parse_args(int argc, char **argv)
> 
>  	argvopt = argv;
> 
> -	while ((opt = getopt_long(argc, argvopt, "p:T:",
> +	while ((opt = getopt_long(argc, argvopt, "p:T:c:",
>  				  lgopts, &option_index)) != EOF) {
> 
>  		switch (opt) {
> @@ -724,6 +957,17 @@ ptp_parse_args(int argc, char **argv)
> 
>  			ptp_data.kernel_time_set = ret;
>  			break;
> +		case 'c':
> +			ret = parse_ptp_servo_mode(optarg);
> +			if (ret == 0) {
> +				mode = MODE_NONE;
> +			} else if (ret == 1) {
> +				mode = MODE_PI;
> +			} else {
> +				print_usage(prgname);
> +				return -1;
> +			}
> +			break;
> 
>  		default:
>  			print_usage(prgname);
> @@ -778,6 +1022,14 @@ main(int argc, char *argv[])
>  		rte_exit(EXIT_FAILURE, "Error with PTP initialization\n");
>  	/* >8 End of parsing specific arguments. */
> 
> +	if (mode == MODE_PI) {
> +		ptp_data.servo = malloc(sizeof(*(ptp_data.servo)));
> +		if (!ptp_data.servo)
> +			rte_exit(EXIT_FAILURE, "no memory for servo\n");
> +
> +		servo_init(ptp_data.servo);
> +	}
> +
>  	/* Check that there is an even number of ports to send/receive on. */
>  	nb_ports = rte_eth_dev_count_avail();
> 
> @@ -831,6 +1083,9 @@ main(int argc, char *argv[])
>  		rte_eth_dev_close(portid);
>  	}
> 
> +	if (mode == MODE_PI)
> +		free(ptp_data.servo);
> +
>  	/* clean up the EAL */
>  	rte_eal_cleanup();
> 
> --
> 2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v7] examples/ptpclient: add frequency adjustment
  2024-10-16 10:02                       ` Ferruh Yigit
@ 2024-11-04  9:47                         ` Ye, MingjinX
  0 siblings, 0 replies; 49+ messages in thread
From: Ye, MingjinX @ 2024-11-04  9:47 UTC (permalink / raw)
  To: Ferruh Yigit; +Cc: dev, Rybalchenko, Kirill

Hi Ferruh Yigit,

Kirill has acked-by my patch. Can you please have it my patch merged as RC2 11/6 is coming near? Thank you.

Regards,
Mingjin

> -----Original Message-----
> From: Ferruh Yigit <ferruh.yigit@amd.com>
> Sent: Wednesday, October 16, 2024 6:03 PM
> To: Ye, MingjinX <mingjinx.ye@intel.com>; Stephen Hemminger
> <stephen@networkplumber.org>
> Cc: dev@dpdk.org; Su, Simei <simei.su@intel.com>; Wenjun Wu
> <wenjun1.wu@intel.com>; Rybalchenko, Kirill <kirill.rybalchenko@intel.com>
> Subject: Re: [PATCH v7] examples/ptpclient: add frequency adjustment
> 
> On 10/16/2024 2:41 AM, Ye, MingjinX wrote:
> > Hi Ferruh Yigit,
> >
> > The patch is in 'Changes Requested' status. Can you review it for me?
> >
> 
> Hi Mingjin,
> 
> I don't remember changing the status to "Changes Requested", there may
> be a mistake, I will update it as "New".
> 
> At quick look it is good, but can we have a review from Kirill as maintainer of
> the sample application.
> 
> If review comes timely, I can merge it directly to next-net.
> 
> > Regards,
> > Mingjin
> >
> >> -----Original Message-----
> >> From: Ferruh Yigit <ferruh.yigit@amd.com>
> >> Sent: Wednesday, October 16, 2024 1:58 AM
> >> To: Stephen Hemminger <stephen@networkplumber.org>; Ye, MingjinX
> >> <mingjinx.ye@intel.com>
> >> Cc: dev@dpdk.org; Su, Simei <simei.su@intel.com>; Wenjun Wu
> >> <wenjun1.wu@intel.com>; Rybalchenko, Kirill
> >> <kirill.rybalchenko@intel.com>
> >> Subject: Re: [PATCH v7] examples/ptpclient: add frequency adjustment
> >>
> >> On 10/15/2024 6:43 PM, Stephen Hemminger wrote:
> >>> On Tue, 15 Oct 2024 08:22:01 +0000
> >>> Mingjin Ye <mingjinx.ye@intel.com> wrote:
> >>>
> >>>> This patch adds PI servo controller to support frequency adjustment
> >>>> API for IEEE1588 PTP.
> >>>>
> >>>> For example, the command for starting ptpclient with PI controller is:
> >>>> dpdk-ptpclient -a 0000:81:00.0 -c 1 -n 3 -- -T 0 -p 0x1 -c 1
> >>>>
> >>>> Signed-off-by: Simei Su <simei.su@intel.com>
> >>>> Signed-off-by: Wenjun Wu <wenjun1.wu@intel.com>
> >>>> Signed-off-by: Mingjin Ye <mingjinx.ye@intel.com>
> >>>
> >>> This fails build testing, is it depending on some other patch?
> >>>
> >>
> >> It is based on next-net, and uses a new API in next-net.
> >>
> >> We may proceed with this patch after next-net pulled to main.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v2 0/3] add frequency adjustment support for PTP
@ 2024-09-06  5:19 Mingjin Ye
  0 siblings, 0 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-09-06  5:19 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye

[1/3] ethdev: add frequency adjustment API
[2/3] net/ice: add frequency adjustment support for PTP
[3/3] examples/ptpclient: add frequency adjustment support

Mingjin Ye (3):
  ethdev: add frequency adjustment API
  net/ice: add frequency adjustment support for PTP
  examples/ptpclient: add frequency adjustment support

 doc/guides/nics/features.rst           |   4 +-
 doc/guides/nics/ice.rst                |  15 ++
 doc/guides/rel_notes/release_24_11.rst |  30 +--
 drivers/net/ice/ice_ethdev.c           | 177 +++++++++++----
 drivers/net/ice/ice_ethdev.h           |   2 +
 drivers/net/ice/ice_rxtx.c             |   4 +-
 examples/ptpclient/ptpclient.c         | 300 ++++++++++++++++++++++---
 lib/ethdev/ethdev_driver.h             |   5 +
 lib/ethdev/ethdev_trace.h              |   9 +
 lib/ethdev/ethdev_trace_points.c       |   3 +
 lib/ethdev/rte_ethdev.c                |  18 ++
 lib/ethdev/rte_ethdev.h                |  20 ++
 lib/ethdev/version.map                 |   3 +
 13 files changed, 482 insertions(+), 108 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v2 0/3] add frequency adjustment support for PTP
@ 2024-09-05 10:26 Mingjin Ye
  0 siblings, 0 replies; 49+ messages in thread
From: Mingjin Ye @ 2024-09-05 10:26 UTC (permalink / raw)
  To: dev; +Cc: Mingjin Ye

[1/3] ethdev: add frequency adjustment API
[2/3] net/ice: add frequency adjustment support for PTP
[3/3] examples/ptpclient: add frequency adjustment support

Mingjin Ye (3):
  ethdev: add frequency adjustment API
  net/ice: add frequency adjustment support for PTP
  examples/ptpclient: add frequency adjustment support

 doc/guides/nics/features.rst           |   4 +-
 doc/guides/nics/ice.rst                |  15 ++
 doc/guides/rel_notes/release_24_11.rst |  30 +--
 drivers/net/ice/ice_ethdev.c           | 177 +++++++++++----
 drivers/net/ice/ice_ethdev.h           |   2 +
 drivers/net/ice/ice_rxtx.c             |   4 +-
 examples/ptpclient/ptpclient.c         | 300 ++++++++++++++++++++++---
 lib/ethdev/ethdev_driver.h             |   5 +
 lib/ethdev/ethdev_trace.h              |   9 +
 lib/ethdev/ethdev_trace_points.c       |   3 +
 lib/ethdev/rte_ethdev.c                |  18 ++
 lib/ethdev/rte_ethdev.h                |  21 ++
 lib/ethdev/version.map                 |   3 +
 13 files changed, 483 insertions(+), 108 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

end of thread, other threads:[~2024-11-04  9:47 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-09-05  1:31 [PATCH 0/3] add frequency adjustment support for PTP Mingjin Ye
2024-09-05  1:31 ` [PATCH 1/3] ethdev: add frequency adjustment API Mingjin Ye
2024-09-05  1:31 ` [PATCH 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
2024-09-05  1:31 ` [PATCH 3/3] examples/ptpclient: add frequency adjustment support Mingjin Ye
2024-09-10  9:13 ` [PATCH v2 0/3] add frequency adjustment support for PTP Mingjin Ye
2024-09-10  9:13   ` [PATCH v2 1/3] ethdev: add frequency adjustment API Mingjin Ye
2024-09-22 18:59     ` Ferruh Yigit
2024-09-23  3:11     ` fengchengwen
2024-09-23  6:28       ` Ye, MingjinX
2024-09-10  9:13   ` [PATCH v2 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
2024-09-10  9:13   ` [PATCH v2 3/3] examples/ptpclient: add frequency adjustment support Mingjin Ye
2024-09-22 19:06   ` [PATCH v2 0/3] add frequency adjustment support for PTP Ferruh Yigit
2024-09-23  2:47     ` Ye, MingjinX
2024-09-30  8:42   ` [PATCH v3 " Mingjin Ye
2024-09-30  8:42     ` [PATCH v3 1/3] ethdev: add frequency adjustment API Mingjin Ye
2024-09-30 20:51       ` Ferruh Yigit
2024-09-30  8:42     ` [PATCH v3 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
2024-09-30  8:42     ` [PATCH v3 3/3] examples/ptpclient: add frequency adjustment support Mingjin Ye
2024-09-30 20:51       ` Ferruh Yigit
2024-09-30 20:53     ` [PATCH v3 0/3] add frequency adjustment support for PTP Ferruh Yigit
2024-10-10  9:32     ` [PATCH v4 " Mingjin Ye
2024-10-10  9:32       ` [PATCH v4 1/3] ethdev: add frequency adjustment API Mingjin Ye
2024-10-11  2:53         ` [PATCH v5 0/3] add frequency adjustment support for PTP Mingjin Ye
2024-10-11  2:53           ` [PATCH v5 1/3] ethdev: add frequency adjustment API Mingjin Ye
2024-10-11  2:53           ` [PATCH v5 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
2024-10-11  2:53           ` [PATCH v5 3/3] examples/ptpclient: add frequency adjustment Mingjin Ye
2024-10-11  6:34           ` [PATCH v6 0/3] add frequency adjustment support for PTP Mingjin Ye
2024-10-11  6:34             ` [PATCH v6 1/3] ethdev: add frequency adjustment API Mingjin Ye
2024-10-11 23:44               ` Ferruh Yigit
2024-10-11  6:34             ` [PATCH v6 2/3] net/ice: add frequency adjustment support for PTP Mingjin Ye
2024-10-11  8:02               ` Bruce Richardson
2024-10-11  9:28                 ` Ye, MingjinX
2024-10-11 23:44                 ` Ferruh Yigit
2024-10-11  6:34             ` [PATCH v6 3/3] examples/ptpclient: add frequency adjustment Mingjin Ye
2024-10-11 19:37               ` Ferruh Yigit
2024-10-15  8:22               ` [PATCH v7] " Mingjin Ye
2024-10-15 17:43                 ` Stephen Hemminger
2024-10-15 17:57                   ` Ferruh Yigit
2024-10-16  1:41                     ` Ye, MingjinX
2024-10-16 10:02                       ` Ferruh Yigit
2024-11-04  9:47                         ` Ye, MingjinX
2024-10-16  1:23                   ` Ye, MingjinX
2024-11-04  9:30                 ` Rybalchenko, Kirill
2024-10-11 23:44             ` [PATCH v6 0/3] add frequency adjustment support for PTP Ferruh Yigit
2024-10-10  9:32       ` [PATCH v4 2/3] net/ice: " Mingjin Ye
2024-10-10 10:34         ` Bruce Richardson
2024-10-10  9:32       ` [PATCH v4 3/3] examples/ptpclient: add frequency adjustment Mingjin Ye
2024-09-05 10:26 [PATCH v2 0/3] add frequency adjustment support for PTP Mingjin Ye
2024-09-06  5:19 Mingjin Ye

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).