Commit 39ecb29d authored by Sergey Temerkhanov's avatar Sergey Temerkhanov Committed by Jakub Kicinski
parent c199b31a
...@@ -227,563 +227,575 @@ static u64 ice_ptp_read_src_incval(struct ice_hw *hw) ...@@ -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 * ice_read_cgu_reg_e82x - Read a CGU register
* @hw: pointer to HW struct * @hw: pointer to the HW struct
* @cmd: Timer command * @addr: Register address to read
* @val: storage for register value read
* *
* Return: the source timer command register value for the given PTP timer * Read the contents of a register of the Clock Generation Unit. Only
* command. * 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, static int ice_read_cgu_reg_e82x(struct ice_hw *hw, u32 addr, u32 *val)
enum ice_ptp_tmr_cmd cmd)
{ {
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) { err = ice_sbq_rw_reg(hw, &cgu_msg);
case ICE_PTP_INIT_TIME: if (err) {
cmd_val = GLTSYN_CMD_INIT_TIME; ice_debug(hw, ICE_DBG_PTP, "Failed to read CGU register 0x%04x, err %d\n",
break; addr, err);
case ICE_PTP_INIT_INCVAL: return err;
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;
} }
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 * ice_write_cgu_reg_e82x - Write a CGU register
* @hw: pointer to HW struct * @hw: pointer to the HW struct
* @cmd: Timer command * @addr: Register address to write
* @val: value to write into the register
* *
* Note that some hardware families use a different command register value for * Write the specified value to a register of the Clock Generation Unit. Only
* the PHY ports, while other hardware families use the same register values * applicable to E822 devices.
* as the source timer.
* *
* Return: the PHY port timer command register value for the given PTP timer * Return: 0 on success, other error codes when failed to write to CGU
* command.
*/ */
static u32 ice_ptp_tmr_cmd_to_port_reg(struct ice_hw *hw, static int ice_write_cgu_reg_e82x(struct ice_hw *hw, u32 addr, u32 val)
enum ice_ptp_tmr_cmd cmd)
{ {
u32 cmd_val, tmr_idx; struct ice_sbq_msg_input cgu_msg = {
.opcode = ice_sbq_msg_wr,
/* Certain hardware families share the same register values for the .dest_dev = cgu,
* port register and source timer register. .msg_addr_low = addr,
*/ .data = val
switch (hw->ptp.phy_model) { };
case ICE_PHY_E810: int err;
return ice_ptp_tmr_cmd_to_src_reg(hw, cmd) & TS_CMD_MASK_E810;
default:
break;
}
switch (cmd) { err = ice_sbq_rw_reg(hw, &cgu_msg);
case ICE_PTP_INIT_TIME: if (err) {
cmd_val = PHY_CMD_INIT_TIME; ice_debug(hw, ICE_DBG_PTP, "Failed to write CGU register 0x%04x, err %d\n",
break; addr, err);
case ICE_PTP_INIT_INCVAL: return err;
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;
} }
tmr_idx = ice_get_ptp_src_clock_index(hw); return err;
return tmr_idx << SEL_PHY_SRC | cmd_val;
} }
/** /**
* ice_ptp_src_cmd - Prepare source timer for a timer command * ice_clk_freq_str - Convert time_ref_freq to string
* @hw: pointer to HW structure * @clk_freq: Clock frequency
* @cmd: Timer command
* *
* 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); switch (clk_freq) {
case ICE_TIME_REF_FREQ_25_000:
wr32(hw, GLTSYN_CMD, cmd_val); 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 * ice_clk_src_str - Convert time_ref_src to string
* @hw: pointer to HW struct * @clk_src: Clock source
* *
* Write the SYNC_EXEC_CMD bit to the GLTSYN_CMD_SYNC register, and flush the * Return: specified clock source converted to its string name
* 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) static const char *ice_clk_src_str(enum ice_clk_src clk_src)
{ {
struct ice_pf *pf = container_of(hw, struct ice_pf, hw); switch (clk_src) {
case ICE_CLK_SRC_TCX0:
guard(spinlock)(&pf->adapter->ptp_gltsyn_time_lock); return "TCX0";
wr32(hw, GLTSYN_CMD_SYNC, SYNC_EXEC_CMD); case ICE_CLK_SRC_TIME_REF:
ice_flush(hw); 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 * @hw: pointer to the HW struct
* @msg: the PHY message buffer to fill in * @clk_freq: Clock frequency to program
* @port: the port to access * @clk_src: Clock source to select (TIME_REF, or TCX0)
* @offset: the register offset *
* 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, static int ice_cfg_cgu_pll_e82x(struct ice_hw *hw,
struct ice_sbq_msg_input *msg, u8 port, enum ice_time_ref_freq clk_freq,
u16 offset) enum ice_clk_src clk_src)
{ {
int phy_port, phy, quadtype; union tspll_ro_bwm_lf bwm_lf;
union nac_cgu_dword19 dw19;
phy_port = port % hw->ptp.ports_per_phy; union nac_cgu_dword22 dw22;
phy = port / hw->ptp.ports_per_phy; union nac_cgu_dword24 dw24;
quadtype = ICE_GET_QUAD_NUM(port) % union nac_cgu_dword9 dw9;
ICE_GET_QUAD_NUM(hw->ptp.ports_per_phy); int err;
if (quadtype == 0) { if (clk_freq >= NUM_ICE_TIME_REF_FREQ) {
msg->msg_addr_low = P_Q0_L(P_0_BASE + offset, phy_port); dev_warn(ice_hw_to_dev(hw), "Invalid TIME_REF frequency %u\n",
msg->msg_addr_high = P_Q0_H(P_0_BASE + offset, phy_port); clk_freq);
} else { return -EINVAL;
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 (phy == 0) if (clk_src >= NUM_ICE_CLK_SRC) {
msg->dest_dev = rmn_0; dev_warn(ice_hw_to_dev(hw), "Invalid clock source %u\n",
else if (phy == 1) clk_src);
msg->dest_dev = rmn_1; return -EINVAL;
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 == ICE_CLK_SRC_TCX0 &&
* ice_is_40b_phy_reg_e82x - Check if this is a 40bit PHY register clk_freq != ICE_TIME_REF_FREQ_25_000) {
* @low_addr: the low address to check dev_warn(ice_hw_to_dev(hw),
* @high_addr: on return, contains the high address of the 40bit value "TCX0 only supports 25 MHz frequency\n");
* return -EINVAL;
* 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;
} }
}
/** err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD9, &dw9.val);
* ice_read_phy_reg_e82x - Read a PHY register if (err)
* @hw: pointer to the HW struct return err;
* @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;
ice_fill_phy_msg_e82x(hw, &msg, port, offset); err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD24, &dw24.val);
msg.opcode = ice_sbq_msg_rd; if (err)
return err;
err = ice_sbq_rw_reg(hw, &msg); err = ice_read_cgu_reg_e82x(hw, TSPLL_RO_BWM_LF, &bwm_lf.val);
if (err) { if (err)
ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
err);
return 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;
/** dw19.field.tspll_fbdiv_intgr = e822_cgu_params[clk_freq].feedback_div;
* ice_read_64b_phy_reg_e82x - Read a 64bit value from PHY registers dw19.field.tspll_ndivratio = 1;
* @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;
/* Only operate on registers known to be split into two 32bit err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD19, dw19.val);
* registers. if (err)
*/ return err;
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_phy_reg_e82x(hw, port, low_addr, &low); /* Configure the TS PLL post divisor */
if (err) { err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD22, &dw22.val);
ice_debug(hw, ICE_DBG_PTP, "Failed to read from low register 0x%08x\n, err %d", if (err)
low_addr, err);
return err; return err;
}
err = ice_read_phy_reg_e82x(hw, port, high_addr, &high); dw22.field.time1588clk_div = e822_cgu_params[clk_freq].post_pll_div;
if (err) { dw22.field.time1588clk_sel_div2 = 0;
ice_debug(hw, ICE_DBG_PTP, "Failed to read from high register 0x%08x\n, err %d",
high_addr, err); 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; 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; return 0;
} }
/** /**
* ice_write_phy_reg_e82x - Write a PHY register * ice_init_cgu_e82x - Initialize CGU with settings from firmware
* @hw: pointer to the HW struct * @hw: pointer to the HW structure
* @port: PHY port to write to
* @offset: PHY register offset to write
* @val: The value to write to the register
* *
* 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 static int ice_init_cgu_e82x(struct ice_hw *hw)
ice_write_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 offset, u32 val)
{ {
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; int err;
ice_fill_phy_msg_e82x(hw, &msg, port, offset); err = ice_read_cgu_reg_e82x(hw, TSPLL_CNTR_BIST_SETTINGS,
msg.opcode = ice_sbq_msg_wr; &cntr_bist.val);
msg.data = val; if (err)
return err;
err = ice_sbq_rw_reg(hw, &msg); /* Disable sticky lock detection so lock err reported is accurate */
if (err) { cntr_bist.field.i_plllock_sel_0 = 0;
ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n", cntr_bist.field.i_plllock_sel_1 = 0;
err);
err = ice_write_cgu_reg_e82x(hw, TSPLL_CNTR_BIST_SETTINGS,
cntr_bist.val);
if (err)
return 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 * ice_ptp_tmr_cmd_to_port_reg- Convert to port timer command value
* @hw: pointer to the HW struct * @hw: pointer to HW struct
* @port: port to write to * @cmd: Timer command
* @low_addr: offset of the low register
* @val: 40b value to write
* *
* Write the provided 40b value to the two associated registers by splitting * Note that some hardware families use a different command register value for
* it up into two chunks, the lower 8 bits and the upper 32 bits. * 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 static u32 ice_ptp_tmr_cmd_to_port_reg(struct ice_hw *hw,
ice_write_40b_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 low_addr, u64 val) enum ice_ptp_tmr_cmd cmd)
{ {
u32 low, high; u32 cmd_val, tmr_idx;
u16 high_addr;
int err;
/* Only operate on registers known to be split into a lower 8 bit /* Certain hardware families share the same register values for the
* register and an upper 32 bit register. * port register and source timer register.
*/ */
if (!ice_is_40b_phy_reg_e82x(low_addr, &high_addr)) { switch (hw->ptp.phy_model) {
ice_debug(hw, ICE_DBG_PTP, "Invalid 40b register addr 0x%08x\n", case ICE_PHY_E810:
low_addr); return ice_ptp_tmr_cmd_to_src_reg(hw, cmd) & TS_CMD_MASK_E810;
return -EINVAL; default:
break;
} }
low = (u32)(val & P_REG_40B_LOW_M); switch (cmd) {
high = (u32)(val >> P_REG_40B_HIGH_S); case ICE_PTP_INIT_TIME:
cmd_val = PHY_CMD_INIT_TIME;
err = ice_write_phy_reg_e82x(hw, port, low_addr, low); break;
if (err) { case ICE_PTP_INIT_INCVAL:
ice_debug(hw, ICE_DBG_PTP, "Failed to write to low register 0x%08x\n, err %d", cmd_val = PHY_CMD_INIT_INCVAL;
low_addr, err); break;
return err; 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); tmr_idx = ice_get_ptp_src_clock_index(hw);
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; return tmr_idx << SEL_PHY_SRC | cmd_val;
} }
/** /**
* ice_write_64b_phy_reg_e82x - Write a 64bit value to PHY registers * ice_ptp_src_cmd - Prepare source timer for a timer command
* @hw: pointer to the HW struct * @hw: pointer to HW structure
* @port: PHY port to read from * @cmd: Timer command
* @low_addr: offset of the lower register to read from
* @val: the contents of the 64bit value to write to PHY
* *
* Write the 64bit value to the two associated 32bit PHY registers. The offset * Prepare the source timer for an upcoming timer sync command.
* 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 void ice_ptp_src_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd)
ice_write_64b_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 low_addr, u64 val)
{ {
u32 low, high; u32 cmd_val = ice_ptp_tmr_cmd_to_src_reg(hw, cmd);
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);
err = ice_write_phy_reg_e82x(hw, port, low_addr, low); wr32(hw, GLTSYN_CMD, cmd_val);
if (err) { }
ice_debug(hw, ICE_DBG_PTP, "Failed to write to low register 0x%08x\n, err %d",
low_addr, err);
return err;
}
err = ice_write_phy_reg_e82x(hw, port, high_addr, high); /**
if (err) { * ice_ptp_exec_tmr_cmd - Execute all prepared timer commands
ice_debug(hw, ICE_DBG_PTP, "Failed to write to high register 0x%08x\n, err %d", * @hw: pointer to HW struct
high_addr, err); *
return err; * 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 * @hw: pointer to the HW struct
* @msg: the PHY message buffer to fill in * @msg: the PHY message buffer to fill in
* @quad: the quad to access * @port: the port to access
* @offset: the register offset * @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, static void ice_fill_phy_msg_e82x(struct ice_hw *hw,
struct ice_sbq_msg_input *msg, u8 quad, struct ice_sbq_msg_input *msg, u8 port,
u16 offset) u16 offset)
{ {
u32 addr; int phy_port, phy, quadtype;
if (quad >= ICE_GET_QUAD_NUM(hw->ptp.num_lports)) phy_port = port % hw->ptp.ports_per_phy;
return -EINVAL; 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))) if (phy == 0)
addr = Q_0_BASE + offset; msg->dest_dev = rmn_0;
else if (phy == 1)
msg->dest_dev = rmn_1;
else else
addr = Q_1_BASE + offset; msg->dest_dev = rmn_2;
msg->msg_addr_low = lower_16_bits(addr);
msg->msg_addr_high = upper_16_bits(addr);
return 0;
} }
/** /**
* ice_read_quad_reg_e82x - Read a PHY quad register * ice_is_64b_phy_reg_e82x - Check if this is a 64bit PHY register
* @hw: pointer to the HW struct * @low_addr: the low address to check
* @quad: quad to read from * @high_addr: on return, contains the high address of the 64bit register
* @offset: quad register offset to read
* @val: on return, the contents read from the quad
* *
* Read a quad register over the device sideband queue. Quad registers are * Checks if the provided low address is one of the known 64bit PHY values
* shared between multiple PHYs. * represented as two 32bit registers. If it is, return the appropriate high
* register offset to use.
*/ */
int static bool ice_is_64b_phy_reg_e82x(u16 low_addr, u16 *high_addr)
ice_read_quad_reg_e82x(struct ice_hw *hw, u8 quad, u16 offset, u32 *val)
{ {
struct ice_sbq_msg_input msg = {0}; switch (low_addr) {
int err; case P_REG_PAR_PCS_TX_OFFSET_L:
*high_addr = P_REG_PAR_PCS_TX_OFFSET_U;
err = ice_fill_quad_msg_e82x(hw, &msg, quad, offset); return true;
if (err) case P_REG_PAR_PCS_RX_OFFSET_L:
return err; *high_addr = P_REG_PAR_PCS_RX_OFFSET_U;
return true;
msg.opcode = ice_sbq_msg_rd; case P_REG_PAR_TX_TIME_L:
*high_addr = P_REG_PAR_TX_TIME_U;
err = ice_sbq_rw_reg(hw, &msg); return true;
if (err) { case P_REG_PAR_RX_TIME_L:
ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n", *high_addr = P_REG_PAR_RX_TIME_U;
err); return true;
return err; 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; /**
* ice_is_40b_phy_reg_e82x - Check if this is a 40bit PHY register
return 0; * @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 * @hw: pointer to the HW struct
* @quad: quad to write to * @port: PHY port to read from
* @offset: quad register offset to write * @offset: PHY register offset to read
* @val: The value to write to the register * @val: on return, the contents read from the PHY
* *
* Write a quad register over the device sideband queue. Quad registers are * Read a PHY register for the given port over the device sideband queue.
* shared between multiple PHYs.
*/ */
int static int
ice_write_quad_reg_e82x(struct ice_hw *hw, u8 quad, u16 offset, u32 val) ice_read_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 offset, u32 *val)
{ {
struct ice_sbq_msg_input msg = {0}; struct ice_sbq_msg_input msg = {0};
int err; int err;
err = ice_fill_quad_msg_e82x(hw, &msg, quad, offset); ice_fill_phy_msg_e82x(hw, &msg, port, offset);
if (err) msg.opcode = ice_sbq_msg_rd;
return err;
msg.opcode = ice_sbq_msg_wr;
msg.data = val;
err = ice_sbq_rw_reg(hw, &msg); err = ice_sbq_rw_reg(hw, &msg);
if (err) { if (err) {
...@@ -792,85 +804,81 @@ ice_write_quad_reg_e82x(struct ice_hw *hw, u8 quad, u16 offset, u32 val) ...@@ -792,85 +804,81 @@ ice_write_quad_reg_e82x(struct ice_hw *hw, u8 quad, u16 offset, u32 val)
return err; return err;
} }
*val = msg.data;
return 0; 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 * @hw: pointer to the HW struct
* @quad: the quad to read from * @port: PHY port to read from
* @idx: the timestamp index to read * @low_addr: offset of the lower register to read from
* @tstamp: on return, the 40bit timestamp value * @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 * Reads the two registers associated with a 64bit value and returns it in the
* quad memory block that is shared between the internal PHYs of the E822 * val pointer. The offset always specifies the lower register offset to use.
* family of devices. * The high offset is looked up. This function only operates on registers
* known to be two parts of a 64bit value.
*/ */
static int 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 low, high;
u32 lo, hi; u16 high_addr;
int err; int err;
lo_addr = (u16)TS_L(Q_REG_TX_MEMORY_BANK_START, idx); /* Only operate on registers known to be split into two 32bit
hi_addr = (u16)TS_H(Q_REG_TX_MEMORY_BANK_START, idx); * 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) { if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read low PTP timestamp register, err %d\n", ice_debug(hw, ICE_DBG_PTP, "Failed to read from low register 0x%08x\n, err %d",
err); low_addr, err);
return 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) { if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read high PTP timestamp register, err %d\n", ice_debug(hw, ICE_DBG_PTP, "Failed to read from high register 0x%08x\n, err %d",
err); high_addr, err);
return err; return err;
} }
/* For E822 based internal PHYs, the timestamp is reported with the *val = (u64)high << 32 | low;
* 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);
return 0; 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 * @hw: pointer to the HW struct
* @quad: the quad to read from * @port: PHY port to write to
* @idx: the timestamp index to reset * @offset: PHY register offset to write
* * @val: The value to write to the register
* 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 * Write a PHY register for the given port over the device sideband queue.
* ice_get_phy_tx_tstamp_ready().
*/ */
static int 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; 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) { if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read the timestamp register for quad %u, idx %u, err %d\n", ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
quad, idx, err); err);
return err; return err;
} }
...@@ -878,312 +886,307 @@ ice_clear_phy_tstamp_e82x(struct ice_hw *hw, u8 quad, u8 idx) ...@@ -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 * ice_write_40b_phy_reg_e82x - Write a 40b value to the PHY
* @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
* @hw: pointer to the HW struct * @hw: pointer to the HW struct
* @addr: Register address to read * @port: port to write to
* @val: storage for register value read * @low_addr: offset of the low register
* @val: 40b value to write
* *
* Read the contents of a register of the Clock Generation Unit. Only * Write the provided 40b value to the two associated registers by splitting
* applicable to E822 devices. * it up into two chunks, the lower 8 bits and the upper 32 bits.
*/ */
static int 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; int err;
cgu_msg.opcode = ice_sbq_msg_rd; /* Only operate on registers known to be split into a lower 8 bit
cgu_msg.dest_dev = cgu; * register and an upper 32 bit register.
cgu_msg.msg_addr_low = addr; */
cgu_msg.msg_addr_high = 0x0; 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) { if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read CGU register 0x%04x, err %d\n", ice_debug(hw, ICE_DBG_PTP, "Failed to write to low register 0x%08x\n, err %d",
addr, err); low_addr, err);
return 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 * @hw: pointer to the HW struct
* @addr: Register address to write * @port: PHY port to read from
* @val: value to write into the register * @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 * Write the 64bit value to the two associated 32bit PHY registers. The offset
* applicable to E822 devices. * 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 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; int err;
cgu_msg.opcode = ice_sbq_msg_wr; /* Only operate on registers known to be split into two 32bit
cgu_msg.dest_dev = cgu; * registers.
cgu_msg.msg_addr_low = addr; */
cgu_msg.msg_addr_high = 0x0; if (!ice_is_64b_phy_reg_e82x(low_addr, &high_addr)) {
cgu_msg.data = val; 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) { if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to write CGU register 0x%04x, err %d\n", ice_debug(hw, ICE_DBG_PTP, "Failed to write to low register 0x%08x\n, err %d",
addr, err); low_addr, err);
return err; return err;
} }
return err; 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);
* ice_clk_freq_str - Convert time_ref_freq to string return err;
* @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";
} }
return 0;
} }
/** /**
* ice_clk_src_str - Convert time_ref_src to string * ice_fill_quad_msg_e82x - Fill message data for quad register access
* @clk_src: Clock source * @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) { u32 addr;
case ICE_CLK_SRC_TCX0:
return "TCX0"; if (quad >= ICE_GET_QUAD_NUM(hw->ptp.num_lports))
case ICE_CLK_SRC_TIME_REF: return -EINVAL;
return "TIME_REF";
default: msg->dest_dev = rmn_0;
return "Unknown";
} 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 * @hw: pointer to the HW struct
* @clk_freq: Clock frequency to program * @quad: quad to read from
* @clk_src: Clock source to select (TIME_REF, or TCX0) * @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 * Read a quad register over the device sideband queue. Quad registers are
* time reference, enabling the PLL which drives the PTP hardware clock. * shared between multiple PHYs.
*/ */
static int int
ice_cfg_cgu_pll_e82x(struct ice_hw *hw, enum ice_time_ref_freq clk_freq, ice_read_quad_reg_e82x(struct ice_hw *hw, u8 quad, u16 offset, u32 *val)
enum ice_clk_src clk_src)
{ {
union tspll_ro_bwm_lf bwm_lf; struct ice_sbq_msg_input msg = {0};
union nac_cgu_dword19 dw19;
union nac_cgu_dword22 dw22;
union nac_cgu_dword24 dw24;
union nac_cgu_dword9 dw9;
int err; int err;
if (clk_freq >= NUM_ICE_TIME_REF_FREQ) { err = ice_fill_quad_msg_e82x(hw, &msg, quad, offset);
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);
if (err) if (err)
return err; return err;
err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD24, &dw24.val); msg.opcode = ice_sbq_msg_rd;
if (err)
return err;
err = ice_read_cgu_reg_e82x(hw, TSPLL_RO_BWM_LF, &bwm_lf.val); err = ice_sbq_rw_reg(hw, &msg);
if (err) if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
err);
return 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 */ *val = msg.data;
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;
dw19.field.tspll_fbdiv_intgr = e822_cgu_params[clk_freq].feedback_div; return 0;
dw19.field.tspll_ndivratio = 1; }
err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD19, dw19.val); /**
if (err) * ice_write_quad_reg_e82x - Write a PHY quad register
return err; * @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_fill_quad_msg_e82x(hw, &msg, quad, offset);
err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD22, &dw22.val);
if (err) if (err)
return err; return err;
dw22.field.time1588clk_div = e822_cgu_params[clk_freq].post_pll_div; msg.opcode = ice_sbq_msg_wr;
dw22.field.time1588clk_sel_div2 = 0; msg.data = val;
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_sbq_rw_reg(hw, &msg);
err = ice_read_cgu_reg_e82x(hw, NAC_CGU_DWORD24, &dw24.val); if (err) {
if (err) ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
err);
return err; return err;
}
dw24.field.ref1588_ck_div = e822_cgu_params[clk_freq].refclk_pre_div; return 0;
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) * ice_read_phy_tstamp_e82x - Read a PHY timestamp out of the quad block
return err; * @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 */ lo_addr = (u16)TS_L(Q_REG_TX_MEMORY_BANK_START, idx);
dw24.field.ts_pll_enable = 1; hi_addr = (u16)TS_H(Q_REG_TX_MEMORY_BANK_START, idx);
err = ice_write_cgu_reg_e82x(hw, NAC_CGU_DWORD24, dw24.val); err = ice_read_quad_reg_e82x(hw, quad, lo_addr, &lo);
if (err) if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read low PTP timestamp register, err %d\n",
err);
return err; return err;
}
/* Wait to verify if the PLL locks */ err = ice_read_quad_reg_e82x(hw, quad, hi_addr, &hi);
usleep_range(1000, 5000); if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read high PTP timestamp register, err %d\n",
err = ice_read_cgu_reg_e82x(hw, TSPLL_RO_BWM_LF, &bwm_lf.val); err);
if (err)
return 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 */ /* For E822 based internal PHYs, the timestamp is reported with the
ice_debug(hw, ICE_DBG_PTP, "New CGU configuration -- %s, clk_src %s, clk_freq %s, PLL %s\n", * lower 8 bits in the low register, and the upper 32 bits in the high
dw24.field.ts_pll_enable ? "enabled" : "disabled", * register.
ice_clk_src_str(dw24.field.time_ref_sel), */
ice_clk_freq_str(dw9.field.time_ref_freq_sel), *tstamp = FIELD_PREP(TS_PHY_HIGH_M, hi) | FIELD_PREP(TS_PHY_LOW_M, lo);
bwm_lf.field.plllock_true_lock_cri ? "locked" : "unlocked");
return 0; return 0;
} }
/** /**
* ice_init_cgu_e82x - Initialize CGU with settings from firmware * ice_clear_phy_tstamp_e82x - Clear a timestamp from the quad block
* @hw: pointer to the HW structure * @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; u64 unused_tstamp;
union tspll_cntr_bist_settings cntr_bist;
int err; int err;
err = ice_read_cgu_reg_e82x(hw, TSPLL_CNTR_BIST_SETTINGS, err = ice_read_phy_tstamp_e82x(hw, quad, idx, &unused_tstamp);
&cntr_bist.val); if (err) {
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; return err;
}
/* Disable sticky lock detection so lock err reported is accurate */ return 0;
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); * ice_ptp_reset_ts_memory_quad_e82x - Clear all timestamps from the quad block
if (err) * @hw: pointer to the HW struct
return err; * @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. * ice_ptp_reset_ts_memory_e82x - Clear all timestamps from all quad blocks
*/ * @hw: pointer to the HW struct
err = ice_cfg_cgu_pll_e82x(hw, ts_info->time_ref, */
(enum ice_clk_src)ts_info->clk_src); static void ice_ptp_reset_ts_memory_e82x(struct ice_hw *hw)
if (err) {
return err; 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, ...@@ -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_L 0x410
#define P_REG_TIMETUS_U 0x414 #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 #define P_REG_40B_HIGH_S 8
/* PHY window length registers */ /* PHY window length registers */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment