* [PATCH iwl-next 0/5] ice: implement low latency PHY timer updates
@ 2024-12-04 18:03 Anton Nadezhdin
2024-12-04 18:03 ` [PATCH iwl-next 1/5] ice: use rd32_poll_timeout_atomic in ice_read_phy_tstamp_ll_e810 Anton Nadezhdin
` (4 more replies)
0 siblings, 5 replies; 8+ messages in thread
From: Anton Nadezhdin @ 2024-12-04 18:03 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, richardcochran,
Anton Nadezhdin
Programming the PHY registers in preparation for an increment value change
or a timer adjustment on E810 requires issuing Admin Queue commands for
each PHY register. It has been found that the firmware Admin Queue
processing occasionally has delays of tens or rarely up to hundreds of
milliseconds. This delay cascades to failures in the PTP applications which
depend on these updates being low latency.
Consider a standard PTP profile with a sync rate of 16 times per second.
This means there is ~62 milliseconds between sync messages. A complete
cycle of the PTP algorithm
1) Sync message (with Tx timestamp) from source
2) Follow-up message from source
3) Delay request (with Tx timestamp) from sink
4) Delay response (with Rx timestamp of request) from source
5) measure instantaneous clock offset
6) request time adjustment via CLOCK_ADJTIME systemcall
The Tx timestamps have a default maximum timeout of 10 milliseconds. If we
assume that the maximum possible time is used, this leaves us with ~42
milliseconds of processing time for a complete cycle.
The CLOCK_ADJTIME system call is synchronous and will block until the
driver completes its timer adjustment or frequency change.
If the writes to prepare the PHY timers get hit by a latency spike of 50
milliseconds, then the PTP application will be delayed past the point where
the next cycle should start. Packets from the next cycle may have already
arrived and are waiting on the socket.
In particular, LinuxPTP ptp4l may start complaining about missing an
announce message from the source, triggering a fault. In addition, the
clockcheck logic it uses may trigger. This clockcheck failure occurs
because the timestamp captured by hardware is compared against a reading of
CLOCK_MONOTONIC. It is assumed that the time when the Rx timestamp is
captured and the read from CLOCK_MONOTONIC are relatively close together.
This is not the case if there is a significant delay to processing the Rx
packet.
Newer firmware supports programming the PHY registers over a low latency
interface which bypasses the Admin Queue. Instead, software writes to the
REG_LL_PROXY_H and REG_LL_PROXY_L registers. Firmware reads these registers and
then programs the PHY timers.
Implement functions to use this interface when available to program the PHY
timers instead of using the Admin Queue. This avoids the Admin Queue
latency and ensures that adjustments happen within acceptable latency
bounds.
Jacob Keller (5):
ice: use rd32_poll_timeout_atomic in ice_read_phy_tstamp_ll_e810
ice: rename TS_LL_READ* macros to REG_LL_PROXY_H_*
ice: add lock to protect low latency interface
ice: check low latency PHY timer update firmware capability
ice: implement low latency PHY timer updates
drivers/net/ethernet/intel/ice/ice_common.c | 3 +
drivers/net/ethernet/intel/ice/ice_osdep.h | 3 +
drivers/net/ethernet/intel/ice/ice_ptp.c | 48 ++++--
drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 155 +++++++++++++++++---
drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 17 ++-
drivers/net/ethernet/intel/ice/ice_type.h | 12 ++
6 files changed, 204 insertions(+), 34 deletions(-)
base-commit: 4376b34cf49c2f38e761beacd173d1dc15a255fd
--
2.42.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH iwl-next 1/5] ice: use rd32_poll_timeout_atomic in ice_read_phy_tstamp_ll_e810
2024-12-04 18:03 [PATCH iwl-next 0/5] ice: implement low latency PHY timer updates Anton Nadezhdin
@ 2024-12-04 18:03 ` Anton Nadezhdin
2024-12-06 13:30 ` Simon Horman
2024-12-04 18:03 ` [PATCH iwl-next 2/5] ice: rename TS_LL_READ* macros to REG_LL_PROXY_H_* Anton Nadezhdin
` (3 subsequent siblings)
4 siblings, 1 reply; 8+ messages in thread
From: Anton Nadezhdin @ 2024-12-04 18:03 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, richardcochran,
Jacob Keller, Karol Kolacinski, Milena Olech, Anton Nadezhdin
From: Jacob Keller <jacob.e.keller@intel.com>
The ice_read_phy_tstamp_ll_e810 function repeatedly reads the PF_SB_ATQBAL
register until the TS_LL_READ_TS bit is cleared. This is a perfect
candidate for using rd32_poll_timeout. However, the default implementation
uses a sleep-based wait.
Add a new rd32_poll_timeout_atomic macro which is based on the non-sleeping
read_poll_timeout_atomic implementation. Use this to replace the loop
reading in the ice_read_phy_tstamp_ll_e810 function.
This will also be used in the future when low latency PHY timer updates are
supported.
Co-developed-by: Karol Kolacinski <karol.kolacinski@intel.com>
Signed-off-by: Karol Kolacinski <karol.kolacinski@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Reviewed-by: Milena Olech <milena.olech@intel.com>
Signed-off-by: Anton Nadezhdin <anton.nadezhdin@intel.com>
---
drivers/net/ethernet/intel/ice/ice_osdep.h | 3 +++
drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 30 +++++++++------------
drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 2 +-
3 files changed, 17 insertions(+), 18 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_osdep.h b/drivers/net/ethernet/intel/ice/ice_osdep.h
index b9f383494b3f..9bb343de80a9 100644
--- a/drivers/net/ethernet/intel/ice/ice_osdep.h
+++ b/drivers/net/ethernet/intel/ice/ice_osdep.h
@@ -26,6 +26,9 @@
#define rd32_poll_timeout(a, addr, val, cond, delay_us, timeout_us) \
read_poll_timeout(rd32, val, cond, delay_us, timeout_us, false, a, addr)
+#define rd32_poll_timeout_atomic(a, addr, val, cond, delay_us, timeout_us) \
+ read_poll_timeout_atomic(rd32, val, cond, delay_us, timeout_us, false, \
+ a, addr)
#define ice_flush(a) rd32((a), GLGEN_STAT)
#define ICE_M(m, s) ((m ## U) << (s))
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index e55aeab0975c..b9cf8ce9644a 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -4868,32 +4868,28 @@ static int
ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
{
u32 val;
- u8 i;
+ u8 err;
/* Write TS index to read to the PF register so the FW can read it */
val = FIELD_PREP(TS_LL_READ_TS_IDX, idx) | TS_LL_READ_TS;
wr32(hw, PF_SB_ATQBAL, val);
/* Read the register repeatedly until the FW provides us the TS */
- for (i = TS_LL_READ_RETRIES; i > 0; i--) {
- val = rd32(hw, PF_SB_ATQBAL);
-
- /* When the bit is cleared, the TS is ready in the register */
- if (!(FIELD_GET(TS_LL_READ_TS, val))) {
- /* High 8 bit value of the TS is on the bits 16:23 */
- *hi = FIELD_GET(TS_LL_READ_TS_HIGH, val);
+ err = rd32_poll_timeout_atomic(hw, PF_SB_ATQBAL, val,
+ !FIELD_GET(TS_LL_READ_TS, val),
+ 10, TS_LL_READ_TIMEOUT);
+ if (err) {
+ ice_debug(hw, ICE_DBG_PTP, "Failed to read PTP timestamp using low latency read\n");
+ return err;
+ }
- /* Read the low 32 bit value and set the TS valid bit */
- *lo = rd32(hw, PF_SB_ATQBAH) | TS_VALID;
- return 0;
- }
+ /* High 8 bit value of the TS is on the bits 16:23 */
+ *hi = FIELD_GET(TS_LL_READ_TS_HIGH, val);
- udelay(10);
- }
+ /* Read the low 32 bit value and set the TS valid bit */
+ *lo = rd32(hw, PF_SB_ATQBAH) | TS_VALID;
- /* FW failed to provide the TS in time */
- ice_debug(hw, ICE_DBG_PTP, "Failed to read PTP timestamp using low latency read\n");
- return -EINVAL;
+ return 0;
}
/**
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
index 5c11d8a69fd3..4c059e2f4d96 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
@@ -694,7 +694,7 @@ static inline bool ice_is_dual(struct ice_hw *hw)
#define BYTES_PER_IDX_ADDR_L 4
/* Tx timestamp low latency read definitions */
-#define TS_LL_READ_RETRIES 200
+#define TS_LL_READ_TIMEOUT 2000
#define TS_LL_READ_TS_HIGH GENMASK(23, 16)
#define TS_LL_READ_TS_IDX GENMASK(29, 24)
#define TS_LL_READ_TS_INTR BIT(30)
--
2.42.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH iwl-next 2/5] ice: rename TS_LL_READ* macros to REG_LL_PROXY_H_*
2024-12-04 18:03 [PATCH iwl-next 0/5] ice: implement low latency PHY timer updates Anton Nadezhdin
2024-12-04 18:03 ` [PATCH iwl-next 1/5] ice: use rd32_poll_timeout_atomic in ice_read_phy_tstamp_ll_e810 Anton Nadezhdin
@ 2024-12-04 18:03 ` Anton Nadezhdin
2024-12-04 18:03 ` [PATCH iwl-next 3/5] ice: add lock to protect low latency interface Anton Nadezhdin
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Anton Nadezhdin @ 2024-12-04 18:03 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, richardcochran,
Jacob Keller, Karol Kolacinski, Milena Olech, Anton Nadezhdin
From: Jacob Keller <jacob.e.keller@intel.com>
The TS_LL_READ macros are used as part of the low latency Tx timestamp
interface. A future firmware extension will add support for performing PHY
timer updates over this interface. Using TS_LL_READ as the prefix for these
macros will be confusing once the interface is used for other purposes.
Rename the macros, using the prefix REG_LL_PROXY_H, to better clarify that
this is for the low latency interface.
Additionally add macroses for PF_SB_ATQBAH and PF_SB_ATQBAL registers to
better clarify content of this registers as PF_SB_ATQBAH contain low
part of Tx timestamp
Co-developed-by: Karol Kolacinski <karol.kolacinski@intel.com>
Signed-off-by: Karol Kolacinski <karol.kolacinski@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Reviewed-by: Milena Olech <milena.olech@intel.com>
Signed-off-by: Anton Nadezhdin <anton.nadezhdin@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp.c | 14 +++++++-------
drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 14 +++++++-------
drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 13 ++++++++-----
3 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index d8ed4240f225..3c81d98883c0 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -499,9 +499,9 @@ void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 idx)
ice_trace(tx_tstamp_fw_req, tx->tstamps[idx].skb, idx);
/* Write TS index to read to the PF register so the FW can read it */
- wr32(&pf->hw, PF_SB_ATQBAL,
- TS_LL_READ_TS_INTR | FIELD_PREP(TS_LL_READ_TS_IDX, idx) |
- TS_LL_READ_TS);
+ wr32(&pf->hw, REG_LL_PROXY_H,
+ REG_LL_PROXY_H_TS_INTR_ENA | FIELD_PREP(REG_LL_PROXY_H_TS_IDX, idx) |
+ REG_LL_PROXY_H_EXEC);
tx->last_ll_ts_idx_read = idx;
}
@@ -528,20 +528,20 @@ void ice_ptp_complete_tx_single_tstamp(struct ice_ptp_tx *tx)
ice_trace(tx_tstamp_fw_done, tx->tstamps[idx].skb, idx);
- val = rd32(&pf->hw, PF_SB_ATQBAL);
+ val = rd32(&pf->hw, REG_LL_PROXY_H);
/* When the bit is cleared, the TS is ready in the register */
- if (val & TS_LL_READ_TS) {
+ if (val & REG_LL_PROXY_H_EXEC) {
dev_err(ice_pf_to_dev(pf), "Failed to get the Tx tstamp - FW not ready");
return;
}
/* High 8 bit value of the TS is on the bits 16:23 */
- raw_tstamp = FIELD_GET(TS_LL_READ_TS_HIGH, val);
+ raw_tstamp = FIELD_GET(REG_LL_PROXY_H_TS_HIGH, val);
raw_tstamp <<= 32;
/* Read the low 32 bit value */
- raw_tstamp |= (u64)rd32(&pf->hw, PF_SB_ATQBAH);
+ raw_tstamp |= (u64)rd32(&pf->hw, REG_LL_PROXY_L);
/* Devices using this interface always verify the timestamp differs
* relative to the last cached timestamp value.
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index b9cf8ce9644a..06a0c78cd491 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -4871,23 +4871,23 @@ ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
u8 err;
/* Write TS index to read to the PF register so the FW can read it */
- val = FIELD_PREP(TS_LL_READ_TS_IDX, idx) | TS_LL_READ_TS;
- wr32(hw, PF_SB_ATQBAL, val);
+ val = FIELD_PREP(REG_LL_PROXY_H_TS_IDX, idx) | REG_LL_PROXY_H_EXEC;
+ wr32(hw, REG_LL_PROXY_H, val);
/* Read the register repeatedly until the FW provides us the TS */
- err = rd32_poll_timeout_atomic(hw, PF_SB_ATQBAL, val,
- !FIELD_GET(TS_LL_READ_TS, val),
- 10, TS_LL_READ_TIMEOUT);
+ err = rd32_poll_timeout_atomic(hw, REG_LL_PROXY_H, val,
+ !FIELD_GET(REG_LL_PROXY_H_EXEC, val),
+ 10, REG_LL_PROXY_H_TIMEOUT_US);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read PTP timestamp using low latency read\n");
return err;
}
/* High 8 bit value of the TS is on the bits 16:23 */
- *hi = FIELD_GET(TS_LL_READ_TS_HIGH, val);
+ *hi = FIELD_GET(REG_LL_PROXY_H_TS_HIGH, val);
/* Read the low 32 bit value and set the TS valid bit */
- *lo = rd32(hw, PF_SB_ATQBAH) | TS_VALID;
+ *lo = rd32(hw, REG_LL_PROXY_L) | TS_VALID;
return 0;
}
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
index 4c059e2f4d96..71097eb67d54 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
@@ -694,11 +694,14 @@ static inline bool ice_is_dual(struct ice_hw *hw)
#define BYTES_PER_IDX_ADDR_L 4
/* Tx timestamp low latency read definitions */
-#define TS_LL_READ_TIMEOUT 2000
-#define TS_LL_READ_TS_HIGH GENMASK(23, 16)
-#define TS_LL_READ_TS_IDX GENMASK(29, 24)
-#define TS_LL_READ_TS_INTR BIT(30)
-#define TS_LL_READ_TS BIT(31)
+#define REG_LL_PROXY_H_TIMEOUT_US 2000
+#define REG_LL_PROXY_H_TS_HIGH GENMASK(23, 16)
+#define REG_LL_PROXY_H_TS_IDX GENMASK(29, 24)
+#define REG_LL_PROXY_H_TS_INTR_ENA BIT(30)
+#define REG_LL_PROXY_H_EXEC BIT(31)
+
+#define REG_LL_PROXY_L PF_SB_ATQBAH
+#define REG_LL_PROXY_H PF_SB_ATQBAL
/* Internal PHY timestamp address */
#define TS_L(a, idx) ((a) + ((idx) * BYTES_PER_IDX_ADDR_L_U))
--
2.42.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH iwl-next 3/5] ice: add lock to protect low latency interface
2024-12-04 18:03 [PATCH iwl-next 0/5] ice: implement low latency PHY timer updates Anton Nadezhdin
2024-12-04 18:03 ` [PATCH iwl-next 1/5] ice: use rd32_poll_timeout_atomic in ice_read_phy_tstamp_ll_e810 Anton Nadezhdin
2024-12-04 18:03 ` [PATCH iwl-next 2/5] ice: rename TS_LL_READ* macros to REG_LL_PROXY_H_* Anton Nadezhdin
@ 2024-12-04 18:03 ` Anton Nadezhdin
2024-12-04 18:03 ` [PATCH iwl-next 4/5] ice: check low latency PHY timer update firmware capability Anton Nadezhdin
2024-12-04 18:03 ` [PATCH iwl-next 5/5] ice: implement low latency PHY timer updates Anton Nadezhdin
4 siblings, 0 replies; 8+ messages in thread
From: Anton Nadezhdin @ 2024-12-04 18:03 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, richardcochran,
Jacob Keller, Milena Olech, Anton Nadezhdin
From: Jacob Keller <jacob.e.keller@intel.com>
Newer firmware for the E810 devices support a 'low latency' interface to
interact with the PHY without using the Admin Queue. This is interacted
with via the REG_LL_PROXY_L and REG_LL_PROXY_H registers.
Currently, this interface is only used for Tx timestamps. There are two
different mechanisms, including one which uses an interrupt for firmware to
signal completion. However, these two methods are mutually exclusive, so no
synchronization between them was necessary.
This low latency interface is being extended in future firmware to support
also programming the PHY timers. Use of the interface for PHY timers will
need synchronization to ensure there is no overlap with a Tx timestamp.
The interrupt-based response complicates the locking somewhat. We can't use
a simple spinlock. This would require being acquired in
ice_ptp_req_tx_single_tstamp, and released in
ice_ptp_complete_tx_single_tstamp. The ice_ptp_req_tx_single_tstamp
function is called from the threaded IRQ, and the
ice_ptp_complete_tx_single_stamp is called from the low latency IRQ, so we
would need to acquire the lock with IRQs disabled.
To handle this, we'll use a wait queue along with
wait_event_interruptible_locked_irq in the update flows which don't use the
interrupt.
The interrupt flow will acquire the wait queue lock, set the
ATQBAL_FLAGS_INTR_IN_PROGRESS, and then initiate the firmware low latency
request, and unlock the wait queue lock.
Upon receipt of the low latency interrupt, the lock will be acquired, the
ATQBAL_FLAGS_INTR_IN_PROGRESS bit will be cleared, and the firmware
response will be captured, and wake_up_locked() will be called on the wait
queue.
The other flows will use wait_event_interruptible_locked_irq() to wait
until the ATQBAL_FLAGS_INTR_IN_PROGRESS is clear. This function checks the
condition under lock, but does not hold the lock while waiting. On return,
the lock is held, and a return of zero indicates we hold the lock and the
in-progress flag is not set.
This will ensure that threads which need to use the low latency interface
will sleep until they can acquire the lock without any pending low latency
interrupt flow interfering.
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Reviewed-by: Milena Olech <milena.olech@intel.com>
Signed-off-by: Anton Nadezhdin <anton.nadezhdin@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp.c | 42 +++++++++++++++++----
drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 18 +++++++++
drivers/net/ethernet/intel/ice/ice_type.h | 10 +++++
3 files changed, 62 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 3c81d98883c0..75d4e77b1167 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -473,7 +473,9 @@ ice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx)
*/
void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 idx)
{
+ struct ice_e810_params *params;
struct ice_ptp_port *ptp_port;
+ unsigned long flags;
struct sk_buff *skb;
struct ice_pf *pf;
@@ -482,6 +484,7 @@ void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 idx)
ptp_port = container_of(tx, struct ice_ptp_port, tx);
pf = ptp_port_to_pf(ptp_port);
+ params = &pf->hw.ptp.phy.e810;
/* Drop packets which have waited for more than 2 seconds */
if (time_is_before_jiffies(tx->tstamps[idx].start + 2 * HZ)) {
@@ -498,11 +501,17 @@ void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 idx)
ice_trace(tx_tstamp_fw_req, tx->tstamps[idx].skb, idx);
+ spin_lock_irqsave(¶ms->atqbal_wq.lock, flags);
+
+ params->atqbal_flags |= ATQBAL_FLAGS_INTR_IN_PROGRESS;
+
/* Write TS index to read to the PF register so the FW can read it */
wr32(&pf->hw, REG_LL_PROXY_H,
REG_LL_PROXY_H_TS_INTR_ENA | FIELD_PREP(REG_LL_PROXY_H_TS_IDX, idx) |
REG_LL_PROXY_H_EXEC);
tx->last_ll_ts_idx_read = idx;
+
+ spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags);
}
/**
@@ -513,35 +522,52 @@ void ice_ptp_complete_tx_single_tstamp(struct ice_ptp_tx *tx)
{
struct skb_shared_hwtstamps shhwtstamps = {};
u8 idx = tx->last_ll_ts_idx_read;
+ struct ice_e810_params *params;
struct ice_ptp_port *ptp_port;
u64 raw_tstamp, tstamp;
bool drop_ts = false;
struct sk_buff *skb;
+ unsigned long flags;
+ struct device *dev;
struct ice_pf *pf;
- u32 val;
+ u32 reg_ll_high;
if (!tx->init || tx->last_ll_ts_idx_read < 0)
return;
ptp_port = container_of(tx, struct ice_ptp_port, tx);
pf = ptp_port_to_pf(ptp_port);
+ dev = ice_pf_to_dev(pf);
+ params = &pf->hw.ptp.phy.e810;
ice_trace(tx_tstamp_fw_done, tx->tstamps[idx].skb, idx);
- val = rd32(&pf->hw, REG_LL_PROXY_H);
+ spin_lock_irqsave(¶ms->atqbal_wq.lock, flags);
+
+ if (!(params->atqbal_flags & ATQBAL_FLAGS_INTR_IN_PROGRESS))
+ dev_dbg(dev, "%s: low latency interrupt request not in progress?\n",
+ __func__);
+
+ /* Read the low 32 bit value */
+ raw_tstamp = rd32(&pf->hw, REG_LL_PROXY_L);
+ /* Read the status together with high TS part */
+ reg_ll_high = rd32(&pf->hw, REG_LL_PROXY_H);
+
+ /* Wake up threads waiting on low latency interface */
+ params->atqbal_flags &= ~ATQBAL_FLAGS_INTR_IN_PROGRESS;
+
+ wake_up_locked(¶ms->atqbal_wq);
+
+ spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags);
/* When the bit is cleared, the TS is ready in the register */
- if (val & REG_LL_PROXY_H_EXEC) {
+ if (reg_ll_high & REG_LL_PROXY_H_EXEC) {
dev_err(ice_pf_to_dev(pf), "Failed to get the Tx tstamp - FW not ready");
return;
}
/* High 8 bit value of the TS is on the bits 16:23 */
- raw_tstamp = FIELD_GET(REG_LL_PROXY_H_TS_HIGH, val);
- raw_tstamp <<= 32;
-
- /* Read the low 32 bit value */
- raw_tstamp |= (u64)rd32(&pf->hw, REG_LL_PROXY_L);
+ raw_tstamp |= ((u64)FIELD_GET(REG_LL_PROXY_H_TS_HIGH, reg_ll_high)) << 32;
/* Devices using this interface always verify the timestamp differs
* relative to the last cached timestamp value.
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index 06a0c78cd491..d4c831b6eb6d 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -4867,9 +4867,22 @@ static int ice_write_phy_reg_e810(struct ice_hw *hw, u32 addr, u32 val)
static int
ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
{
+ struct ice_e810_params *params = &hw->ptp.phy.e810;
+ unsigned long flags;
u32 val;
u8 err;
+ spin_lock_irqsave(¶ms->atqbal_wq.lock, flags);
+
+ /* Wait for any pending in-progress low latency interrupt */
+ err = wait_event_interruptible_locked_irq(params->atqbal_wq,
+ !(params->atqbal_flags &
+ ATQBAL_FLAGS_INTR_IN_PROGRESS));
+ if (err) {
+ spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags);
+ return err;
+ }
+
/* Write TS index to read to the PF register so the FW can read it */
val = FIELD_PREP(REG_LL_PROXY_H_TS_IDX, idx) | REG_LL_PROXY_H_EXEC;
wr32(hw, REG_LL_PROXY_H, val);
@@ -4880,6 +4893,7 @@ ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
10, REG_LL_PROXY_H_TIMEOUT_US);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read PTP timestamp using low latency read\n");
+ spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags);
return err;
}
@@ -4889,6 +4903,8 @@ ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
/* Read the low 32 bit value and set the TS valid bit */
*lo = rd32(hw, REG_LL_PROXY_L) | TS_VALID;
+ spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags);
+
return 0;
}
@@ -5316,6 +5332,8 @@ static void ice_ptp_init_phy_e810(struct ice_ptp_hw *ptp)
{
ptp->num_lports = 8;
ptp->ports_per_phy = 4;
+
+ init_waitqueue_head(&ptp->phy.e810.atqbal_wq);
}
/* E830 functions
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index e2e6b2119889..5f3af5f3d2cb 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -18,6 +18,7 @@
#include "ice_sbq_cmd.h"
#include "ice_vlan_mode.h"
#include "ice_fwlog.h"
+#include <linux/wait.h>
static inline bool ice_is_tc_ena(unsigned long bitmap, u8 tc)
{
@@ -848,6 +849,14 @@ struct ice_mbx_data {
#define ICE_PORTS_PER_QUAD 4
#define ICE_GET_QUAD_NUM(port) ((port) / ICE_PORTS_PER_QUAD)
+#define ATQBAL_FLAGS_INTR_IN_PROGRESS BIT(0)
+
+struct ice_e810_params {
+ /* The wait queue lock also protects the low latency interface */
+ wait_queue_head_t atqbal_wq;
+ unsigned int atqbal_flags;
+};
+
struct ice_eth56g_params {
u8 num_phys;
u8 phy_addr[2];
@@ -857,6 +866,7 @@ struct ice_eth56g_params {
};
union ice_phy_params {
+ struct ice_e810_params e810;
struct ice_eth56g_params eth56g;
};
--
2.42.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH iwl-next 4/5] ice: check low latency PHY timer update firmware capability
2024-12-04 18:03 [PATCH iwl-next 0/5] ice: implement low latency PHY timer updates Anton Nadezhdin
` (2 preceding siblings ...)
2024-12-04 18:03 ` [PATCH iwl-next 3/5] ice: add lock to protect low latency interface Anton Nadezhdin
@ 2024-12-04 18:03 ` Anton Nadezhdin
2024-12-04 18:03 ` [PATCH iwl-next 5/5] ice: implement low latency PHY timer updates Anton Nadezhdin
4 siblings, 0 replies; 8+ messages in thread
From: Anton Nadezhdin @ 2024-12-04 18:03 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, richardcochran,
Jacob Keller, Karol Kolacinski, Milena Olech, Anton Nadezhdin
From: Jacob Keller <jacob.e.keller@intel.com>
Newer versions of firmware support programming the PHY timer via the low
latency interface exposed over REG_LL_PROXY_L and REG_LL_PROXY_H. Add
support for checking the device capabilities for this feature.
Co-developed-by: Karol Kolacinski <karol.kolacinski@intel.com>
Signed-off-by: Karol Kolacinski <karol.kolacinski@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Reviewed-by: Milena Olech <milena.olech@intel.com>
Signed-off-by: Anton Nadezhdin <anton.nadezhdin@intel.com>
---
drivers/net/ethernet/intel/ice/ice_common.c | 3 +++
drivers/net/ethernet/intel/ice/ice_type.h | 2 ++
2 files changed, 5 insertions(+)
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index faba09b9d880..d23f413740c4 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -2507,6 +2507,7 @@ ice_parse_1588_dev_caps(struct ice_hw *hw, struct ice_hw_dev_caps *dev_p,
info->ts_ll_read = ((number & ICE_TS_LL_TX_TS_READ_M) != 0);
info->ts_ll_int_read = ((number & ICE_TS_LL_TX_TS_INT_READ_M) != 0);
+ info->ll_phy_tmr_update = ((number & ICE_TS_LL_PHY_TMR_UPDATE_M) != 0);
info->ena_ports = logical_id;
info->tmr_own_map = phys_id;
@@ -2529,6 +2530,8 @@ ice_parse_1588_dev_caps(struct ice_hw *hw, struct ice_hw_dev_caps *dev_p,
info->ts_ll_read);
ice_debug(hw, ICE_DBG_INIT, "dev caps: ts_ll_int_read = %u\n",
info->ts_ll_int_read);
+ ice_debug(hw, ICE_DBG_INIT, "dev caps: ll_phy_tmr_update = %u\n",
+ info->ll_phy_tmr_update);
ice_debug(hw, ICE_DBG_INIT, "dev caps: ieee_1588 ena_ports = %u\n",
info->ena_ports);
ice_debug(hw, ICE_DBG_INIT, "dev caps: tmr_own_map = %u\n",
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index 5f3af5f3d2cb..25d6dad1852b 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -369,6 +369,7 @@ struct ice_ts_func_info {
#define ICE_TS_TMR1_ENA_M BIT(26)
#define ICE_TS_LL_TX_TS_READ_M BIT(28)
#define ICE_TS_LL_TX_TS_INT_READ_M BIT(29)
+#define ICE_TS_LL_PHY_TMR_UPDATE_M BIT(30)
struct ice_ts_dev_info {
/* Device specific info */
@@ -383,6 +384,7 @@ struct ice_ts_dev_info {
u8 tmr1_ena;
u8 ts_ll_read;
u8 ts_ll_int_read;
+ u8 ll_phy_tmr_update;
};
#define ICE_NAC_TOPO_PRIMARY_M BIT(0)
--
2.42.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH iwl-next 5/5] ice: implement low latency PHY timer updates
2024-12-04 18:03 [PATCH iwl-next 0/5] ice: implement low latency PHY timer updates Anton Nadezhdin
` (3 preceding siblings ...)
2024-12-04 18:03 ` [PATCH iwl-next 4/5] ice: check low latency PHY timer update firmware capability Anton Nadezhdin
@ 2024-12-04 18:03 ` Anton Nadezhdin
4 siblings, 0 replies; 8+ messages in thread
From: Anton Nadezhdin @ 2024-12-04 18:03 UTC (permalink / raw)
To: intel-wired-lan
Cc: netdev, anthony.l.nguyen, przemyslaw.kitszel, richardcochran,
Jacob Keller, Karol Kolacinski, Milena Olech, Anton Nadezhdin
From: Jacob Keller <jacob.e.keller@intel.com>
Programming the PHY registers in preparation for an increment value change
or a timer adjustment on E810 requires issuing Admin Queue commands for
each PHY register. It has been found that the firmware Admin Queue
processing occasionally has delays of tens or rarely up to hundreds of
milliseconds. This delay cascades to failures in the PTP applications which
depend on these updates being low latency.
Consider a standard PTP profile with a sync rate of 16 times per second.
This means there is ~62 milliseconds between sync messages. A complete
cycle of the PTP algorithm
1) Sync message (with Tx timestamp) from source
2) Follow-up message from source
3) Delay request (with Tx timestamp) from sink
4) Delay response (with Rx timestamp of request) from source
5) measure instantaneous clock offset
6) request time adjustment via CLOCK_ADJTIME systemcall
The Tx timestamps have a default maximum timeout of 10 milliseconds. If we
assume that the maximum possible time is used, this leaves us with ~42
milliseconds of processing time for a complete cycle.
The CLOCK_ADJTIME system call is synchronous and will block until the
driver completes its timer adjustment or frequency change.
If the writes to prepare the PHY timers get hit by a latency spike of 50
milliseconds, then the PTP application will be delayed past the point where
the next cycle should start. Packets from the next cycle may have already
arrived and are waiting on the socket.
In particular, LinuxPTP ptp4l may start complaining about missing an
announce message from the source, triggering a fault. In addition, the
clockcheck logic it uses may trigger. This clockcheck failure occurs
because the timestamp captured by hardware is compared against a reading of
CLOCK_MONOTONIC. It is assumed that the time when the Rx timestamp is
captured and the read from CLOCK_MONOTONIC are relatively close together.
This is not the case if there is a significant delay to processing the Rx
packet.
Newer firmware supports programming the PHY registers over a low latency
interface which bypasses the Admin Queue. Instead, software writes to the
REG_LL_PROXY_L and REG_LL_PROXY_H registers. Firmware reads these registers
and then programs the PHY timers.
Implement functions to use this interface when available to program the PHY
timers instead of using the Admin Queue. This avoids the Admin Queue
latency and ensures that adjustments happen within acceptable latency
bounds.
Co-developed-by: Karol Kolacinski <karol.kolacinski@intel.com>
Signed-off-by: Karol Kolacinski <karol.kolacinski@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Reviewed-by: Milena Olech <milena.olech@intel.com>
Signed-off-by: Anton Nadezhdin <anton.nadezhdin@intel.com>
---
drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 105 ++++++++++++++++++++
drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 4 +
2 files changed, 109 insertions(+)
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index d4c831b6eb6d..d1d7b96ee39a 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -5087,6 +5087,55 @@ static int ice_ptp_prep_phy_time_e810(struct ice_hw *hw, u32 time)
return 0;
}
+/**
+ * ice_ptp_prep_phy_adj_ll_e810 - Prep PHY ports for a time adjustment
+ * @hw: pointer to HW struct
+ * @adj: adjustment value to program
+ *
+ * Use the low latency firmware interface to program PHY time adjustment to
+ * all PHY ports.
+ *
+ * Return: 0 on success, -EBUSY on timeout
+ */
+static int ice_ptp_prep_phy_adj_ll_e810(struct ice_hw *hw, s32 adj)
+{
+ const u8 tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
+ struct ice_e810_params *params = &hw->ptp.phy.e810;
+ unsigned long flags;
+ u32 val;
+ int err;
+
+ spin_lock_irqsave(¶ms->atqbal_wq.lock, flags);
+
+ /* Wait for any pending in-progress low latency interrupt */
+ err = wait_event_interruptible_locked_irq(params->atqbal_wq,
+ !(params->atqbal_flags &
+ ATQBAL_FLAGS_INTR_IN_PROGRESS));
+ if (err) {
+ spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags);
+ return err;
+ }
+
+ wr32(hw, PF_SB_ATQBAH, adj);
+ val = FIELD_PREP(REG_LL_PROXY_H_PHY_TMR_CMD_M, REG_LL_PROXY_H_PHY_TMR_CMD_ADJ) |
+ FIELD_PREP(REG_LL_PROXY_H_PHY_TMR_IDX_M, tmr_idx) | REG_LL_PROXY_H_EXEC;
+ wr32(hw, PF_SB_ATQBAL, val);
+
+ /* Read the register repeatedly until the FW indicates completion */
+ err = rd32_poll_timeout_atomic(hw, PF_SB_ATQBAL, val,
+ !FIELD_GET(REG_LL_PROXY_H_EXEC, val),
+ 10, REG_LL_PROXY_H_TIMEOUT_US);
+ if (err) {
+ ice_debug(hw, ICE_DBG_PTP, "Failed to prepare PHY timer adjustment using low latency interface\n");
+ spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags);
+ return err;
+ }
+
+ spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags);
+
+ return 0;
+}
+
/**
* ice_ptp_prep_phy_adj_e810 - Prep PHY port for a time adjustment
* @hw: pointer to HW struct
@@ -5105,6 +5154,9 @@ static int ice_ptp_prep_phy_adj_e810(struct ice_hw *hw, s32 adj)
u8 tmr_idx;
int err;
+ if (hw->dev_caps.ts_dev_info.ll_phy_tmr_update)
+ return ice_ptp_prep_phy_adj_ll_e810(hw, adj);
+
tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
/* Adjustments are represented as signed 2's complement values in
@@ -5127,6 +5179,56 @@ static int ice_ptp_prep_phy_adj_e810(struct ice_hw *hw, s32 adj)
return 0;
}
+/**
+ * ice_ptp_prep_phy_incval_ll_e810 - Prep PHY ports increment value change
+ * @hw: pointer to HW struct
+ * @incval: The new 40bit increment value to prepare
+ *
+ * Use the low latency firmware interface to program PHY time increment value
+ * for all PHY ports.
+ *
+ * Return: 0 on success, -EBUSY on timeout
+ */
+static int ice_ptp_prep_phy_incval_ll_e810(struct ice_hw *hw, u64 incval)
+{
+ const u8 tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
+ struct ice_e810_params *params = &hw->ptp.phy.e810;
+ unsigned long flags;
+ u32 val;
+ int err;
+
+ spin_lock_irqsave(¶ms->atqbal_wq.lock, flags);
+
+ /* Wait for any pending in-progress low latency interrupt */
+ err = wait_event_interruptible_locked_irq(params->atqbal_wq,
+ !(params->atqbal_flags &
+ ATQBAL_FLAGS_INTR_IN_PROGRESS));
+ if (err) {
+ spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags);
+ return err;
+ }
+
+ wr32(hw, PF_SB_ATQBAH, lower_32_bits(incval));
+ val = FIELD_PREP(REG_LL_PROXY_H_PHY_TMR_CMD_M, REG_LL_PROXY_H_PHY_TMR_CMD_FREQ) |
+ FIELD_PREP(REG_LL_PROXY_H_TS_HIGH, (u8)upper_32_bits(incval)) |
+ FIELD_PREP(REG_LL_PROXY_H_PHY_TMR_IDX_M, tmr_idx) | REG_LL_PROXY_H_EXEC;
+ wr32(hw, PF_SB_ATQBAL, val);
+
+ /* Read the register repeatedly until the FW indicates completion */
+ err = rd32_poll_timeout_atomic(hw, PF_SB_ATQBAL, val,
+ !FIELD_GET(REG_LL_PROXY_H_EXEC, val),
+ 10, REG_LL_PROXY_H_TIMEOUT_US);
+ if (err) {
+ ice_debug(hw, ICE_DBG_PTP, "Failed to prepare PHY timer increment using low latency interface\n");
+ spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags);
+ return err;
+ }
+
+ spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags);
+
+ return 0;
+}
+
/**
* ice_ptp_prep_phy_incval_e810 - Prep PHY port increment value change
* @hw: pointer to HW struct
@@ -5142,6 +5244,9 @@ static int ice_ptp_prep_phy_incval_e810(struct ice_hw *hw, u64 incval)
u8 tmr_idx;
int err;
+ if (hw->dev_caps.ts_dev_info.ll_phy_tmr_update)
+ return ice_ptp_prep_phy_incval_ll_e810(hw, incval);
+
tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
low = lower_32_bits(incval);
high = upper_32_bits(incval);
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
index 71097eb67d54..ba722cbcf694 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
@@ -695,7 +695,11 @@ static inline bool ice_is_dual(struct ice_hw *hw)
/* Tx timestamp low latency read definitions */
#define REG_LL_PROXY_H_TIMEOUT_US 2000
+#define REG_LL_PROXY_H_PHY_TMR_CMD_M GENMASK(7, 6)
+#define REG_LL_PROXY_H_PHY_TMR_CMD_ADJ 0x1
+#define REG_LL_PROXY_H_PHY_TMR_CMD_FREQ 0x2
#define REG_LL_PROXY_H_TS_HIGH GENMASK(23, 16)
+#define REG_LL_PROXY_H_PHY_TMR_IDX_M BIT(24)
#define REG_LL_PROXY_H_TS_IDX GENMASK(29, 24)
#define REG_LL_PROXY_H_TS_INTR_ENA BIT(30)
#define REG_LL_PROXY_H_EXEC BIT(31)
--
2.42.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH iwl-next 1/5] ice: use rd32_poll_timeout_atomic in ice_read_phy_tstamp_ll_e810
2024-12-04 18:03 ` [PATCH iwl-next 1/5] ice: use rd32_poll_timeout_atomic in ice_read_phy_tstamp_ll_e810 Anton Nadezhdin
@ 2024-12-06 13:30 ` Simon Horman
2024-12-06 20:15 ` Keller, Jacob E
0 siblings, 1 reply; 8+ messages in thread
From: Simon Horman @ 2024-12-06 13:30 UTC (permalink / raw)
To: Anton Nadezhdin
Cc: intel-wired-lan, netdev, anthony.l.nguyen, przemyslaw.kitszel,
richardcochran, Jacob Keller, Karol Kolacinski, Milena Olech
On Wed, Dec 04, 2024 at 01:03:44PM -0500, Anton Nadezhdin wrote:
> From: Jacob Keller <jacob.e.keller@intel.com>
>
> The ice_read_phy_tstamp_ll_e810 function repeatedly reads the PF_SB_ATQBAL
> register until the TS_LL_READ_TS bit is cleared. This is a perfect
> candidate for using rd32_poll_timeout. However, the default implementation
> uses a sleep-based wait.
>
> Add a new rd32_poll_timeout_atomic macro which is based on the non-sleeping
> read_poll_timeout_atomic implementation. Use this to replace the loop
> reading in the ice_read_phy_tstamp_ll_e810 function.
>
> This will also be used in the future when low latency PHY timer updates are
> supported.
>
> Co-developed-by: Karol Kolacinski <karol.kolacinski@intel.com>
> Signed-off-by: Karol Kolacinski <karol.kolacinski@intel.com>
> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
> Reviewed-by: Milena Olech <milena.olech@intel.com>
> Signed-off-by: Anton Nadezhdin <anton.nadezhdin@intel.com>
> ---
> drivers/net/ethernet/intel/ice/ice_osdep.h | 3 +++
> drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 30 +++++++++------------
> drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 2 +-
> 3 files changed, 17 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/ice/ice_osdep.h b/drivers/net/ethernet/intel/ice/ice_osdep.h
> index b9f383494b3f..9bb343de80a9 100644
> --- a/drivers/net/ethernet/intel/ice/ice_osdep.h
> +++ b/drivers/net/ethernet/intel/ice/ice_osdep.h
> @@ -26,6 +26,9 @@
>
> #define rd32_poll_timeout(a, addr, val, cond, delay_us, timeout_us) \
> read_poll_timeout(rd32, val, cond, delay_us, timeout_us, false, a, addr)
> +#define rd32_poll_timeout_atomic(a, addr, val, cond, delay_us, timeout_us) \
> + read_poll_timeout_atomic(rd32, val, cond, delay_us, timeout_us, false, \
> + a, addr)
>
> #define ice_flush(a) rd32((a), GLGEN_STAT)
> #define ICE_M(m, s) ((m ## U) << (s))
> diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
> index e55aeab0975c..b9cf8ce9644a 100644
> --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
> +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
> @@ -4868,32 +4868,28 @@ static int
> ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
> {
> u32 val;
> - u8 i;
> + u8 err;
>
> /* Write TS index to read to the PF register so the FW can read it */
> val = FIELD_PREP(TS_LL_READ_TS_IDX, idx) | TS_LL_READ_TS;
> wr32(hw, PF_SB_ATQBAL, val);
>
> /* Read the register repeatedly until the FW provides us the TS */
> - for (i = TS_LL_READ_RETRIES; i > 0; i--) {
> - val = rd32(hw, PF_SB_ATQBAL);
> -
> - /* When the bit is cleared, the TS is ready in the register */
> - if (!(FIELD_GET(TS_LL_READ_TS, val))) {
> - /* High 8 bit value of the TS is on the bits 16:23 */
> - *hi = FIELD_GET(TS_LL_READ_TS_HIGH, val);
> + err = rd32_poll_timeout_atomic(hw, PF_SB_ATQBAL, val,
> + !FIELD_GET(TS_LL_READ_TS, val),
> + 10, TS_LL_READ_TIMEOUT);
Hi Jakob and Karol,
A minor nit from my side: rd32_poll_timeout_atomic may return a negative
error value but err is unsigned. This doesn't seem ideal.
Flagged by Smatch
> + if (err) {
> + ice_debug(hw, ICE_DBG_PTP, "Failed to read PTP timestamp using low latency read\n");
> + return err;
> + }
>
> - /* Read the low 32 bit value and set the TS valid bit */
> - *lo = rd32(hw, PF_SB_ATQBAH) | TS_VALID;
> - return 0;
> - }
> + /* High 8 bit value of the TS is on the bits 16:23 */
> + *hi = FIELD_GET(TS_LL_READ_TS_HIGH, val);
>
> - udelay(10);
> - }
> + /* Read the low 32 bit value and set the TS valid bit */
> + *lo = rd32(hw, PF_SB_ATQBAH) | TS_VALID;
>
> - /* FW failed to provide the TS in time */
> - ice_debug(hw, ICE_DBG_PTP, "Failed to read PTP timestamp using low latency read\n");
> - return -EINVAL;
> + return 0;
> }
>
> /**
...
^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH iwl-next 1/5] ice: use rd32_poll_timeout_atomic in ice_read_phy_tstamp_ll_e810
2024-12-06 13:30 ` Simon Horman
@ 2024-12-06 20:15 ` Keller, Jacob E
0 siblings, 0 replies; 8+ messages in thread
From: Keller, Jacob E @ 2024-12-06 20:15 UTC (permalink / raw)
To: Simon Horman, Nadezhdin, Anton
Cc: intel-wired-lan@lists.osuosl.org, netdev@vger.kernel.org,
Nguyen, Anthony L, Kitszel, Przemyslaw, richardcochran@gmail.com,
Kolacinski, Karol, Olech, Milena
> -----Original Message-----
> From: Simon Horman <horms@kernel.org>
> Sent: Friday, December 6, 2024 5:30 AM
> To: Nadezhdin, Anton <anton.nadezhdin@intel.com>
> Cc: intel-wired-lan@lists.osuosl.org; netdev@vger.kernel.org; Nguyen, Anthony L
> <anthony.l.nguyen@intel.com>; Kitszel, Przemyslaw
> <przemyslaw.kitszel@intel.com>; richardcochran@gmail.com; Keller, Jacob E
> <jacob.e.keller@intel.com>; Kolacinski, Karol <karol.kolacinski@intel.com>; Olech,
> Milena <milena.olech@intel.com>
> Subject: Re: [PATCH iwl-next 1/5] ice: use rd32_poll_timeout_atomic in
> ice_read_phy_tstamp_ll_e810
>
> On Wed, Dec 04, 2024 at 01:03:44PM -0500, Anton Nadezhdin wrote:
> > From: Jacob Keller <jacob.e.keller@intel.com>
> >
> > The ice_read_phy_tstamp_ll_e810 function repeatedly reads the PF_SB_ATQBAL
> > register until the TS_LL_READ_TS bit is cleared. This is a perfect
> > candidate for using rd32_poll_timeout. However, the default implementation
> > uses a sleep-based wait.
> >
> > Add a new rd32_poll_timeout_atomic macro which is based on the non-
> sleeping
> > read_poll_timeout_atomic implementation. Use this to replace the loop
> > reading in the ice_read_phy_tstamp_ll_e810 function.
> >
> > This will also be used in the future when low latency PHY timer updates are
> > supported.
> >
> > Co-developed-by: Karol Kolacinski <karol.kolacinski@intel.com>
> > Signed-off-by: Karol Kolacinski <karol.kolacinski@intel.com>
> > Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
> > Reviewed-by: Milena Olech <milena.olech@intel.com>
> > Signed-off-by: Anton Nadezhdin <anton.nadezhdin@intel.com>
> > ---
> > drivers/net/ethernet/intel/ice/ice_osdep.h | 3 +++
> > drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 30 +++++++++------------
> > drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 2 +-
> > 3 files changed, 17 insertions(+), 18 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/intel/ice/ice_osdep.h
> b/drivers/net/ethernet/intel/ice/ice_osdep.h
> > index b9f383494b3f..9bb343de80a9 100644
> > --- a/drivers/net/ethernet/intel/ice/ice_osdep.h
> > +++ b/drivers/net/ethernet/intel/ice/ice_osdep.h
> > @@ -26,6 +26,9 @@
> >
> > #define rd32_poll_timeout(a, addr, val, cond, delay_us, timeout_us) \
> > read_poll_timeout(rd32, val, cond, delay_us, timeout_us, false, a, addr)
> > +#define rd32_poll_timeout_atomic(a, addr, val, cond, delay_us, timeout_us) \
> > + read_poll_timeout_atomic(rd32, val, cond, delay_us, timeout_us, false, \
> > + a, addr)
> >
> > #define ice_flush(a) rd32((a), GLGEN_STAT)
> > #define ICE_M(m, s) ((m ## U) << (s))
> > diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
> b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
> > index e55aeab0975c..b9cf8ce9644a 100644
> > --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
> > +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
> > @@ -4868,32 +4868,28 @@ static int
> > ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
> > {
> > u32 val;
> > - u8 i;
> > + u8 err;
> >
> > /* Write TS index to read to the PF register so the FW can read it */
> > val = FIELD_PREP(TS_LL_READ_TS_IDX, idx) | TS_LL_READ_TS;
> > wr32(hw, PF_SB_ATQBAL, val);
> >
> > /* Read the register repeatedly until the FW provides us the TS */
> > - for (i = TS_LL_READ_RETRIES; i > 0; i--) {
> > - val = rd32(hw, PF_SB_ATQBAL);
> > -
> > - /* When the bit is cleared, the TS is ready in the register */
> > - if (!(FIELD_GET(TS_LL_READ_TS, val))) {
> > - /* High 8 bit value of the TS is on the bits 16:23 */
> > - *hi = FIELD_GET(TS_LL_READ_TS_HIGH, val);
> > + err = rd32_poll_timeout_atomic(hw, PF_SB_ATQBAL, val,
> > + !FIELD_GET(TS_LL_READ_TS, val),
> > + 10, TS_LL_READ_TIMEOUT);
>
> Hi Jakob and Karol,
>
> A minor nit from my side: rd32_poll_timeout_atomic may return a negative
> error value but err is unsigned. This doesn't seem ideal.
>
> Flagged by Smatch
>
Yep, err should just be an integer here.
Thanks,
Jake
> > + if (err) {
> > + ice_debug(hw, ICE_DBG_PTP, "Failed to read PTP timestamp using
> low latency read\n");
> > + return err;
> > + }
> >
> > - /* Read the low 32 bit value and set the TS valid bit */
> > - *lo = rd32(hw, PF_SB_ATQBAH) | TS_VALID;
> > - return 0;
> > - }
> > + /* High 8 bit value of the TS is on the bits 16:23 */
> > + *hi = FIELD_GET(TS_LL_READ_TS_HIGH, val);
> >
> > - udelay(10);
> > - }
> > + /* Read the low 32 bit value and set the TS valid bit */
> > + *lo = rd32(hw, PF_SB_ATQBAH) | TS_VALID;
> >
> > - /* FW failed to provide the TS in time */
> > - ice_debug(hw, ICE_DBG_PTP, "Failed to read PTP timestamp using low
> latency read\n");
> > - return -EINVAL;
> > + return 0;
> > }
> >
> > /**
>
> ...
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2024-12-06 20:15 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-12-04 18:03 [PATCH iwl-next 0/5] ice: implement low latency PHY timer updates Anton Nadezhdin
2024-12-04 18:03 ` [PATCH iwl-next 1/5] ice: use rd32_poll_timeout_atomic in ice_read_phy_tstamp_ll_e810 Anton Nadezhdin
2024-12-06 13:30 ` Simon Horman
2024-12-06 20:15 ` Keller, Jacob E
2024-12-04 18:03 ` [PATCH iwl-next 2/5] ice: rename TS_LL_READ* macros to REG_LL_PROXY_H_* Anton Nadezhdin
2024-12-04 18:03 ` [PATCH iwl-next 3/5] ice: add lock to protect low latency interface Anton Nadezhdin
2024-12-04 18:03 ` [PATCH iwl-next 4/5] ice: check low latency PHY timer update firmware capability Anton Nadezhdin
2024-12-04 18:03 ` [PATCH iwl-next 5/5] ice: implement low latency PHY timer updates Anton Nadezhdin
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).