Skip to content

Commit ef9a64c

Browse files
jacob-kelleranguy11
authored andcommitted
ice: implement low latency PHY timer updates
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> Signed-off-by: Anton Nadezhdin <anton.nadezhdin@intel.com> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
1 parent a5c69d4 commit ef9a64c

File tree

2 files changed

+109
-0
lines changed

2 files changed

+109
-0
lines changed

drivers/net/ethernet/intel/ice/ice_ptp_hw.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5078,6 +5078,55 @@ static int ice_ptp_prep_phy_time_e810(struct ice_hw *hw, u32 time)
50785078
return 0;
50795079
}
50805080

5081+
/**
5082+
* ice_ptp_prep_phy_adj_ll_e810 - Prep PHY ports for a time adjustment
5083+
* @hw: pointer to HW struct
5084+
* @adj: adjustment value to program
5085+
*
5086+
* Use the low latency firmware interface to program PHY time adjustment to
5087+
* all PHY ports.
5088+
*
5089+
* Return: 0 on success, -EBUSY on timeout
5090+
*/
5091+
static int ice_ptp_prep_phy_adj_ll_e810(struct ice_hw *hw, s32 adj)
5092+
{
5093+
const u8 tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
5094+
struct ice_e810_params *params = &hw->ptp.phy.e810;
5095+
u32 val;
5096+
int err;
5097+
5098+
spin_lock_irq(&params->atqbal_wq.lock);
5099+
5100+
/* Wait for any pending in-progress low latency interrupt */
5101+
err = wait_event_interruptible_locked_irq(params->atqbal_wq,
5102+
!(params->atqbal_flags &
5103+
ATQBAL_FLAGS_INTR_IN_PROGRESS));
5104+
if (err) {
5105+
spin_unlock_irq(&params->atqbal_wq.lock);
5106+
return err;
5107+
}
5108+
5109+
wr32(hw, REG_LL_PROXY_L, adj);
5110+
val = FIELD_PREP(REG_LL_PROXY_H_PHY_TMR_CMD_M, REG_LL_PROXY_H_PHY_TMR_CMD_ADJ) |
5111+
FIELD_PREP(REG_LL_PROXY_H_PHY_TMR_IDX_M, tmr_idx) | REG_LL_PROXY_H_EXEC;
5112+
wr32(hw, REG_LL_PROXY_H, val);
5113+
5114+
/* Read the register repeatedly until the FW indicates completion */
5115+
err = read_poll_timeout_atomic(rd32, val,
5116+
!FIELD_GET(REG_LL_PROXY_H_EXEC, val),
5117+
10, REG_LL_PROXY_H_TIMEOUT_US, false, hw,
5118+
REG_LL_PROXY_H);
5119+
if (err) {
5120+
ice_debug(hw, ICE_DBG_PTP, "Failed to prepare PHY timer adjustment using low latency interface\n");
5121+
spin_unlock_irq(&params->atqbal_wq.lock);
5122+
return err;
5123+
}
5124+
5125+
spin_unlock_irq(&params->atqbal_wq.lock);
5126+
5127+
return 0;
5128+
}
5129+
50815130
/**
50825131
* ice_ptp_prep_phy_adj_e810 - Prep PHY port for a time adjustment
50835132
* @hw: pointer to HW struct
@@ -5096,6 +5145,9 @@ static int ice_ptp_prep_phy_adj_e810(struct ice_hw *hw, s32 adj)
50965145
u8 tmr_idx;
50975146
int err;
50985147

5148+
if (hw->dev_caps.ts_dev_info.ll_phy_tmr_update)
5149+
return ice_ptp_prep_phy_adj_ll_e810(hw, adj);
5150+
50995151
tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
51005152

51015153
/* Adjustments are represented as signed 2's complement values in
@@ -5118,6 +5170,56 @@ static int ice_ptp_prep_phy_adj_e810(struct ice_hw *hw, s32 adj)
51185170
return 0;
51195171
}
51205172

5173+
/**
5174+
* ice_ptp_prep_phy_incval_ll_e810 - Prep PHY ports increment value change
5175+
* @hw: pointer to HW struct
5176+
* @incval: The new 40bit increment value to prepare
5177+
*
5178+
* Use the low latency firmware interface to program PHY time increment value
5179+
* for all PHY ports.
5180+
*
5181+
* Return: 0 on success, -EBUSY on timeout
5182+
*/
5183+
static int ice_ptp_prep_phy_incval_ll_e810(struct ice_hw *hw, u64 incval)
5184+
{
5185+
const u8 tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
5186+
struct ice_e810_params *params = &hw->ptp.phy.e810;
5187+
u32 val;
5188+
int err;
5189+
5190+
spin_lock_irq(&params->atqbal_wq.lock);
5191+
5192+
/* Wait for any pending in-progress low latency interrupt */
5193+
err = wait_event_interruptible_locked_irq(params->atqbal_wq,
5194+
!(params->atqbal_flags &
5195+
ATQBAL_FLAGS_INTR_IN_PROGRESS));
5196+
if (err) {
5197+
spin_unlock_irq(&params->atqbal_wq.lock);
5198+
return err;
5199+
}
5200+
5201+
wr32(hw, REG_LL_PROXY_L, lower_32_bits(incval));
5202+
val = FIELD_PREP(REG_LL_PROXY_H_PHY_TMR_CMD_M, REG_LL_PROXY_H_PHY_TMR_CMD_FREQ) |
5203+
FIELD_PREP(REG_LL_PROXY_H_TS_HIGH, (u8)upper_32_bits(incval)) |
5204+
FIELD_PREP(REG_LL_PROXY_H_PHY_TMR_IDX_M, tmr_idx) | REG_LL_PROXY_H_EXEC;
5205+
wr32(hw, REG_LL_PROXY_H, val);
5206+
5207+
/* Read the register repeatedly until the FW indicates completion */
5208+
err = read_poll_timeout_atomic(rd32, val,
5209+
!FIELD_GET(REG_LL_PROXY_H_EXEC, val),
5210+
10, REG_LL_PROXY_H_TIMEOUT_US, false, hw,
5211+
REG_LL_PROXY_H);
5212+
if (err) {
5213+
ice_debug(hw, ICE_DBG_PTP, "Failed to prepare PHY timer increment using low latency interface\n");
5214+
spin_unlock_irq(&params->atqbal_wq.lock);
5215+
return err;
5216+
}
5217+
5218+
spin_unlock_irq(&params->atqbal_wq.lock);
5219+
5220+
return 0;
5221+
}
5222+
51215223
/**
51225224
* ice_ptp_prep_phy_incval_e810 - Prep PHY port increment value change
51235225
* @hw: pointer to HW struct
@@ -5133,6 +5235,9 @@ static int ice_ptp_prep_phy_incval_e810(struct ice_hw *hw, u64 incval)
51335235
u8 tmr_idx;
51345236
int err;
51355237

5238+
if (hw->dev_caps.ts_dev_info.ll_phy_tmr_update)
5239+
return ice_ptp_prep_phy_incval_ll_e810(hw, incval);
5240+
51365241
tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned;
51375242
low = lower_32_bits(incval);
51385243
high = upper_32_bits(incval);

drivers/net/ethernet/intel/ice/ice_ptp_hw.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,11 @@ static inline bool ice_is_dual(struct ice_hw *hw)
690690

691691
/* Tx timestamp low latency read definitions */
692692
#define REG_LL_PROXY_H_TIMEOUT_US 2000
693+
#define REG_LL_PROXY_H_PHY_TMR_CMD_M GENMASK(7, 6)
694+
#define REG_LL_PROXY_H_PHY_TMR_CMD_ADJ 0x1
695+
#define REG_LL_PROXY_H_PHY_TMR_CMD_FREQ 0x2
693696
#define REG_LL_PROXY_H_TS_HIGH GENMASK(23, 16)
697+
#define REG_LL_PROXY_H_PHY_TMR_IDX_M BIT(24)
694698
#define REG_LL_PROXY_H_TS_IDX GENMASK(29, 24)
695699
#define REG_LL_PROXY_H_TS_INTR_ENA BIT(30)
696700
#define REG_LL_PROXY_H_EXEC BIT(31)

0 commit comments

Comments
 (0)