@@ -227,563 +227,575 @@ static u64 ice_ptp_read_src_incval(struct ice_hw *hw)
}
/**
- * ice_ptp_tmr_cmd_to_src_reg - Convert to source timer command value
- * @hw: pointer to HW struct
- * @cmd: Timer command
+ * ice_read_cgu_reg_e82x - Read a CGU register
+ * @hw: pointer to the HW struct
+ * @addr: Register address to read
+ * @val: storage for register value read
*
- * Return: the source timer command register value for the given PTP timer
- * command.
+ * Read the contents of a register of the Clock Generation Unit. Only
+ * applicable to E822 devices.
+ *
+ * Return: 0 on success, other error codes when failed to read from CGU
*/
-static u32 ice_ptp_tmr_cmd_to_src_reg(struct ice_hw *hw,
- enum ice_ptp_tmr_cmd cmd)
+static int ice_read_cgu_reg_e82x(struct ice_hw *hw, u32 addr, u32 *val)
{
- u32 cmd_val, tmr_idx;
+ struct ice_sbq_msg_input cgu_msg = {
+ .opcode = ice_sbq_msg_rd,
+ .dest_dev = cgu,
+ .msg_addr_low = addr
+ };
+ int err;
- 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:
- dev_warn(ice_hw_to_dev(hw),
- "Ignoring unrecognized timer command %u\n", cmd);
- cmd_val = 0;
+ err = ice_sbq_rw_reg(hw, &cgu_msg);
+ if (err) {
+ ice_debug(hw, ICE_DBG_PTP, "Failed to read CGU register 0x%04x, err %d\n",
+ addr, err);
+ return err;
}
- tmr_idx = ice_get_ptp_src_clock_index(hw);
+ *val = cgu_msg.data;
- return tmr_idx << SEL_CPK_SRC | cmd_val;
+ return 0;
}
/**
- * ice_ptp_tmr_cmd_to_port_reg- Convert to port timer command value
- * @hw: pointer to HW struct
- * @cmd: Timer command
+ * ice_write_cgu_reg_e82x - Write a CGU register
+ * @hw: pointer to the HW struct
+ * @addr: Register address to write
+ * @val: value to write into the register
*
- * 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.
+ * Write the specified value to a register of the Clock Generation Unit. Only
+ * applicable to E822 devices.
*
- * Return: the PHY port timer command register value for the given PTP timer
- * command.
+ * Return: 0 on success, other error codes when failed to write to CGU
*/
-static u32 ice_ptp_tmr_cmd_to_port_reg(struct ice_hw *hw,
- enum ice_ptp_tmr_cmd cmd)
+static int ice_write_cgu_reg_e82x(struct ice_hw *hw, u32 addr, u32 val)
{
- u32 cmd_val, tmr_idx;
-
- /* Certain hardware families share the same register values for the
- * port register and source timer register.
- */
- switch (hw->ptp.phy_model) {
- case ICE_PHY_E810:
- return ice_ptp_tmr_cmd_to_src_reg(hw, cmd) & TS_CMD_MASK_E810;
- default:
- break;
- }
+ struct ice_sbq_msg_input cgu_msg = {
+ .opcode = ice_sbq_msg_wr,
+ .dest_dev = cgu,
+ .msg_addr_low = addr,
+ .data = val
+ };
+ int err;
- 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:
- dev_warn(ice_hw_to_dev(hw),
- "Ignoring unrecognized timer command %u\n", cmd);
- cmd_val = 0;
+ err = ice_sbq_rw_reg(hw, &cgu_msg);
+ if (err) {
+ ice_debug(hw, ICE_DBG_PTP, "Failed to write CGU register 0x%04x, err %d\n",
+ addr, err);
+ return err;
}
- tmr_idx = ice_get_ptp_src_clock_index(hw);
-
- return tmr_idx << SEL_PHY_SRC | cmd_val;
+ return err;
}
/**
- * ice_ptp_src_cmd - Prepare source timer for a timer command
- * @hw: pointer to HW structure
- * @cmd: Timer command
+ * ice_clk_freq_str - Convert time_ref_freq to string
+ * @clk_freq: Clock frequency
*
- * Prepare the source timer for an upcoming timer sync command.
+ * Return: specified TIME_REF clock frequency converted to a string
*/
-void ice_ptp_src_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd)
+static const char *ice_clk_freq_str(enum ice_time_ref_freq clk_freq)
{
- u32 cmd_val = ice_ptp_tmr_cmd_to_src_reg(hw, cmd);
-
- wr32(hw, GLTSYN_CMD, cmd_val);
+ switch (clk_freq) {
+ case ICE_TIME_REF_FREQ_25_000:
+ return "25 MHz";
+ case ICE_TIME_REF_FREQ_122_880:
+ return "122.88 MHz";
+ case ICE_TIME_REF_FREQ_125_000:
+ return "125 MHz";
+ case ICE_TIME_REF_FREQ_153_600:
+ return "153.6 MHz";
+ case ICE_TIME_REF_FREQ_156_250:
+ return "156.25 MHz";
+ case ICE_TIME_REF_FREQ_245_760:
+ return "245.76 MHz";
+ default:
+ return "Unknown";
+ }
}
/**
- * ice_ptp_exec_tmr_cmd - Execute all prepared timer commands
- * @hw: pointer to HW struct
+ * ice_clk_src_str - Convert time_ref_src to string
+ * @clk_src: Clock source
*
- * Write the SYNC_EXEC_CMD bit to the GLTSYN_CMD_SYNC register, and flush the
- * write immediately. This triggers the hardware to begin executing all of the
- * source and PHY timer commands synchronously.
+ * Return: specified clock source converted to its string name
*/
-static void ice_ptp_exec_tmr_cmd(struct ice_hw *hw)
+static const char *ice_clk_src_str(enum ice_clk_src clk_src)
{
- struct ice_pf *pf = container_of(hw, struct ice_pf, hw);
-
- guard(spinlock)(&pf->adapter->ptp_gltsyn_time_lock);
- wr32(hw, GLTSYN_CMD_SYNC, SYNC_EXEC_CMD);
- ice_flush(hw);
+ switch (clk_src) {
+ case ICE_CLK_SRC_TCX0:
+ return "TCX0";
+ case ICE_CLK_SRC_TIME_REF:
+ return "TIME_REF";
+ default:
+ return "Unknown";
+ }
}
-/* E822 family functions
- *
- * The following functions operate on the E822 family of devices.
- */
-
/**
- * ice_fill_phy_msg_e82x - Fill message data for a PHY register access
+ * ice_cfg_cgu_pll_e82x - Configure the Clock Generation Unit
* @hw: pointer to the HW struct
- * @msg: the PHY message buffer to fill in
- * @port: the port to access
- * @offset: the register offset
+ * @clk_freq: Clock frequency to program
+ * @clk_src: Clock source to select (TIME_REF, or TCX0)
+ *
+ * Configure the Clock Generation Unit with the desired clock frequency and
+ * time reference, enabling the PLL which drives the PTP hardware clock.
+ *
+ * Return:
+ * * %0 - success
+ * * %-EINVAL - input parameters are incorrect
+ * * %-EBUSY - failed to lock TS PLL
+ * * %other - CGU read/write failure
*/
-static void ice_fill_phy_msg_e82x(struct ice_hw *hw,
- struct ice_sbq_msg_input *msg, u8 port,
- u16 offset)
+static int ice_cfg_cgu_pll_e82x(struct ice_hw *hw,
+ enum ice_time_ref_freq clk_freq,
+ enum ice_clk_src clk_src)
{
- int phy_port, phy, quadtype;
-
- phy_port = port % hw->ptp.ports_per_phy;
- phy = port / hw->ptp.ports_per_phy;
- quadtype = ICE_GET_QUAD_NUM(port) %
- ICE_GET_QUAD_NUM(hw->ptp.ports_per_phy);
+ union tspll_ro_bwm_lf bwm_lf;
+ union nac_cgu_dword19 dw19;
+ union nac_cgu_dword22 dw22;
+ union nac_cgu_dword24 dw24;
+ union nac_cgu_dword9 dw9;
+ int err;
- if (quadtype == 0) {
- msg->msg_addr_low = P_Q0_L(P_0_BASE + offset, phy_port);
- msg->msg_addr_high = P_Q0_H(P_0_BASE + offset, phy_port);
- } else {
- msg->msg_addr_low = P_Q1_L(P_4_BASE + offset, phy_port);
- msg->msg_addr_high = P_Q1_H(P_4_BASE + offset, phy_port);
+ if (clk_freq >= NUM_ICE_TIME_REF_FREQ) {
+ dev_warn(ice_hw_to_dev(hw), "Invalid TIME_REF frequency %u\n",
+ clk_freq);
+ return -EINVAL;
}
- if (phy == 0)
- msg->dest_dev = rmn_0;
- else if (phy == 1)
- msg->dest_dev = rmn_1;
- else
- msg->dest_dev = rmn_2;
-}
-
-/**
- * ice_is_64b_phy_reg_e82x - Check if this is a 64bit PHY register
- * @low_addr: the low address to check
- * @high_addr: on return, contains the high address of the 64bit register
- *
- * Checks if the provided low address is one of the known 64bit PHY values
- * represented as two 32bit registers. If it is, return the appropriate high
- * register offset to use.
- */
-static bool ice_is_64b_phy_reg_e82x(u16 low_addr, u16 *high_addr)
-{
- switch (low_addr) {
- case P_REG_PAR_PCS_TX_OFFSET_L:
- *high_addr = P_REG_PAR_PCS_TX_OFFSET_U;
- return true;
- case P_REG_PAR_PCS_RX_OFFSET_L:
- *high_addr = P_REG_PAR_PCS_RX_OFFSET_U;
- return true;
- case P_REG_PAR_TX_TIME_L:
- *high_addr = P_REG_PAR_TX_TIME_U;
- return true;
- case P_REG_PAR_RX_TIME_L:
- *high_addr = P_REG_PAR_RX_TIME_U;
- return true;
- case P_REG_TOTAL_TX_OFFSET_L:
- *high_addr = P_REG_TOTAL_TX_OFFSET_U;
- return true;
- case P_REG_TOTAL_RX_OFFSET_L:
- *high_addr = P_REG_TOTAL_RX_OFFSET_U;
- return true;
- case P_REG_UIX66_10G_40G_L:
- *high_addr = P_REG_UIX66_10G_40G_U;
- return true;
- case P_REG_UIX66_25G_100G_L:
- *high_addr = P_REG_UIX66_25G_100G_U;
- return true;
- case P_REG_TX_CAPTURE_L:
- *high_addr = P_REG_TX_CAPTURE_U;
- return true;
- case P_REG_RX_CAPTURE_L:
- *high_addr = P_REG_RX_CAPTURE_U;
- return true;
- case P_REG_TX_TIMER_INC_PRE_L:
- *high_addr = P_REG_TX_TIMER_INC_PRE_U;
- return true;
- case P_REG_RX_TIMER_INC_PRE_L:
- *high_addr = P_REG_RX_TIMER_INC_PRE_U;
- return true;
- default:
- return false;
+ if (clk_src >= NUM_ICE_CLK_SRC) {
+ dev_warn(ice_hw_to_dev(hw), "Invalid clock source %u\n",
+ clk_src);
+ return -EINVAL;
}
-}
-/**
- * ice_is_40b_phy_reg_e82x - Check if this is a 40bit PHY register
- * @low_addr: the low address to check
- * @high_addr: on return, contains the high address of the 40bit value
- *
- * Checks if the provided low address is one of the known 40bit PHY values
- * split into two registers with the lower 8 bits in the low register and the
- * upper 32 bits in the high register. If it is, return the appropriate high
- * register offset to use.
- */
-static bool ice_is_40b_phy_reg_e82x(u16 low_addr, u16 *high_addr)
-{
- switch (low_addr) {
- case P_REG_TIMETUS_L:
- *high_addr = P_REG_TIMETUS_U;
- return true;
- case P_REG_PAR_RX_TUS_L:
- *high_addr = P_REG_PAR_RX_TUS_U;
- return true;
- case P_REG_PAR_TX_TUS_L:
- *high_addr = P_REG_PAR_TX_TUS_U;
- return true;
- case P_REG_PCS_RX_TUS_L:
- *high_addr = P_REG_PCS_RX_TUS_U;
- return true;
- case P_REG_PCS_TX_TUS_L:
- *high_addr = P_REG_PCS_TX_TUS_U;
- return true;
- case P_REG_DESK_PAR_RX_TUS_L:
- *high_addr = P_REG_DESK_PAR_RX_TUS_U;
- return true;
- case P_REG_DESK_PAR_TX_TUS_L:
- *high_addr = P_REG_DESK_PAR_TX_TUS_U;
- return true;
- case P_REG_DESK_PCS_RX_TUS_L:
- *high_addr = P_REG_DESK_PCS_RX_TUS_U;
- return true;
- case P_REG_DESK_PCS_TX_TUS_L:
- *high_addr = P_REG_DESK_PCS_TX_TUS_U;
- return true;
- default:
- return false;
+ if (clk_src == ICE_CLK_SRC_TCX0 &&
+ clk_freq != ICE_TIME_REF_FREQ_25_000) {
+ dev_warn(ice_hw_to_dev(hw),
+ "TCX0 only supports 25 MHz frequency\n");
+ return -EINVAL;
}
-}
-/**
- * ice_read_phy_reg_e82x - Read a PHY register
- * @hw: pointer to the HW struct
- * @port: PHY port to read from
- * @offset: PHY register offset to read
- * @val: on return, the contents read from the PHY
- *
- * Read a PHY register for the given port over the device sideband queue.
- */
-static int
-ice_read_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 offset, u32 *val)
-{
- struct ice_sbq_msg_input msg = {0};
- int err;
+ err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD9, &dw9.val);
+ if (err)
+ return err;
- ice_fill_phy_msg_e82x(hw, &msg, port, offset);
- msg.opcode = ice_sbq_msg_rd;
+ err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD24, &dw24.val);
+ if (err)
+ return err;
- err = ice_sbq_rw_reg(hw, &msg);
- if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
- err);
+ err = ice_read_cgu_reg_e82x(hw, TSPLL_RO_BWM_LF, &bwm_lf.val);
+ if (err)
return err;
+
+ /* Log the current clock configuration */
+ ice_debug(hw, ICE_DBG_PTP, "Current CGU configuration -- %s, clk_src %s, clk_freq %s, PLL %s\n",
+ dw24.field.ts_pll_enable ? "enabled" : "disabled",
+ ice_clk_src_str(dw24.field.time_ref_sel),
+ ice_clk_freq_str(dw9.field.time_ref_freq_sel),
+ bwm_lf.field.plllock_true_lock_cri ? "locked" : "unlocked");
+
+ /* Disable the PLL before changing the clock source or frequency */
+ if (dw24.field.ts_pll_enable) {
+ dw24.field.ts_pll_enable = 0;
+
+ err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD24, dw24.val);
+ if (err)
+ return err;
}
- *val = msg.data;
+ /* Set the frequency */
+ dw9.field.time_ref_freq_sel = clk_freq;
+ err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD9, dw9.val);
+ if (err)
+ return err;
- return 0;
-}
+ /* Configure the TS PLL feedback divisor */
+ err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD19, &dw19.val);
+ if (err)
+ return err;
-/**
- * ice_read_64b_phy_reg_e82x - Read a 64bit value from PHY registers
- * @hw: pointer to the HW struct
- * @port: PHY port to read from
- * @low_addr: offset of the lower register to read from
- * @val: on return, the contents of the 64bit value from the PHY registers
- *
- * Reads the two registers associated with a 64bit value and returns it in the
- * val pointer. The offset always specifies the lower register offset to use.
- * The high offset is looked up. This function only operates on registers
- * known to be two parts of a 64bit value.
- */
-static int
-ice_read_64b_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 low_addr, u64 *val)
-{
- u32 low, high;
- u16 high_addr;
- int err;
+ dw19.field.tspll_fbdiv_intgr = e822_cgu_params[clk_freq].feedback_div;
+ dw19.field.tspll_ndivratio = 1;
- /* Only operate on registers known to be split into two 32bit
- * registers.
- */
- if (!ice_is_64b_phy_reg_e82x(low_addr, &high_addr)) {
- ice_debug(hw, ICE_DBG_PTP, "Invalid 64b register addr 0x%08x\n",
- low_addr);
- return -EINVAL;
- }
+ err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD19, dw19.val);
+ if (err)
+ return err;
- err = ice_read_phy_reg_e82x(hw, port, low_addr, &low);
- if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to read from low register 0x%08x\n, err %d",
- low_addr, err);
+ /* Configure the TS PLL post divisor */
+ err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD22, &dw22.val);
+ if (err)
return err;
- }
- err = ice_read_phy_reg_e82x(hw, port, high_addr, &high);
- if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to read from high register 0x%08x\n, err %d",
- high_addr, err);
+ dw22.field.time1588clk_div = e822_cgu_params[clk_freq].post_pll_div;
+ dw22.field.time1588clk_sel_div2 = 0;
+
+ err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD22, dw22.val);
+ if (err)
+ return err;
+
+ /* Configure the TS PLL pre divisor and clock source */
+ err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD24, &dw24.val);
+ if (err)
+ return err;
+
+ dw24.field.ref1588_ck_div = e822_cgu_params[clk_freq].refclk_pre_div;
+ dw24.field.tspll_fbdiv_frac = e822_cgu_params[clk_freq].frac_n_div;
+ dw24.field.time_ref_sel = clk_src;
+
+ err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD24, dw24.val);
+ if (err)
return err;
+
+ /* Finally, enable the PLL */
+ dw24.field.ts_pll_enable = 1;
+
+ err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD24, dw24.val);
+ if (err)
+ return err;
+
+ /* Wait to verify if the PLL locks */
+ usleep_range(1000, 5000);
+
+ err = ice_read_cgu_reg_e82x(hw, TSPLL_RO_BWM_LF, &bwm_lf.val);
+ if (err)
+ return err;
+
+ if (!bwm_lf.field.plllock_true_lock_cri) {
+ dev_warn(ice_hw_to_dev(hw), "CGU PLL failed to lock\n");
+ return -EBUSY;
}
- *val = (u64)high << 32 | low;
+ /* Log the current clock configuration */
+ ice_debug(hw, ICE_DBG_PTP, "New CGU configuration -- %s, clk_src %s, clk_freq %s, PLL %s\n",
+ dw24.field.ts_pll_enable ? "enabled" : "disabled",
+ ice_clk_src_str(dw24.field.time_ref_sel),
+ ice_clk_freq_str(dw9.field.time_ref_freq_sel),
+ bwm_lf.field.plllock_true_lock_cri ? "locked" : "unlocked");
return 0;
}
/**
- * ice_write_phy_reg_e82x - Write a PHY register
- * @hw: pointer to the HW struct
- * @port: PHY port to write to
- * @offset: PHY register offset to write
- * @val: The value to write to the register
+ * ice_init_cgu_e82x - Initialize CGU with settings from firmware
+ * @hw: pointer to the HW structure
*
- * Write a PHY register for the given port over the device sideband queue.
+ * Initialize the Clock Generation Unit of the E822 device.
+ *
+ * Return: 0 on success, other error codes when failed to read/write/cfg CGU
*/
-static int
-ice_write_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 offset, u32 val)
+static int ice_init_cgu_e82x(struct ice_hw *hw)
{
- struct ice_sbq_msg_input msg = {0};
+ struct ice_ts_func_info *ts_info = &hw->func_caps.ts_func_info;
+ union tspll_cntr_bist_settings cntr_bist;
int err;
- ice_fill_phy_msg_e82x(hw, &msg, port, offset);
- msg.opcode = ice_sbq_msg_wr;
- msg.data = val;
+ err = ice_read_cgu_reg_e82x(hw, TSPLL_CNTR_BIST_SETTINGS,
+ &cntr_bist.val);
+ if (err)
+ return err;
- err = ice_sbq_rw_reg(hw, &msg);
- if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
- err);
+ /* Disable sticky lock detection so lock err reported is accurate */
+ cntr_bist.field.i_plllock_sel_0 = 0;
+ cntr_bist.field.i_plllock_sel_1 = 0;
+
+ err = ice_write_cgu_reg_e82x(hw, TSPLL_CNTR_BIST_SETTINGS,
+ cntr_bist.val);
+ if (err)
return err;
+
+ /* Configure the CGU PLL using the parameters from the function
+ * capabilities.
+ */
+ return ice_cfg_cgu_pll_e82x(hw, ts_info->time_ref,
+ (enum ice_clk_src)ts_info->clk_src);
+}
+
+/**
+ * ice_ptp_tmr_cmd_to_src_reg - Convert to source timer command value
+ * @hw: pointer to HW struct
+ * @cmd: Timer command
+ *
+ * Return: 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, 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:
+ dev_warn(ice_hw_to_dev(hw),
+ "Ignoring unrecognized timer command %u\n", cmd);
+ cmd_val = 0;
}
- return 0;
+ tmr_idx = ice_get_ptp_src_clock_index(hw);
+
+ return tmr_idx << SEL_CPK_SRC | cmd_val;
}
/**
- * ice_write_40b_phy_reg_e82x - Write a 40b value to the PHY
- * @hw: pointer to the HW struct
- * @port: port to write to
- * @low_addr: offset of the low register
- * @val: 40b value to write
+ * ice_ptp_tmr_cmd_to_port_reg- Convert to port timer command value
+ * @hw: pointer to HW struct
+ * @cmd: Timer command
*
- * Write the provided 40b value to the two associated registers by splitting
- * it up into two chunks, the lower 8 bits and the upper 32 bits.
+ * 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.
+ *
+ * Return: the PHY port timer command register value for the given PTP timer
+ * command.
*/
-static int
-ice_write_40b_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 low_addr, u64 val)
+static u32 ice_ptp_tmr_cmd_to_port_reg(struct ice_hw *hw,
+ enum ice_ptp_tmr_cmd cmd)
{
- u32 low, high;
- u16 high_addr;
- int err;
+ u32 cmd_val, tmr_idx;
- /* Only operate on registers known to be split into a lower 8 bit
- * register and an upper 32 bit register.
+ /* Certain hardware families share the same register values for the
+ * port register and source timer register.
*/
- if (!ice_is_40b_phy_reg_e82x(low_addr, &high_addr)) {
- ice_debug(hw, ICE_DBG_PTP, "Invalid 40b register addr 0x%08x\n",
- low_addr);
- return -EINVAL;
+ switch (hw->ptp.phy_model) {
+ case ICE_PHY_E810:
+ return ice_ptp_tmr_cmd_to_src_reg(hw, cmd) & TS_CMD_MASK_E810;
+ default:
+ break;
}
- low = (u32)(val & P_REG_40B_LOW_M);
- high = (u32)(val >> P_REG_40B_HIGH_S);
-
- err = ice_write_phy_reg_e82x(hw, port, low_addr, low);
- if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to write to low register 0x%08x\n, err %d",
- low_addr, err);
- return err;
+ 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:
+ dev_warn(ice_hw_to_dev(hw),
+ "Ignoring unrecognized timer command %u\n", cmd);
+ cmd_val = 0;
}
- err = ice_write_phy_reg_e82x(hw, port, high_addr, high);
- if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to write to high register 0x%08x\n, err %d",
- high_addr, err);
- return err;
- }
+ tmr_idx = ice_get_ptp_src_clock_index(hw);
- return 0;
+ return tmr_idx << SEL_PHY_SRC | cmd_val;
}
/**
- * ice_write_64b_phy_reg_e82x - Write a 64bit value to PHY registers
- * @hw: pointer to the HW struct
- * @port: PHY port to read from
- * @low_addr: offset of the lower register to read from
- * @val: the contents of the 64bit value to write to PHY
+ * ice_ptp_src_cmd - Prepare source timer for a timer command
+ * @hw: pointer to HW structure
+ * @cmd: Timer command
*
- * Write the 64bit value to the two associated 32bit PHY registers. The offset
- * is always specified as the lower register, and the high address is looked
- * up. This function only operates on registers known to be two parts of
- * a 64bit value.
+ * Prepare the source timer for an upcoming timer sync command.
*/
-static int
-ice_write_64b_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 low_addr, u64 val)
+void ice_ptp_src_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd)
{
- u32 low, high;
- u16 high_addr;
- int err;
-
- /* Only operate on registers known to be split into two 32bit
- * registers.
- */
- if (!ice_is_64b_phy_reg_e82x(low_addr, &high_addr)) {
- ice_debug(hw, ICE_DBG_PTP, "Invalid 64b register addr 0x%08x\n",
- low_addr);
- return -EINVAL;
- }
-
- low = lower_32_bits(val);
- high = upper_32_bits(val);
+ u32 cmd_val = ice_ptp_tmr_cmd_to_src_reg(hw, cmd);
- err = ice_write_phy_reg_e82x(hw, port, low_addr, low);
- if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to write to low register 0x%08x\n, err %d",
- low_addr, err);
- return err;
- }
+ wr32(hw, GLTSYN_CMD, cmd_val);
+}
- err = ice_write_phy_reg_e82x(hw, port, high_addr, high);
- if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to write to high register 0x%08x\n, err %d",
- high_addr, err);
- return err;
- }
+/**
+ * ice_ptp_exec_tmr_cmd - Execute all prepared timer commands
+ * @hw: pointer to HW struct
+ *
+ * Write the SYNC_EXEC_CMD bit to the GLTSYN_CMD_SYNC register, and flush the
+ * write immediately. This triggers the hardware to begin executing all of the
+ * source and PHY timer commands synchronously.
+ */
+static void ice_ptp_exec_tmr_cmd(struct ice_hw *hw)
+{
+ struct ice_pf *pf = container_of(hw, struct ice_pf, hw);
- return 0;
+ guard(spinlock)(&pf->adapter->ptp_gltsyn_time_lock);
+ wr32(hw, GLTSYN_CMD_SYNC, SYNC_EXEC_CMD);
+ ice_flush(hw);
}
+/* E822 family functions
+ *
+ * The following functions operate on the E822 family of devices.
+ */
+
/**
- * ice_fill_quad_msg_e82x - Fill message data for quad register access
+ * ice_fill_phy_msg_e82x - Fill message data for a PHY register access
* @hw: pointer to the HW struct
* @msg: the PHY message buffer to fill in
- * @quad: the quad to access
+ * @port: the port to access
* @offset: the register offset
- *
- * Fill a message buffer for accessing a register in a quad shared between
- * multiple PHYs.
- *
- * Return:
- * * %0 - OK
- * * %-EINVAL - invalid quad number
*/
-static int ice_fill_quad_msg_e82x(struct ice_hw *hw,
- struct ice_sbq_msg_input *msg, u8 quad,
+static void ice_fill_phy_msg_e82x(struct ice_hw *hw,
+ struct ice_sbq_msg_input *msg, u8 port,
u16 offset)
{
- u32 addr;
+ int phy_port, phy, quadtype;
- if (quad >= ICE_GET_QUAD_NUM(hw->ptp.num_lports))
- return -EINVAL;
+ phy_port = port % hw->ptp.ports_per_phy;
+ phy = port / hw->ptp.ports_per_phy;
+ quadtype = ICE_GET_QUAD_NUM(port) %
+ ICE_GET_QUAD_NUM(hw->ptp.ports_per_phy);
- msg->dest_dev = rmn_0;
+ if (quadtype == 0) {
+ msg->msg_addr_low = P_Q0_L(P_0_BASE + offset, phy_port);
+ msg->msg_addr_high = P_Q0_H(P_0_BASE + offset, phy_port);
+ } else {
+ msg->msg_addr_low = P_Q1_L(P_4_BASE + offset, phy_port);
+ msg->msg_addr_high = P_Q1_H(P_4_BASE + offset, phy_port);
+ }
- if (!(quad % ICE_GET_QUAD_NUM(hw->ptp.ports_per_phy)))
- addr = Q_0_BASE + offset;
+ if (phy == 0)
+ msg->dest_dev = rmn_0;
+ else if (phy == 1)
+ msg->dest_dev = rmn_1;
else
- addr = Q_1_BASE + offset;
-
- msg->msg_addr_low = lower_16_bits(addr);
- msg->msg_addr_high = upper_16_bits(addr);
-
- return 0;
+ msg->dest_dev = rmn_2;
}
/**
- * ice_read_quad_reg_e82x - Read a PHY quad register
- * @hw: pointer to the HW struct
- * @quad: quad to read from
- * @offset: quad register offset to read
- * @val: on return, the contents read from the quad
+ * ice_is_64b_phy_reg_e82x - Check if this is a 64bit PHY register
+ * @low_addr: the low address to check
+ * @high_addr: on return, contains the high address of the 64bit register
*
- * Read a quad register over the device sideband queue. Quad registers are
- * shared between multiple PHYs.
+ * Checks if the provided low address is one of the known 64bit PHY values
+ * represented as two 32bit registers. If it is, return the appropriate high
+ * register offset to use.
*/
-int
-ice_read_quad_reg_e82x(struct ice_hw *hw, u8 quad, u16 offset, u32 *val)
+static bool ice_is_64b_phy_reg_e82x(u16 low_addr, u16 *high_addr)
{
- struct ice_sbq_msg_input msg = {0};
- int err;
-
- err = ice_fill_quad_msg_e82x(hw, &msg, quad, offset);
- if (err)
- return err;
-
- msg.opcode = ice_sbq_msg_rd;
-
- err = ice_sbq_rw_reg(hw, &msg);
- if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
- err);
- return err;
+ switch (low_addr) {
+ case P_REG_PAR_PCS_TX_OFFSET_L:
+ *high_addr = P_REG_PAR_PCS_TX_OFFSET_U;
+ return true;
+ case P_REG_PAR_PCS_RX_OFFSET_L:
+ *high_addr = P_REG_PAR_PCS_RX_OFFSET_U;
+ return true;
+ case P_REG_PAR_TX_TIME_L:
+ *high_addr = P_REG_PAR_TX_TIME_U;
+ return true;
+ case P_REG_PAR_RX_TIME_L:
+ *high_addr = P_REG_PAR_RX_TIME_U;
+ return true;
+ case P_REG_TOTAL_TX_OFFSET_L:
+ *high_addr = P_REG_TOTAL_TX_OFFSET_U;
+ return true;
+ case P_REG_TOTAL_RX_OFFSET_L:
+ *high_addr = P_REG_TOTAL_RX_OFFSET_U;
+ return true;
+ case P_REG_UIX66_10G_40G_L:
+ *high_addr = P_REG_UIX66_10G_40G_U;
+ return true;
+ case P_REG_UIX66_25G_100G_L:
+ *high_addr = P_REG_UIX66_25G_100G_U;
+ return true;
+ case P_REG_TX_CAPTURE_L:
+ *high_addr = P_REG_TX_CAPTURE_U;
+ return true;
+ case P_REG_RX_CAPTURE_L:
+ *high_addr = P_REG_RX_CAPTURE_U;
+ return true;
+ case P_REG_TX_TIMER_INC_PRE_L:
+ *high_addr = P_REG_TX_TIMER_INC_PRE_U;
+ return true;
+ case P_REG_RX_TIMER_INC_PRE_L:
+ *high_addr = P_REG_RX_TIMER_INC_PRE_U;
+ return true;
+ default:
+ return false;
}
+}
- *val = msg.data;
-
- return 0;
+/**
+ * ice_is_40b_phy_reg_e82x - Check if this is a 40bit PHY register
+ * @low_addr: the low address to check
+ * @high_addr: on return, contains the high address of the 40bit value
+ *
+ * Checks if the provided low address is one of the known 40bit PHY values
+ * split into two registers with the lower 8 bits in the low register and the
+ * upper 32 bits in the high register. If it is, return the appropriate high
+ * register offset to use.
+ */
+static bool ice_is_40b_phy_reg_e82x(u16 low_addr, u16 *high_addr)
+{
+ switch (low_addr) {
+ case P_REG_TIMETUS_L:
+ *high_addr = P_REG_TIMETUS_U;
+ return true;
+ case P_REG_PAR_RX_TUS_L:
+ *high_addr = P_REG_PAR_RX_TUS_U;
+ return true;
+ case P_REG_PAR_TX_TUS_L:
+ *high_addr = P_REG_PAR_TX_TUS_U;
+ return true;
+ case P_REG_PCS_RX_TUS_L:
+ *high_addr = P_REG_PCS_RX_TUS_U;
+ return true;
+ case P_REG_PCS_TX_TUS_L:
+ *high_addr = P_REG_PCS_TX_TUS_U;
+ return true;
+ case P_REG_DESK_PAR_RX_TUS_L:
+ *high_addr = P_REG_DESK_PAR_RX_TUS_U;
+ return true;
+ case P_REG_DESK_PAR_TX_TUS_L:
+ *high_addr = P_REG_DESK_PAR_TX_TUS_U;
+ return true;
+ case P_REG_DESK_PCS_RX_TUS_L:
+ *high_addr = P_REG_DESK_PCS_RX_TUS_U;
+ return true;
+ case P_REG_DESK_PCS_TX_TUS_L:
+ *high_addr = P_REG_DESK_PCS_TX_TUS_U;
+ return true;
+ default:
+ return false;
+ }
}
/**
- * ice_write_quad_reg_e82x - Write a PHY quad register
+ * ice_read_phy_reg_e82x - Read a PHY register
* @hw: pointer to the HW struct
- * @quad: quad to write to
- * @offset: quad register offset to write
- * @val: The value to write to the register
+ * @port: PHY port to read from
+ * @offset: PHY register offset to read
+ * @val: on return, the contents read from the PHY
*
- * Write a quad register over the device sideband queue. Quad registers are
- * shared between multiple PHYs.
+ * Read a PHY register for the given port over the device sideband queue.
*/
-int
-ice_write_quad_reg_e82x(struct ice_hw *hw, u8 quad, u16 offset, u32 val)
+static int
+ice_read_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 offset, u32 *val)
{
struct ice_sbq_msg_input msg = {0};
int err;
- err = ice_fill_quad_msg_e82x(hw, &msg, quad, offset);
- if (err)
- return err;
-
- msg.opcode = ice_sbq_msg_wr;
- msg.data = val;
+ ice_fill_phy_msg_e82x(hw, &msg, port, offset);
+ msg.opcode = ice_sbq_msg_rd;
err = ice_sbq_rw_reg(hw, &msg);
if (err) {
@@ -792,85 +804,81 @@ ice_write_quad_reg_e82x(struct ice_hw *hw, u8 quad, u16 offset, u32 val)
return err;
}
+ *val = msg.data;
+
return 0;
}
/**
- * ice_read_phy_tstamp_e82x - Read a PHY timestamp out of the quad block
+ * ice_read_64b_phy_reg_e82x - Read a 64bit value from PHY registers
* @hw: pointer to the HW struct
- * @quad: the quad to read from
- * @idx: the timestamp index to read
- * @tstamp: on return, the 40bit timestamp value
+ * @port: PHY port to read from
+ * @low_addr: offset of the lower register to read from
+ * @val: on return, the contents of the 64bit value from the PHY registers
*
- * Read a 40bit timestamp value out of the two associated registers in the
- * quad memory block that is shared between the internal PHYs of the E822
- * family of devices.
+ * Reads the two registers associated with a 64bit value and returns it in the
+ * val pointer. The offset always specifies the lower register offset to use.
+ * The high offset is looked up. This function only operates on registers
+ * known to be two parts of a 64bit value.
*/
static int
-ice_read_phy_tstamp_e82x(struct ice_hw *hw, u8 quad, u8 idx, u64 *tstamp)
+ice_read_64b_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 low_addr, u64 *val)
{
- u16 lo_addr, hi_addr;
- u32 lo, hi;
+ u32 low, high;
+ u16 high_addr;
int err;
- lo_addr = (u16)TS_L(Q_REG_TX_MEMORY_BANK_START, idx);
- hi_addr = (u16)TS_H(Q_REG_TX_MEMORY_BANK_START, idx);
+ /* Only operate on registers known to be split into two 32bit
+ * registers.
+ */
+ if (!ice_is_64b_phy_reg_e82x(low_addr, &high_addr)) {
+ ice_debug(hw, ICE_DBG_PTP, "Invalid 64b register addr 0x%08x\n",
+ low_addr);
+ return -EINVAL;
+ }
- err = ice_read_quad_reg_e82x(hw, quad, lo_addr, &lo);
+ err = ice_read_phy_reg_e82x(hw, port, low_addr, &low);
if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to read low PTP timestamp register, err %d\n",
- err);
+ ice_debug(hw, ICE_DBG_PTP, "Failed to read from low register 0x%08x\n, err %d",
+ low_addr, err);
return err;
}
- err = ice_read_quad_reg_e82x(hw, quad, hi_addr, &hi);
+ err = ice_read_phy_reg_e82x(hw, port, high_addr, &high);
if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to read high PTP timestamp register, err %d\n",
- err);
+ ice_debug(hw, ICE_DBG_PTP, "Failed to read from high register 0x%08x\n, err %d",
+ high_addr, err);
return err;
}
- /* For E822 based internal PHYs, the timestamp is reported with the
- * lower 8 bits in the low register, and the upper 32 bits in the high
- * register.
- */
- *tstamp = ((u64)hi) << TS_PHY_HIGH_S | ((u64)lo & TS_PHY_LOW_M);
+ *val = (u64)high << 32 | low;
return 0;
}
/**
- * ice_clear_phy_tstamp_e82x - Clear a timestamp from the quad block
+ * ice_write_phy_reg_e82x - Write a PHY register
* @hw: pointer to the HW struct
- * @quad: the quad to read from
- * @idx: the timestamp index to reset
- *
- * Read the timestamp out of the quad to clear its timestamp status bit from
- * the PHY quad block that is shared between the internal PHYs of the E822
- * devices.
- *
- * Note that unlike E810, software cannot directly write to the quad memory
- * bank registers. E822 relies on the ice_get_phy_tx_tstamp_ready() function
- * to determine which timestamps are valid. Reading a timestamp auto-clears
- * the valid bit.
- *
- * To directly clear the contents of the timestamp block entirely, discarding
- * all timestamp data at once, software should instead use
- * ice_ptp_reset_ts_memory_quad_e82x().
+ * @port: PHY port to write to
+ * @offset: PHY register offset to write
+ * @val: The value to write to the register
*
- * This function should only be called on an idx whose bit is set according to
- * ice_get_phy_tx_tstamp_ready().
+ * Write a PHY register for the given port over the device sideband queue.
*/
static int
-ice_clear_phy_tstamp_e82x(struct ice_hw *hw, u8 quad, u8 idx)
+ice_write_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 offset, u32 val)
{
- u64 unused_tstamp;
+ struct ice_sbq_msg_input msg = {0};
int err;
- err = ice_read_phy_tstamp_e82x(hw, quad, idx, &unused_tstamp);
+ ice_fill_phy_msg_e82x(hw, &msg, port, offset);
+ msg.opcode = ice_sbq_msg_wr;
+ msg.data = val;
+
+ err = ice_sbq_rw_reg(hw, &msg);
if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to read the timestamp register for quad %u, idx %u, err %d\n",
- quad, idx, err);
+ ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
+ err);
return err;
}
@@ -878,312 +886,307 @@ ice_clear_phy_tstamp_e82x(struct ice_hw *hw, u8 quad, u8 idx)
}
/**
- * ice_ptp_reset_ts_memory_quad_e82x - Clear all timestamps from the quad block
- * @hw: pointer to the HW struct
- * @quad: the quad to read from
- *
- * Clear all timestamps from the PHY quad block that is shared between the
- * internal PHYs on the E822 devices.
- */
-void ice_ptp_reset_ts_memory_quad_e82x(struct ice_hw *hw, u8 quad)
-{
- ice_write_quad_reg_e82x(hw, quad, Q_REG_TS_CTRL, Q_REG_TS_CTRL_M);
- ice_write_quad_reg_e82x(hw, quad, Q_REG_TS_CTRL, ~(u32)Q_REG_TS_CTRL_M);
-}
-
-/**
- * ice_ptp_reset_ts_memory_e82x - Clear all timestamps from all quad blocks
- * @hw: pointer to the HW struct
- */
-static void ice_ptp_reset_ts_memory_e82x(struct ice_hw *hw)
-{
- unsigned int quad;
-
- for (quad = 0; quad < ICE_GET_QUAD_NUM(hw->ptp.num_lports); quad++)
- ice_ptp_reset_ts_memory_quad_e82x(hw, quad);
-}
-
-/**
- * ice_read_cgu_reg_e82x - Read a CGU register
+ * ice_write_40b_phy_reg_e82x - Write a 40b value to the PHY
* @hw: pointer to the HW struct
- * @addr: Register address to read
- * @val: storage for register value read
+ * @port: port to write to
+ * @low_addr: offset of the low register
+ * @val: 40b value to write
*
- * Read the contents of a register of the Clock Generation Unit. Only
- * applicable to E822 devices.
+ * Write the provided 40b value to the two associated registers by splitting
+ * it up into two chunks, the lower 8 bits and the upper 32 bits.
*/
static int
-ice_read_cgu_reg_e82x(struct ice_hw *hw, u32 addr, u32 *val)
+ice_write_40b_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 low_addr, u64 val)
{
- struct ice_sbq_msg_input cgu_msg;
+ u32 low, high;
+ u16 high_addr;
int err;
- cgu_msg.opcode = ice_sbq_msg_rd;
- cgu_msg.dest_dev = cgu;
- cgu_msg.msg_addr_low = addr;
- cgu_msg.msg_addr_high = 0x0;
+ /* Only operate on registers known to be split into a lower 8 bit
+ * register and an upper 32 bit register.
+ */
+ if (!ice_is_40b_phy_reg_e82x(low_addr, &high_addr)) {
+ ice_debug(hw, ICE_DBG_PTP, "Invalid 40b register addr 0x%08x\n",
+ low_addr);
+ return -EINVAL;
+ }
+ low = FIELD_GET(P_REG_40B_LOW_M, val);
+ high = (u32)(val >> P_REG_40B_HIGH_S);
- err = ice_sbq_rw_reg(hw, &cgu_msg);
+ err = ice_write_phy_reg_e82x(hw, port, low_addr, low);
if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to read CGU register 0x%04x, err %d\n",
- addr, err);
+ ice_debug(hw, ICE_DBG_PTP, "Failed to write to low register 0x%08x\n, err %d",
+ low_addr, err);
return err;
}
- *val = cgu_msg.data;
+ err = ice_write_phy_reg_e82x(hw, port, high_addr, high);
+ if (err) {
+ ice_debug(hw, ICE_DBG_PTP, "Failed to write to high register 0x%08x\n, err %d",
+ high_addr, err);
+ return err;
+ }
- return err;
+ return 0;
}
/**
- * ice_write_cgu_reg_e82x - Write a CGU register
+ * ice_write_64b_phy_reg_e82x - Write a 64bit value to PHY registers
* @hw: pointer to the HW struct
- * @addr: Register address to write
- * @val: value to write into the register
+ * @port: PHY port to read from
+ * @low_addr: offset of the lower register to read from
+ * @val: the contents of the 64bit value to write to PHY
*
- * Write the specified value to a register of the Clock Generation Unit. Only
- * applicable to E822 devices.
+ * Write the 64bit value to the two associated 32bit PHY registers. The offset
+ * is always specified as the lower register, and the high address is looked
+ * up. This function only operates on registers known to be two parts of
+ * a 64bit value.
*/
static int
-ice_write_cgu_reg_e82x(struct ice_hw *hw, u32 addr, u32 val)
+ice_write_64b_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 low_addr, u64 val)
{
- struct ice_sbq_msg_input cgu_msg;
+ u32 low, high;
+ u16 high_addr;
int err;
- cgu_msg.opcode = ice_sbq_msg_wr;
- cgu_msg.dest_dev = cgu;
- cgu_msg.msg_addr_low = addr;
- cgu_msg.msg_addr_high = 0x0;
- cgu_msg.data = val;
+ /* Only operate on registers known to be split into two 32bit
+ * registers.
+ */
+ if (!ice_is_64b_phy_reg_e82x(low_addr, &high_addr)) {
+ ice_debug(hw, ICE_DBG_PTP, "Invalid 64b register addr 0x%08x\n",
+ low_addr);
+ return -EINVAL;
+ }
+
+ low = lower_32_bits(val);
+ high = upper_32_bits(val);
- err = ice_sbq_rw_reg(hw, &cgu_msg);
+ err = ice_write_phy_reg_e82x(hw, port, low_addr, low);
if (err) {
- ice_debug(hw, ICE_DBG_PTP, "Failed to write CGU register 0x%04x, err %d\n",
- addr, err);
+ ice_debug(hw, ICE_DBG_PTP, "Failed to write to low register 0x%08x\n, err %d",
+ low_addr, err);
return err;
}
- return err;
-}
-
-/**
- * ice_clk_freq_str - Convert time_ref_freq to string
- * @clk_freq: Clock frequency
- *
- * Convert the specified TIME_REF clock frequency to a string.
- */
-static const char *ice_clk_freq_str(u8 clk_freq)
-{
- switch ((enum ice_time_ref_freq)clk_freq) {
- case ICE_TIME_REF_FREQ_25_000:
- return "25 MHz";
- case ICE_TIME_REF_FREQ_122_880:
- return "122.88 MHz";
- case ICE_TIME_REF_FREQ_125_000:
- return "125 MHz";
- case ICE_TIME_REF_FREQ_153_600:
- return "153.6 MHz";
- case ICE_TIME_REF_FREQ_156_250:
- return "156.25 MHz";
- case ICE_TIME_REF_FREQ_245_760:
- return "245.76 MHz";
- default:
- return "Unknown";
+ err = ice_write_phy_reg_e82x(hw, port, high_addr, high);
+ if (err) {
+ ice_debug(hw, ICE_DBG_PTP, "Failed to write to high register 0x%08x\n, err %d",
+ high_addr, err);
+ return err;
}
+
+ return 0;
}
/**
- * ice_clk_src_str - Convert time_ref_src to string
- * @clk_src: Clock source
+ * ice_fill_quad_msg_e82x - Fill message data for quad register access
+ * @hw: pointer to the HW struct
+ * @msg: the PHY message buffer to fill in
+ * @quad: the quad to access
+ * @offset: the register offset
+ *
+ * Fill a message buffer for accessing a register in a quad shared between
+ * multiple PHYs.
*
- * Convert the specified clock source to its string name.
+ * Return:
+ * * %0 - OK
+ * * %-EINVAL - invalid quad number
*/
-static const char *ice_clk_src_str(u8 clk_src)
+static int ice_fill_quad_msg_e82x(struct ice_hw *hw,
+ struct ice_sbq_msg_input *msg, u8 quad,
+ u16 offset)
{
- switch ((enum ice_clk_src)clk_src) {
- case ICE_CLK_SRC_TCX0:
- return "TCX0";
- case ICE_CLK_SRC_TIME_REF:
- return "TIME_REF";
- default:
- return "Unknown";
- }
+ u32 addr;
+
+ if (quad >= ICE_GET_QUAD_NUM(hw->ptp.num_lports))
+ return -EINVAL;
+
+ msg->dest_dev = rmn_0;
+
+ if (!(quad % ICE_GET_QUAD_NUM(hw->ptp.ports_per_phy)))
+ addr = Q_0_BASE + offset;
+ else
+ addr = Q_1_BASE + offset;
+
+ msg->msg_addr_low = lower_16_bits(addr);
+ msg->msg_addr_high = upper_16_bits(addr);
+
+ return 0;
}
/**
- * ice_cfg_cgu_pll_e82x - Configure the Clock Generation Unit
+ * ice_read_quad_reg_e82x - Read a PHY quad register
* @hw: pointer to the HW struct
- * @clk_freq: Clock frequency to program
- * @clk_src: Clock source to select (TIME_REF, or TCX0)
+ * @quad: quad to read from
+ * @offset: quad register offset to read
+ * @val: on return, the contents read from the quad
*
- * Configure the Clock Generation Unit with the desired clock frequency and
- * time reference, enabling the PLL which drives the PTP hardware clock.
+ * Read a quad register over the device sideband queue. Quad registers are
+ * shared between multiple PHYs.
*/
-static int
-ice_cfg_cgu_pll_e82x(struct ice_hw *hw, enum ice_time_ref_freq clk_freq,
- enum ice_clk_src clk_src)
+int
+ice_read_quad_reg_e82x(struct ice_hw *hw, u8 quad, u16 offset, u32 *val)
{
- union tspll_ro_bwm_lf bwm_lf;
- union nac_cgu_dword19 dw19;
- union nac_cgu_dword22 dw22;
- union nac_cgu_dword24 dw24;
- union nac_cgu_dword9 dw9;
+ struct ice_sbq_msg_input msg = {0};
int err;
- if (clk_freq >= NUM_ICE_TIME_REF_FREQ) {
- dev_warn(ice_hw_to_dev(hw), "Invalid TIME_REF frequency %u\n",
- clk_freq);
- return -EINVAL;
- }
-
- if (clk_src >= NUM_ICE_CLK_SRC) {
- dev_warn(ice_hw_to_dev(hw), "Invalid clock source %u\n",
- clk_src);
- return -EINVAL;
- }
-
- if (clk_src == ICE_CLK_SRC_TCX0 &&
- clk_freq != ICE_TIME_REF_FREQ_25_000) {
- dev_warn(ice_hw_to_dev(hw),
- "TCX0 only supports 25 MHz frequency\n");
- return -EINVAL;
- }
-
- err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD9, &dw9.val);
+ err = ice_fill_quad_msg_e82x(hw, &msg, quad, offset);
if (err)
return err;
- err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD24, &dw24.val);
- if (err)
- return err;
+ msg.opcode = ice_sbq_msg_rd;
- err = ice_read_cgu_reg_e82x(hw, TSPLL_RO_BWM_LF, &bwm_lf.val);
- if (err)
+ err = ice_sbq_rw_reg(hw, &msg);
+ if (err) {
+ ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
+ err);
return err;
-
- /* Log the current clock configuration */
- ice_debug(hw, ICE_DBG_PTP, "Current CGU configuration -- %s, clk_src %s, clk_freq %s, PLL %s\n",
- dw24.field.ts_pll_enable ? "enabled" : "disabled",
- ice_clk_src_str(dw24.field.time_ref_sel),
- ice_clk_freq_str(dw9.field.time_ref_freq_sel),
- bwm_lf.field.plllock_true_lock_cri ? "locked" : "unlocked");
-
- /* Disable the PLL before changing the clock source or frequency */
- if (dw24.field.ts_pll_enable) {
- dw24.field.ts_pll_enable = 0;
-
- err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD24, dw24.val);
- if (err)
- return err;
}
- /* Set the frequency */
- dw9.field.time_ref_freq_sel = clk_freq;
- err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD9, dw9.val);
- if (err)
- return err;
-
- /* Configure the TS PLL feedback divisor */
- err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD19, &dw19.val);
- if (err)
- return err;
+ *val = msg.data;
- dw19.field.tspll_fbdiv_intgr = e822_cgu_params[clk_freq].feedback_div;
- dw19.field.tspll_ndivratio = 1;
+ return 0;
+}
- err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD19, dw19.val);
- if (err)
- return err;
+/**
+ * ice_write_quad_reg_e82x - Write a PHY quad register
+ * @hw: pointer to the HW struct
+ * @quad: quad to write to
+ * @offset: quad register offset to write
+ * @val: The value to write to the register
+ *
+ * Write a quad register over the device sideband queue. Quad registers are
+ * shared between multiple PHYs.
+ */
+int
+ice_write_quad_reg_e82x(struct ice_hw *hw, u8 quad, u16 offset, u32 val)
+{
+ struct ice_sbq_msg_input msg = {0};
+ int err;
- /* Configure the TS PLL post divisor */
- err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD22, &dw22.val);
+ err = ice_fill_quad_msg_e82x(hw, &msg, quad, offset);
if (err)
return err;
- dw22.field.time1588clk_div = e822_cgu_params[clk_freq].post_pll_div;
- dw22.field.time1588clk_sel_div2 = 0;
-
- err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD22, dw22.val);
- if (err)
- return err;
+ msg.opcode = ice_sbq_msg_wr;
+ msg.data = val;
- /* Configure the TS PLL pre divisor and clock source */
- err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD24, &dw24.val);
- if (err)
+ err = ice_sbq_rw_reg(hw, &msg);
+ if (err) {
+ ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
+ err);
return err;
+ }
- dw24.field.ref1588_ck_div = e822_cgu_params[clk_freq].refclk_pre_div;
- dw24.field.tspll_fbdiv_frac = e822_cgu_params[clk_freq].frac_n_div;
- dw24.field.time_ref_sel = clk_src;
+ return 0;
+}
- err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD24, dw24.val);
- if (err)
- return err;
+/**
+ * ice_read_phy_tstamp_e82x - Read a PHY timestamp out of the quad block
+ * @hw: pointer to the HW struct
+ * @quad: the quad to read from
+ * @idx: the timestamp index to read
+ * @tstamp: on return, the 40bit timestamp value
+ *
+ * Read a 40bit timestamp value out of the two associated registers in the
+ * quad memory block that is shared between the internal PHYs of the E822
+ * family of devices.
+ */
+static int
+ice_read_phy_tstamp_e82x(struct ice_hw *hw, u8 quad, u8 idx, u64 *tstamp)
+{
+ u16 lo_addr, hi_addr;
+ u32 lo, hi;
+ int err;
- /* Finally, enable the PLL */
- dw24.field.ts_pll_enable = 1;
+ lo_addr = (u16)TS_L(Q_REG_TX_MEMORY_BANK_START, idx);
+ hi_addr = (u16)TS_H(Q_REG_TX_MEMORY_BANK_START, idx);
- err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD24, dw24.val);
- if (err)
+ err = ice_read_quad_reg_e82x(hw, quad, lo_addr, &lo);
+ if (err) {
+ ice_debug(hw, ICE_DBG_PTP, "Failed to read low PTP timestamp register, err %d\n",
+ err);
return err;
+ }
- /* Wait to verify if the PLL locks */
- usleep_range(1000, 5000);
-
- err = ice_read_cgu_reg_e82x(hw, TSPLL_RO_BWM_LF, &bwm_lf.val);
- if (err)
+ err = ice_read_quad_reg_e82x(hw, quad, hi_addr, &hi);
+ if (err) {
+ ice_debug(hw, ICE_DBG_PTP, "Failed to read high PTP timestamp register, err %d\n",
+ err);
return err;
-
- if (!bwm_lf.field.plllock_true_lock_cri) {
- dev_warn(ice_hw_to_dev(hw), "CGU PLL failed to lock\n");
- return -EBUSY;
}
- /* Log the current clock configuration */
- ice_debug(hw, ICE_DBG_PTP, "New CGU configuration -- %s, clk_src %s, clk_freq %s, PLL %s\n",
- dw24.field.ts_pll_enable ? "enabled" : "disabled",
- ice_clk_src_str(dw24.field.time_ref_sel),
- ice_clk_freq_str(dw9.field.time_ref_freq_sel),
- bwm_lf.field.plllock_true_lock_cri ? "locked" : "unlocked");
+ /* For E822 based internal PHYs, the timestamp is reported with the
+ * lower 8 bits in the low register, and the upper 32 bits in the high
+ * register.
+ */
+ *tstamp = FIELD_PREP(TS_PHY_HIGH_M, hi) | FIELD_PREP(TS_PHY_LOW_M, lo);
return 0;
}
/**
- * ice_init_cgu_e82x - Initialize CGU with settings from firmware
- * @hw: pointer to the HW structure
+ * ice_clear_phy_tstamp_e82x - Clear a timestamp from the quad block
+ * @hw: pointer to the HW struct
+ * @quad: the quad to read from
+ * @idx: the timestamp index to reset
*
- * Initialize the Clock Generation Unit of the E822 device.
+ * Read the timestamp out of the quad to clear its timestamp status bit from
+ * the PHY quad block that is shared between the internal PHYs of the E822
+ * devices.
+ *
+ * Note that unlike E810, software cannot directly write to the quad memory
+ * bank registers. E822 relies on the ice_get_phy_tx_tstamp_ready() function
+ * to determine which timestamps are valid. Reading a timestamp auto-clears
+ * the valid bit.
+ *
+ * To directly clear the contents of the timestamp block entirely, discarding
+ * all timestamp data at once, software should instead use
+ * ice_ptp_reset_ts_memory_quad_e82x().
+ *
+ * This function should only be called on an idx whose bit is set according to
+ * ice_get_phy_tx_tstamp_ready().
*/
-static int ice_init_cgu_e82x(struct ice_hw *hw)
+static int
+ice_clear_phy_tstamp_e82x(struct ice_hw *hw, u8 quad, u8 idx)
{
- struct ice_ts_func_info *ts_info = &hw->func_caps.ts_func_info;
- union tspll_cntr_bist_settings cntr_bist;
+ u64 unused_tstamp;
int err;
- err = ice_read_cgu_reg_e82x(hw, TSPLL_CNTR_BIST_SETTINGS,
- &cntr_bist.val);
- if (err)
+ err = ice_read_phy_tstamp_e82x(hw, quad, idx, &unused_tstamp);
+ if (err) {
+ ice_debug(hw, ICE_DBG_PTP, "Failed to read the timestamp register for quad %u, idx %u, err %d\n",
+ quad, idx, err);
return err;
+ }
- /* Disable sticky lock detection so lock err reported is accurate */
- cntr_bist.field.i_plllock_sel_0 = 0;
- cntr_bist.field.i_plllock_sel_1 = 0;
+ return 0;
+}
- err = ice_write_cgu_reg_e82x(hw, TSPLL_CNTR_BIST_SETTINGS,
- cntr_bist.val);
- if (err)
- return err;
+/**
+ * ice_ptp_reset_ts_memory_quad_e82x - Clear all timestamps from the quad block
+ * @hw: pointer to the HW struct
+ * @quad: the quad to read from
+ *
+ * Clear all timestamps from the PHY quad block that is shared between the
+ * internal PHYs on the E822 devices.
+ */
+void ice_ptp_reset_ts_memory_quad_e82x(struct ice_hw *hw, u8 quad)
+{
+ ice_write_quad_reg_e82x(hw, quad, Q_REG_TS_CTRL, Q_REG_TS_CTRL_M);
+ ice_write_quad_reg_e82x(hw, quad, Q_REG_TS_CTRL, ~(u32)Q_REG_TS_CTRL_M);
+}
- /* Configure the CGU PLL using the parameters from the function
- * capabilities.
- */
- err = ice_cfg_cgu_pll_e82x(hw, ts_info->time_ref,
- (enum ice_clk_src)ts_info->clk_src);
- if (err)
- return err;
+/**
+ * ice_ptp_reset_ts_memory_e82x - Clear all timestamps from all quad blocks
+ * @hw: pointer to the HW struct
+ */
+static void ice_ptp_reset_ts_memory_e82x(struct ice_hw *hw)
+{
+ unsigned int quad;
- return 0;
+ for (quad = 0; quad < ICE_GET_QUAD_NUM(hw->ptp.num_lports); quad++)
+ ice_ptp_reset_ts_memory_quad_e82x(hw, quad);
}
/**
@@ -377,7 +377,7 @@ int ice_cgu_get_output_pin_state_caps(struct ice_hw *hw, u8 pin_id,
#define P_REG_TIMETUS_L 0x410
#define P_REG_TIMETUS_U 0x414
-#define P_REG_40B_LOW_M 0xFF
+#define P_REG_40B_LOW_M GENMASK(7, 0)
#define P_REG_40B_HIGH_S 8
/* PHY window length registers */