From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 585254404F; Wed, 12 Jun 2024 17:13:21 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 6510E42E47; Wed, 12 Jun 2024 17:05:07 +0200 (CEST) Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.10]) by mails.dpdk.org (Postfix) with ESMTP id 3115242D9D for ; Wed, 12 Jun 2024 17:05:03 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1718204704; x=1749740704; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=piP1ajP+B34MySRFVaEPSsDO5iT8QVRWIqEdEUsoOeo=; b=B/7P2IKLvojin2IZ48eVh1brCb0hXRnVxUv3O9/M/fgPfmKJTiqypJFb PkkQZLiA4q0QnldVHW1r8dHj3fv4Pfx2hvqYZRk/dQuXA5RrbfS6kKB21 RY+9aa34x5VJOOCnngsASWDMU/fq6Im5pZQFWkABHeVcGCUczgb66wsJc baM0qYMfoHqs+xKezHWGNVDJr8apcUEAZiEkXfArpEOOaxNzqrIlKr+TO TiqEToSxn1yDJVtD1ID9p0D/aciMWHwlQzHVDsz7lccWtXj71ckHfc07B /vgo3QJtBnb4ZS0PpQVrBjtlEDG4oSURuAdO8k+EGf00a1pB3ZUGoCzmX A==; X-CSE-ConnectionGUID: kGjoNo4jQHWi/W4Wtl589w== X-CSE-MsgGUID: jq+iSBfoQbyfrJS4zV3yoQ== X-IronPort-AV: E=McAfee;i="6700,10204,11101"; a="32459538" X-IronPort-AV: E=Sophos;i="6.08,233,1712646000"; d="scan'208";a="32459538" Received: from orviesa009.jf.intel.com ([10.64.159.149]) by orvoesa102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Jun 2024 08:04:47 -0700 X-CSE-ConnectionGUID: eszGMlJ/SAW7pYEnck59eQ== X-CSE-MsgGUID: D0nuHP1RSk6ImwxHdeniPQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.08,233,1712646000"; d="scan'208";a="39925359" Received: from silpixa00401119.ir.intel.com ([10.55.129.167]) by orviesa009.jf.intel.com with ESMTP; 12 Jun 2024 08:04:44 -0700 From: Anatoly Burakov To: dev@dpdk.org Cc: Ian Stokes , bruce.richardson@intel.com, Jacob Keller Subject: [PATCH v2 055/148] net/ice/base: fix ice_ptp_one_port_cmd to avoid stale PHY commands Date: Wed, 12 Jun 2024 16:00:49 +0100 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: <20240430154014.1026-1-ian.stokes@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org From: Ian Stokes Code using ice_ptp_one_port_cmd() (or the device specific variants for E822 and ETH56G) has a subtle bug where-in the executing of ice_ptp_exec_tmr_cmd() will cause all other ports to execute their last executed command. Most flows operate on all timers at once, but some flows such as handling link state change only operate on one port at a time. This can lead to issues if the userspace performs clock operations at just the right time. In addition to this bug, recent support for new hardware families has complicated the structure and organization of the various functions involved in programming PHY ports. For example: * ETH56G and E822 both re-implement the same looping structure for operating on all ports * E810 and E830 use "ice_ptp_port_cmd" which takes a register value to write and only operates on these two families despite having a generic name. Refactor ice_ptp_one_port_cmd() to both fix the issue of stale PHY port commands, and reduce the organizational complexity. Rename the existing device implementations from ice_ptp_one_port_cmd_ to ice_ptp_write_port_cmd_. This function just performs the task of writing one command to one port. Do not export this outside of ice_ptp_hw.c, instead ensure all callers use the refactored ice_ptp_one_port_cmd() which properly initializes all ports. Fix ice_ptp_one_port_cmd() to correctly loop over all ports. For the configured port, call ice_ptp_write_port_cmd() with the configured command. For all other ports, call ice_ptp_write_port_cmd() with ICE_PTP_NOP. Stop duplicating the port iteration logic in each of the ice_ptp_port_cmd_() implementations, instead moving this into the generic ice_ptp_port_cmd() implementation. Since every flow now properly initializes both the main timer register and all port registers, ice_ptp_clean_cmd() is no longer needed, so remove it. This fixes the issue of using stale PHY port commands, and aligns better with the flow for how ice_ptp_hw.c is implemented. Signed-off-by: Jacob Keller Signed-off-by: Ian Stokes --- drivers/net/ice/base/ice_ptp_hw.c | 649 +++++++++++++++--------------- drivers/net/ice/base/ice_ptp_hw.h | 25 +- 2 files changed, 356 insertions(+), 318 deletions(-) diff --git a/drivers/net/ice/base/ice_ptp_hw.c b/drivers/net/ice/base/ice_ptp_hw.c index bd759a0e83..ad3a7599d7 100644 --- a/drivers/net/ice/base/ice_ptp_hw.c +++ b/drivers/net/ice/base/ice_ptp_hw.c @@ -772,6 +772,104 @@ static enum ice_status ice_init_cgu_e82x(struct ice_hw *hw) return 0; } +/** + * @ice_ptp_tmr_cmd_to_src_reg - Convert to source timer command value + * @hw: pointer to HW struct + * @cmd: Timer command + * + * Returns: the source timer command register value for the given PTP timer + * command. + */ +static u32 ice_ptp_tmr_cmd_to_src_reg(struct ice_hw *hw, + enum ice_ptp_tmr_cmd cmd) +{ + u32 cmd_val; + u8 tmr_idx; + + switch (cmd) { + case ICE_PTP_INIT_TIME: + cmd_val = GLTSYN_CMD_INIT_TIME; + break; + case ICE_PTP_INIT_INCVAL: + cmd_val = GLTSYN_CMD_INIT_INCVAL; + break; + case ICE_PTP_ADJ_TIME: + cmd_val = GLTSYN_CMD_ADJ_TIME; + break; + case ICE_PTP_ADJ_TIME_AT_TIME: + cmd_val = GLTSYN_CMD_ADJ_INIT_TIME; + break; + case ICE_PTP_NOP: + case ICE_PTP_READ_TIME: + cmd_val = GLTSYN_CMD_READ_TIME; + break; + default: + ice_warn(hw, "Ignoring unrecognized timer command %u\n", cmd); + cmd_val = 0; + } + + tmr_idx = ice_get_ptp_src_clock_index(hw) << SEL_CPK_SRC; + + return tmr_idx | cmd_val; +} + +/** + * ice_ptp_tmr_cmd_to_port_reg- Convert to port timer command value + * @hw: pointer to HW struct + * @cmd: Timer command + * + * Note that some hardware families use a different command register value for + * the PHY ports, while other hardware families use the same register values + * as the source timer. + * + * Returns: the PHY port timer command register value for the given PTP timer + * command. + */ +static u32 ice_ptp_tmr_cmd_to_port_reg(struct ice_hw *hw, + enum ice_ptp_tmr_cmd cmd) +{ + u32 cmd_val, tmr_idx; + + /* Certain hardware families share the same register values for the + * port register and source timer register. + */ + switch (hw->phy_cfg) { + case ICE_PHY_E810: + case ICE_PHY_E830: + return ice_ptp_tmr_cmd_to_src_reg(hw, cmd) & TS_CMD_MASK_E810; + default: + break; + } + + switch (cmd) { + case ICE_PTP_INIT_TIME: + cmd_val = PHY_CMD_INIT_TIME; + break; + case ICE_PTP_INIT_INCVAL: + cmd_val = PHY_CMD_INIT_INCVAL; + break; + case ICE_PTP_ADJ_TIME: + cmd_val = PHY_CMD_ADJ_TIME; + break; + case ICE_PTP_ADJ_TIME_AT_TIME: + cmd_val = PHY_CMD_ADJ_TIME_AT_TIME; + break; + case ICE_PTP_READ_TIME: + cmd_val = PHY_CMD_READ_TIME; + break; + case ICE_PTP_NOP: + cmd_val = 0; + break; + default: + ice_warn(hw, "Ignoring unrecognized timer command %u\n", cmd); + cmd_val = 0; + } + + tmr_idx = ice_get_ptp_src_clock_index(hw) << SEL_PHY_SRC; + + return tmr_idx | cmd_val; +} + /** * ice_ptp_src_cmd - Prepare source timer for a timer command * @hw: pointer to HW structure @@ -781,34 +879,7 @@ static enum ice_status ice_init_cgu_e82x(struct ice_hw *hw) */ void ice_ptp_src_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd) { - u32 cmd_val; - u8 tmr_idx; - - tmr_idx = ice_get_ptp_src_clock_index(hw); - cmd_val = tmr_idx << SEL_CPK_SRC; - - switch (cmd) { - case ICE_PTP_INIT_TIME: - cmd_val |= GLTSYN_CMD_INIT_TIME; - break; - case ICE_PTP_INIT_INCVAL: - cmd_val |= GLTSYN_CMD_INIT_INCVAL; - break; - case ICE_PTP_ADJ_TIME: - cmd_val |= GLTSYN_CMD_ADJ_TIME; - break; - case ICE_PTP_ADJ_TIME_AT_TIME: - cmd_val |= GLTSYN_CMD_ADJ_INIT_TIME; - break; - case ICE_PTP_READ_TIME: - cmd_val |= GLTSYN_CMD_READ_TIME; - break; - case ICE_PTP_NOP: - break; - default: - ice_warn(hw, "Unknown timer command %u\n", cmd); - return; - } + u32 cmd_val = ice_ptp_tmr_cmd_to_src_reg(hw, cmd); wr32(hw, GLTSYN_CMD, cmd_val); } @@ -827,18 +898,6 @@ static void ice_ptp_exec_tmr_cmd(struct ice_hw *hw) ice_flush(hw); } -/** - * ice_ptp_clean_cmd - Clean the timer command register - * @hw: pointer to HW struct - * - * Zero out the GLTSYN_CMD to avoid any residual command execution. - */ -static void ice_ptp_clean_cmd(struct ice_hw *hw) -{ - wr32(hw, GLTSYN_CMD, 0); - ice_flush(hw); -} - /* 56G PHY access functions */ static const u32 eth56g_port_base[ICE_NUM_PHY_PORTS] = { ICE_PHY0_BASE, @@ -1796,7 +1855,7 @@ ice_ptp_one_port_cmd_eth56g(struct ice_hw *hw, u8 port, * Prepare all ports connected to this device for an upcoming timer sync * command. */ -static int +int ice_ptp_port_cmd_eth56g(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd, bool lock_sbq) { @@ -1949,7 +2008,6 @@ ice_read_phy_and_phc_time_eth56g(struct ice_hw *hw, u8 port, u64 *phy_time, /* Issue the sync to start the ICE_PTP_READ_TIME capture */ ice_ptp_exec_tmr_cmd(hw); - ice_ptp_clean_cmd(hw); /* Read the captured PHC time from the shadow time registers */ zo = rd32(hw, GLTSYN_SHTIME_0(tmr_idx)); @@ -2024,7 +2082,6 @@ static int ice_sync_phy_timer_eth56g(struct ice_hw *hw, u8 port) /* Issue the sync to activate the time adjustment */ ice_ptp_exec_tmr_cmd(hw); - ice_ptp_clean_cmd(hw); /* Re-capture the timer values to flush the command registers and * verify that the time was properly adjusted. @@ -2185,8 +2242,7 @@ ice_ptp_read_tx_hwtstamp_status_eth56g(struct ice_hw *hw, u32 *ts_status) * mask. Returns the mask of ports where TX timestamps are available * @hw: pointer to the HW struct */ -int -ice_ptp_init_phy_cfg(struct ice_hw *hw) +int ice_ptp_init_phy_cfg(struct ice_hw *hw) { int status; u32 phy_rev; @@ -2211,8 +2267,7 @@ ice_ptp_init_phy_cfg(struct ice_hw *hw) return 0; } -/* ---------------------------------------------------------------------------- - * E822 family functions +/* E822 family functions * * The following functions operate on the E822 family of devices. */ @@ -3154,7 +3209,7 @@ ice_ptp_read_port_capture_e822(struct ice_hw *hw, u8 port, u64 *tx_ts, } /** - * ice_ptp_one_port_cmd_e822 - Prepare a single PHY port for a timer command + * ice_ptp_write_port_cmd_e822 - Prepare a single PHY port for a timer command * @hw: pointer to HW struct * @port: Port to which cmd has to be sent * @cmd: Command to be sent to the port @@ -3166,50 +3221,13 @@ ice_ptp_read_port_capture_e822(struct ice_hw *hw, u8 port, u64 *tx_ts, * always handles all external PHYs internally. */ int -ice_ptp_one_port_cmd_e822(struct ice_hw *hw, u8 port, enum ice_ptp_tmr_cmd cmd, - bool lock_sbq) +ice_ptp_write_port_cmd_e822(struct ice_hw *hw, u8 port, + enum ice_ptp_tmr_cmd cmd, bool lock_sbq) { + u32 val = ice_ptp_tmr_cmd_to_port_reg(hw, cmd); int status; - u32 cmd_val, val; - u8 tmr_idx; - - tmr_idx = ice_get_ptp_src_clock_index(hw); - cmd_val = tmr_idx << SEL_PHY_SRC; - switch (cmd) { - case ICE_PTP_INIT_TIME: - cmd_val |= PHY_CMD_INIT_TIME; - break; - case ICE_PTP_INIT_INCVAL: - cmd_val |= PHY_CMD_INIT_INCVAL; - break; - case ICE_PTP_ADJ_TIME: - cmd_val |= PHY_CMD_ADJ_TIME; - break; - case ICE_PTP_ADJ_TIME_AT_TIME: - cmd_val |= PHY_CMD_ADJ_TIME_AT_TIME; - break; - case ICE_PTP_READ_TIME: - cmd_val |= PHY_CMD_READ_TIME; - break; - default: - ice_warn(hw, "Unknown timer command %u\n", cmd); - return ICE_ERR_PARAM; - } /* Tx case */ - /* Read, modify, write */ - status = ice_read_phy_reg_e822_lp(hw, port, P_REG_TX_TMR_CMD, &val, - lock_sbq); - if (status) { - ice_debug(hw, ICE_DBG_PTP, "Failed to read TX_TMR_CMD, status %d\n", - status); - return status; - } - - /* Modify necessary bits only and perform write */ - val &= ~TS_CMD_MASK; - val |= cmd_val; - status = ice_write_phy_reg_e822_lp(hw, port, P_REG_TX_TMR_CMD, val, lock_sbq); if (status) { @@ -3219,19 +3237,6 @@ ice_ptp_one_port_cmd_e822(struct ice_hw *hw, u8 port, enum ice_ptp_tmr_cmd cmd, } /* Rx case */ - /* Read, modify, write */ - status = ice_read_phy_reg_e822_lp(hw, port, P_REG_RX_TMR_CMD, &val, - lock_sbq); - if (status) { - ice_debug(hw, ICE_DBG_PTP, "Failed to read RX_TMR_CMD, status %d\n", - status); - return status; - } - - /* Modify necessary bits only and perform write */ - val &= ~TS_CMD_MASK; - val |= cmd_val; - status = ice_write_phy_reg_e822_lp(hw, port, P_REG_RX_TMR_CMD, val, lock_sbq); if (status) { @@ -3243,32 +3248,6 @@ ice_ptp_one_port_cmd_e822(struct ice_hw *hw, u8 port, enum ice_ptp_tmr_cmd cmd, return 0; } -/** - * ice_ptp_port_cmd_e822 - Prepare all ports for a timer command - * @hw: pointer to the HW struct - * @cmd: timer command to prepare - * @lock_sbq: true if the sideband queue lock must be acquired - * - * Prepare all ports connected to this device for an upcoming timer sync - * command. - */ -static int -ice_ptp_port_cmd_e822(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd, - bool lock_sbq) -{ - u8 port; - - for (port = 0; port < ICE_NUM_EXTERNAL_PORTS; port++) { - int status; - - status = ice_ptp_one_port_cmd_e822(hw, port, cmd, lock_sbq); - if (status) - return status; - } - - return 0; -} - /* E822 Vernier calibration functions * * The following functions are used as part of the vernier calibration of @@ -4138,13 +4117,12 @@ ice_read_phy_and_phc_time_e822(struct ice_hw *hw, u8 port, u64 *phy_time, ice_ptp_src_cmd(hw, ICE_PTP_READ_TIME); /* Prepare the PHY timer for a ICE_PTP_READ_TIME capture command */ - status = ice_ptp_one_port_cmd_e822(hw, port, ICE_PTP_READ_TIME, true); + status = ice_ptp_one_port_cmd(hw, port, ICE_PTP_READ_TIME, true); if (status) return status; /* Issue the sync to start the ICE_PTP_READ_TIME capture */ ice_ptp_exec_tmr_cmd(hw); - ice_ptp_clean_cmd(hw); /* Read the captured PHC time from the shadow time registers */ zo = rd32(hw, GLTSYN_SHTIME_0(tmr_idx)); @@ -4210,7 +4188,7 @@ static int ice_sync_phy_timer_e822(struct ice_hw *hw, u8 port) if (status) goto err_unlock; - status = ice_ptp_one_port_cmd_e822(hw, port, ICE_PTP_ADJ_TIME, true); + status = ice_ptp_one_port_cmd(hw, port, ICE_PTP_ADJ_TIME, true); if (status) goto err_unlock; @@ -4219,7 +4197,6 @@ static int ice_sync_phy_timer_e822(struct ice_hw *hw, u8 port) /* Issue the sync to activate the time adjustment */ ice_ptp_exec_tmr_cmd(hw); - ice_ptp_clean_cmd(hw); /* Re-capture the timer values to flush the command registers and * verify that the time was properly adjusted. @@ -4315,7 +4292,6 @@ ice_start_phy_timer_e822(struct ice_hw *hw, u8 port, bool bypass) u64 incval; u8 tmr_idx; - ice_ptp_clean_cmd(hw); tmr_idx = ice_get_ptp_src_clock_index(hw); status = ice_stop_phy_timer_e822(hw, port, false); @@ -4340,7 +4316,7 @@ ice_start_phy_timer_e822(struct ice_hw *hw, u8 port, bool bypass) if (status) return status; - status = ice_ptp_one_port_cmd_e822(hw, port, ICE_PTP_INIT_INCVAL, true); + status = ice_ptp_one_port_cmd(hw, port, ICE_PTP_INIT_INCVAL, true); if (status) return status; @@ -4368,7 +4344,7 @@ ice_start_phy_timer_e822(struct ice_hw *hw, u8 port, bool bypass) if (status) return status; - status = ice_ptp_one_port_cmd_e822(hw, port, ICE_PTP_INIT_INCVAL, true); + status = ice_ptp_one_port_cmd(hw, port, ICE_PTP_INIT_INCVAL, true); if (status) return status; @@ -4690,38 +4666,6 @@ ice_read_phy_tstamp_e810(struct ice_hw *hw, u8 lport, u8 idx, u64 *tstamp) return 0; } -/** - * ice_read_phy_tstamp_e830 - Read a PHY timestamp out of the external PHY - * @hw: pointer to the HW struct - * @lport: the lport to read from - * @idx: the timestamp index to read - * @tstamp: on return, the 40bit timestamp value - * - * Read a 40bit timestamp value out of the timestamp block of the external PHY - * on the E830 device. - */ -static enum ice_status -ice_read_phy_tstamp_e830(struct ice_hw *hw, u8 lport, u8 idx, u64 *tstamp) -{ - u32 hi_addr = E830_HIGH_TX_MEMORY_BANK(idx, lport); - u32 lo_addr = E830_LOW_TX_MEMORY_BANK(idx, lport); - u32 lo_val, hi_val, lo; - u8 hi; - - lo_val = rd32(hw, lo_addr); - hi_val = rd32(hw, hi_addr); - - lo = lo_val; - hi = (u8)hi_val; - - /* For E830 devices, the timestamp is reported with the lower 32 bits - * in the low register, and the upper 8 bits in the high register. - */ - *tstamp = ((u64)hi) << TS_HIGH_S | ((u64)lo & TS_LOW_M); - - return ICE_SUCCESS; -} - /** * ice_clear_phy_tstamp_e810 - Clear a timestamp from the external PHY * @hw: pointer to the HW struct @@ -4912,31 +4856,6 @@ ice_ptp_prep_phy_incval_e810(struct ice_hw *hw, u64 incval) return 0; } -/** - * ice_ptp_write_direct_incval_e830 - Prep PHY port increment value change - * @hw: pointer to HW struct - * @incval: The new 40bit increment value to prepare - * - * Prepare the PHY port for a new increment value by programming the PHC - * GLTSYN_INCVAL_L and GLTSYN_INCVAL_H registers. The actual change is - * completed by FW automatically. - */ -static enum ice_status -ice_ptp_write_direct_incval_e830(struct ice_hw *hw, u64 incval) -{ - u32 high, low; - u8 tmr_idx; - - tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; - low = ICE_LO_DWORD(incval); - high = ICE_HI_DWORD(incval); - - wr32(hw, GLTSYN_INCVAL_L(tmr_idx), low); - wr32(hw, GLTSYN_INCVAL_H(tmr_idx), high); - - return ICE_SUCCESS; -} - /** * ice_ptp_prep_phy_adj_target_e810 - Prepare PHY port with adjust target * @hw: Board private structure @@ -4976,68 +4895,6 @@ ice_ptp_prep_phy_adj_target_e810(struct ice_hw *hw, u32 target_time) return 0; } -/** - * ice_ptp_port_cmd - Prepare all external PHYs for a timer command - * @hw: pointer to HW struct - * @cmd: Command to be sent to the port - * @lock_sbq: true if the sideband queue lock must be acquired - * @eth_gltsyn_cmd_addr: address for ETH_GLTSYN_CMD register - * - * Prepare the external PHYs connected to this device for a timer sync - * command. - */ -static int -ice_ptp_port_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd, - bool lock_sbq, u32 eth_gltsyn_cmd_addr) -{ - int status; - u32 cmd_val, val; - - switch (cmd) { - case ICE_PTP_INIT_TIME: - cmd_val = GLTSYN_CMD_INIT_TIME; - break; - case ICE_PTP_INIT_INCVAL: - cmd_val = GLTSYN_CMD_INIT_INCVAL; - break; - case ICE_PTP_ADJ_TIME: - cmd_val = GLTSYN_CMD_ADJ_TIME; - break; - case ICE_PTP_ADJ_TIME_AT_TIME: - cmd_val = GLTSYN_CMD_ADJ_INIT_TIME; - break; - case ICE_PTP_READ_TIME: - cmd_val = GLTSYN_CMD_READ_TIME; - break; - default: - ice_warn(hw, "Unknown timer command %u\n", cmd); - return ICE_ERR_PARAM; - } - - /* Read, modify, write */ - status = ice_read_phy_reg_e810_lp(hw, eth_gltsyn_cmd_addr, &val, - lock_sbq); - if (status) { - ice_debug(hw, ICE_DBG_PTP, "Failed to read GLTSYN_CMD, status %d\n", - status); - return status; - } - - /* Modify necessary bits only and perform write */ - val &= ~TS_CMD_MASK_E810; - val |= cmd_val; - - status = ice_write_phy_reg_e810_lp(hw, eth_gltsyn_cmd_addr, val, - lock_sbq); - if (status) { - ice_debug(hw, ICE_DBG_PTP, "Failed to write back GLTSYN_CMD, status %d\n", - status); - return status; - } - - return 0; -} - /** * ice_ptp_port_cmd_e810 - Prepare all external PHYs for a timer command * @hw: pointer to HW struct @@ -5047,27 +4904,14 @@ ice_ptp_port_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd, * Prepare the external PHYs connected to this device for a timer sync * command. */ -static enum ice_status +enum ice_status ice_ptp_port_cmd_e810(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd, bool lock_sbq) { - return ice_ptp_port_cmd(hw, cmd, lock_sbq, E810_ETH_GLTSYN_CMD); -} + u32 val = ice_ptp_tmr_cmd_to_port_reg(hw, cmd); -/** - * ice_ptp_port_cmd_e830 - Prepare all external PHYs for a timer command - * @hw: pointer to HW struct - * @cmd: Command to be sent to the port - * @lock_sbq: true if the sideband queue lock must be acquired - * - * Prepare the external PHYs connected to this device for a timer sync - * command. - */ -static enum ice_status -ice_ptp_port_cmd_e830(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd, - bool lock_sbq) -{ - return ice_ptp_port_cmd(hw, cmd, lock_sbq, E830_ETH_GLTSYN_CMD); + return ice_write_phy_reg_e810_lp(hw, E810_ETH_GLTSYN_CMD, val, + lock_sbq); } /* E810T SMA functions @@ -5296,6 +5140,129 @@ bool ice_is_pca9575_present(struct ice_hw *hw) return false; } +/** + * ice_ptp_write_direct_incval_e830 - Prep PHY port increment value change + * @hw: pointer to HW struct + * @incval: The new 40bit increment value to prepare + * + * Prepare the PHY port for a new increment value by programming the PHC + * GLTSYN_INCVAL_L and GLTSYN_INCVAL_H registers. The actual change is + * completed by FW automatically. + */ +static enum ice_status +ice_ptp_write_direct_incval_e830(struct ice_hw *hw, u64 incval) +{ + u32 high, low; + u8 tmr_idx; + + tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; + low = ICE_LO_DWORD(incval); + high = ICE_HI_DWORD(incval); + + wr32(hw, GLTSYN_INCVAL_L(tmr_idx), low); + wr32(hw, GLTSYN_INCVAL_H(tmr_idx), high); + + return ICE_SUCCESS; +} + +/** + * ice_ptp_write_direct_phc_time_e830 - Prepare PHY port with initial time + * @hw: Board private structure + * @time: Time to initialize the PHY port clock to + * + * Program the PHY port ETH_GLTSYN_SHTIME registers in preparation setting the + * initial clock time. The time will not actually be programmed until the + * driver issues an ICE_PTP_INIT_TIME command. + * + * The time value is the upper 32 bits of the PHY timer, usually in units of + * nominal nanoseconds. + */ +static enum ice_status +ice_ptp_write_direct_phc_time_e830(struct ice_hw *hw, u64 time) +{ + u8 tmr_idx; + + tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; + + wr32(hw, GLTSYN_TIME_0(tmr_idx), 0); + wr32(hw, GLTSYN_TIME_L(tmr_idx), ICE_LO_DWORD(time)); + wr32(hw, GLTSYN_TIME_H(tmr_idx), ICE_HI_DWORD(time)); + + return ICE_SUCCESS; +} + +/** + * ice_ptp_port_cmd_e830 - Prepare all external PHYs for a timer command + * @hw: pointer to HW struct + * @cmd: Command to be sent to the port + * @lock_sbq: true if the sideband queue lock must be acquired + * + * Prepare the external PHYs connected to this device for a timer sync + * command. + */ +enum ice_status +ice_ptp_port_cmd_e830(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd, + bool lock_sbq) +{ + u32 val = ice_ptp_tmr_cmd_to_port_reg(hw, cmd); + + return ice_write_phy_reg_e810_lp(hw, E830_ETH_GLTSYN_CMD, val, + lock_sbq); +} + +/** + * ice_read_phy_tstamp_e830 - Read a PHY timestamp out of the external PHY + * @hw: pointer to the HW struct + * @lport: the lport to read from + * @idx: the timestamp index to read + * @tstamp: on return, the 40bit timestamp value + * + * Read a 40bit timestamp value out of the timestamp block of the external PHY + * on the E830 device. + */ +static enum ice_status +ice_read_phy_tstamp_e830(struct ice_hw *hw, u8 lport, u8 idx, u64 *tstamp) +{ + u32 hi_addr = E830_HIGH_TX_MEMORY_BANK(idx, lport); + u32 lo_addr = E830_LOW_TX_MEMORY_BANK(idx, lport); + u32 lo_val, hi_val, lo; + u8 hi; + + lo_val = rd32(hw, lo_addr); + hi_val = rd32(hw, hi_addr); + + lo = lo_val; + hi = (u8)hi_val; + + /* For E830 devices, the timestamp is reported with the lower 32 bits + * in the low register, and the upper 8 bits in the high register. + */ + *tstamp = ((u64)hi) << TS_HIGH_S | ((u64)lo & TS_LOW_M); + + return ICE_SUCCESS; +} + +/** + * ice_get_phy_tx_tstamp_ready_e830 - Read Tx memory status register + * @hw: pointer to the HW struct + * @port: the PHY port to read + * @tstamp_ready: contents of the Tx memory status register + * + */ +enum ice_status +ice_get_phy_tx_tstamp_ready_e830(struct ice_hw *hw, u8 port, u64 *tstamp_ready) +{ + u64 hi; + u32 lo; + + lo = rd32(hw, E830_PRTMAC_TS_TX_MEM_VALID_L); + hi = (u64)rd32(hw, E830_PRTMAC_TS_TX_MEM_VALID_H) << 32; + + *tstamp_ready = hi | lo; + + return ICE_SUCCESS; +} + /* Device agnostic functions * * The following functions implement shared behavior common to both E822/E823 @@ -5352,6 +5319,100 @@ void ice_ptp_unlock(struct ice_hw *hw) wr32(hw, PFTSYN_SEM + (PFTSYN_SEM_BYTES * hw->pf_id), 0); } +/** + * ice_ptp_write_port_cmd - Prepare a single PHY port for a timer command + * @hw: pointer to HW struct + * @port: Port to which cmd has to be sent + * @cmd: Command to be sent to the port + * @lock_sbq: true if the sideband queue lock must be acquired + * + * Prepare one port for the upcoming timer sync command. Do not use this for + * programming only a single port, instead use ice_ptp_one_port_cmd() to + * ensure non-modified ports get properly initialized to ICE_PTP_NOP. + */ +enum ice_status +ice_ptp_write_port_cmd(struct ice_hw *hw, u8 port, enum ice_ptp_tmr_cmd cmd, + bool lock_sbq) +{ + switch (hw->phy_cfg) { + case ICE_PHY_E822: + return ice_ptp_write_port_cmd_e822(hw, port, cmd, lock_sbq); + default: + return ICE_ERR_NOT_SUPPORTED; + } +} + +/** + * ice_ptp_one_port_cmd - Program one PHY port for a timer command + * @hw: pointer to HW struct + * @configured_port: the port that should execute the command + * @configured_cmd: the command to be executed on the configured port + * @lock_sbq: true if the sideband queue lock must be acquired + * + * Prepare one port for executing a timer command, while preparing all other + * ports to ICE_PTP_NOP. This allows executing a command on a single port + * while ensuring all other ports do not execute stale commands. + */ +enum ice_status +ice_ptp_one_port_cmd(struct ice_hw *hw, u8 configured_port, + enum ice_ptp_tmr_cmd configured_cmd, bool lock_sbq) +{ + u8 port; + + for (port = 0; port < hw->max_phy_port; port++) { + enum ice_ptp_tmr_cmd cmd; + enum ice_status status; + + /* Program the configured port with the configured command, + * program all other ports with ICE_PTP_NOP. + */ + cmd = port == configured_port ? configured_cmd : ICE_PTP_NOP; + + status = ice_ptp_write_port_cmd(hw, port, cmd, lock_sbq); + if (status) + return status; + } + + return ICE_SUCCESS; +} + +/** + * ice_ptp_port_cmd - Prepare PHY ports for a timer sync command + * @hw: pointer to HW struct + * @cmd: the timer command to setup + * @lock_sbq: true of sideband queue lock must be acquired + * + * Prepare all PHY ports on this device for the requested timer command. For + * some families this can be done in one shot, but for other families each + * port must be configured individually. + */ +static enum ice_status +ice_ptp_port_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd, bool lock_sbq) +{ + u8 port; + + /* PHY models which can program all ports simultaneously */ + switch (hw->phy_cfg) { + case ICE_PHY_E830: + return ice_ptp_port_cmd_e830(hw, cmd, lock_sbq); + case ICE_PHY_E810: + return ice_ptp_port_cmd_e810(hw, cmd, lock_sbq); + default: + break; + } + + /* PHY models which require programming each port separately */ + for (port = 0; port < hw->max_phy_port; port++) { + enum ice_status status; + + status = ice_ptp_write_port_cmd(hw, port, cmd, lock_sbq); + if (status) + return status; + } + + return ICE_SUCCESS; +} + /** * ice_ptp_tmr_cmd - Prepare and trigger a timer sync command * @hw: pointer to HW struct @@ -5372,22 +5433,7 @@ static int ice_ptp_tmr_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd, ice_ptp_src_cmd(hw, cmd); /* Next, prepare the ports */ - switch (hw->phy_cfg) { - case ICE_PHY_ETH56G: - status = ice_ptp_port_cmd_eth56g(hw, cmd, lock_sbq); - break; - case ICE_PHY_E830: - status = ice_ptp_port_cmd_e830(hw, cmd, lock_sbq); - break; - case ICE_PHY_E810: - status = ice_ptp_port_cmd_e810(hw, cmd, lock_sbq); - break; - case ICE_PHY_E822: - status = ice_ptp_port_cmd_e822(hw, cmd, lock_sbq); - break; - default: - status = ICE_ERR_NOT_SUPPORTED; - } + status = ice_ptp_port_cmd(hw, cmd, lock_sbq); if (status) { ice_debug(hw, ICE_DBG_PTP, "Failed to prepare PHY ports for timer command %u, status %d\n", cmd, status); @@ -5398,37 +5444,10 @@ static int ice_ptp_tmr_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd, * commands synchronously */ ice_ptp_exec_tmr_cmd(hw); - ice_ptp_clean_cmd(hw); return 0; } -/** - * ice_ptp_write_direct_phc_time_e830 - Prepare PHY port with initial time - * @hw: Board private structure - * @time: Time to initialize the PHY port clock to - * - * Program the PHY port ETH_GLTSYN_SHTIME registers in preparation setting the - * initial clock time. The time will not actually be programmed until the - * driver issues an ICE_PTP_INIT_TIME command. - * - * The time value is the upper 32 bits of the PHY timer, usually in units of - * nominal nanoseconds. - */ -static enum ice_status -ice_ptp_write_direct_phc_time_e830(struct ice_hw *hw, u64 time) -{ - u8 tmr_idx; - - tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; - - wr32(hw, GLTSYN_TIME_0(tmr_idx), 0); - wr32(hw, GLTSYN_TIME_L(tmr_idx), ICE_LO_DWORD(time)); - wr32(hw, GLTSYN_TIME_H(tmr_idx), ICE_HI_DWORD(time)); - - return ICE_SUCCESS; -} - /** * ice_ptp_init_time - Initialize device time to provided value * @hw: pointer to HW struct @@ -5451,7 +5470,7 @@ int ice_ptp_init_time(struct ice_hw *hw, u64 time) /* Source timers */ /* For E830 we don't need to use shadow registers, its automatic */ - if (ice_is_e830(hw)) + if (hw->phy_cfg == ICE_PHY_E830) return ice_ptp_write_direct_phc_time_e830(hw, time); wr32(hw, GLTSYN_SHTIME_L(tmr_idx), ICE_LO_DWORD(time)); @@ -5502,7 +5521,7 @@ int ice_ptp_write_incval(struct ice_hw *hw, u64 incval) tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; /* For E830 we don't need to use shadow registers, its automatic */ - if (ice_is_e830(hw)) + if (hw->phy_cfg == ICE_PHY_E830) return ice_ptp_write_direct_incval_e830(hw, incval); /* Shadow Adjust */ diff --git a/drivers/net/ice/base/ice_ptp_hw.h b/drivers/net/ice/base/ice_ptp_hw.h index da5acf7556..1fda3cd74c 100644 --- a/drivers/net/ice/base/ice_ptp_hw.h +++ b/drivers/net/ice/base/ice_ptp_hw.h @@ -143,6 +143,9 @@ int ice_clear_phy_tstamp(struct ice_hw *hw, u8 block, u8 idx); int ice_ptp_init_phc(struct ice_hw *hw); bool refsync_pin_id_valid(struct ice_hw *hw, u8 id); +int +ice_ptp_one_port_cmd(struct ice_hw *hw, u8 configured_port, + enum ice_ptp_tmr_cmd configured_cmd, bool lock_sbq); /* E822 family functions */ int @@ -162,11 +165,14 @@ int ice_ptp_read_port_capture_e822(struct ice_hw *hw, u8 port, u64 *tx_ts, u64 *rx_ts); int -ice_ptp_one_port_cmd_e822(struct ice_hw *hw, u8 port, - enum ice_ptp_tmr_cmd cmd, bool lock_sbq); -int ice_cfg_cgu_pll_e822(struct ice_hw *hw, enum ice_time_ref_freq clk_freq, enum ice_clk_src clk_src); +int +ice_ptp_write_port_cmd_e822(struct ice_hw *hw, u8 port, + enum ice_ptp_tmr_cmd cmd, bool lock_sbq); +enum ice_status +ice_ptp_write_port_cmd(struct ice_hw *hw, u8 port, enum ice_ptp_tmr_cmd cmd, + bool lock_sbq); enum ice_status ice_cfg_cgu_pll_e825c(struct ice_hw *hw, enum ice_time_ref_freq *clk_freq, enum ice_clk_src *clk_src); @@ -180,6 +186,19 @@ ice_cfg_cgu_bypass_mux_e825c(struct ice_hw *hw, u8 port_num, bool clock_1588, unsigned int ena); enum ice_status ice_cfg_synce_ethdiv_e825c(struct ice_hw *hw, u8 *divider); +/* E8 Family */ +enum ice_status +ice_ptp_port_cmd_e830(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd, + bool lock_sbq); +enum ice_status +ice_ptp_port_cmd_e810(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd, + bool lock_sbq); +enum ice_status +ice_get_phy_tx_tstamp_ready_e830(struct ice_hw *hw, u8 port, u64 *tstamp_ready); +int +ice_ptp_port_cmd_eth56g(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd, + bool lock_sbq); + /** * ice_e822_time_ref - Get the current TIME_REF from capabilities * @hw: pointer to the HW structure -- 2.43.0