Commit 58f9416d authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'ice-support-to-dump-phy-config-fec'

Tony Nguyen says:

====================
ice: Support to dump PHY config, FEC

Anil Samal says:

Implementation to dump PHY configuration and FEC statistics to
facilitate link level debugging of customer issues. Implementation has
two parts

a.     Serdes equalization
        # ethtool  -d eth0
        Output:
        Offset          Values
        ------          ------
        0x0000:         00 00 00 00 03 00 00 00 05 00 00 00 01 08 00 40
        0x0010:         01 00 00 40 00 00 39 3c 01 00 00 00 00 00 00 00
        0x0020:         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        ...
        0x01f0:         01 00 00 00 ef be ad de 8f 00 00 00 00 00 00 00
        0x0200:         00 00 00 00 ef be ad de 00 00 00 00 00 00 00 00
        0x0210:         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x0220:         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x0230:         00 00 00 00 00 00 00 00 00 00 00 00 fa ff 00 00
        0x0240:         06 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00
        0x0250:         0f b0 0f b0 00 00 00 00 00 00 00 00 00 00 00 00
        0x0260:         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x0270:         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x0280:         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x0290:         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x02a0:         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x02b0:         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x02c0:         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x02d0:         00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        0x02e0:         00 00 00 00 00 00 00 00 00 00 00 00
Current implementation appends 176 bytes i.e. 44 bytes * 4 serdes lane.
For port with 2 serdes lane, first 88 bytes are valid values and
remaining 88 bytes are filled with zero. Similarly for port with 1
serdes lane, first 44 bytes are valid and remaining 132 bytes are marked
zero.

Each set of serdes equalizer parameter (i.e. set of 44 bytes) follows
below order
    a. rx_equalization_pre2
    b. rx_equalization_pre1
    c. rx_equalization_post1
    d. rx_equalization_bflf
    e. rx_equalization_bfhf
    f. rx_equalization_drate
    g. tx_equalization_pre1
    h. tx_equalization_pre3
    i. tx_equalization_atten
    j. tx_equalization_post1
    k. tx_equalization_pre2
Where each individual equalizer parameter is of 4 bytes. As ethtool
prints values as individual bytes, for little endian machine these
values will be in reverse byte order.

b.    FEC block counts
        # ethtool  -I --show-fec eth0
        Output:
         FEC parameters for eth0:
        Supported/Configured FEC encodings: Auto RS BaseR
        Active FEC encoding: RS
        Statistics:
        corrected_blocks: 0
         uncorrectable_blocks: 0

This series do following:
Patch 1 - Implementation to support user provided flag for side band
queue command.
Patch 2 - Currently driver does not have a way to derive serdes lane
number, pcs quad , pcs port from port number. So we introduced a
mechanism to derive above info.
          Ethtool interface extension to include FEC statistics counter.
Patch 3 - Ethtool interface extension to include serdes equalizer output.

v1: https://lore.kernel.org/netdev/20240702180710.2606969-1-anthony.l.nguyen@intel.com/
====================

Link: https://patch.msgid.link/20240709202951.2103115-1-anthony.l.nguyen@intel.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents d00ba1d7 70838938
......@@ -1461,6 +1461,55 @@ struct ice_aqc_get_sensor_reading_resp {
} data;
};
/* DNL call command (indirect 0x0682)
* Struct is used for both command and response
*/
struct ice_aqc_dnl_call_command {
u8 ctx; /* Used in command, reserved in response */
u8 reserved;
__le16 activity_id;
#define ICE_AQC_ACT_ID_DNL 0x1129
__le32 reserved1;
__le32 addr_high;
__le32 addr_low;
};
struct ice_aqc_dnl_equa_param {
__le16 data_in;
#define ICE_AQC_RX_EQU_SHIFT 8
#define ICE_AQC_RX_EQU_PRE2 (0x10 << ICE_AQC_RX_EQU_SHIFT)
#define ICE_AQC_RX_EQU_PRE1 (0x11 << ICE_AQC_RX_EQU_SHIFT)
#define ICE_AQC_RX_EQU_POST1 (0x12 << ICE_AQC_RX_EQU_SHIFT)
#define ICE_AQC_RX_EQU_BFLF (0x13 << ICE_AQC_RX_EQU_SHIFT)
#define ICE_AQC_RX_EQU_BFHF (0x14 << ICE_AQC_RX_EQU_SHIFT)
#define ICE_AQC_RX_EQU_DRATE (0x15 << ICE_AQC_RX_EQU_SHIFT)
#define ICE_AQC_TX_EQU_PRE1 0x0
#define ICE_AQC_TX_EQU_PRE3 0x3
#define ICE_AQC_TX_EQU_ATTEN 0x4
#define ICE_AQC_TX_EQU_POST1 0x8
#define ICE_AQC_TX_EQU_PRE2 0xC
__le16 op_code_serdes_sel;
#define ICE_AQC_OP_CODE_SHIFT 4
#define ICE_AQC_OP_CODE_RX_EQU (0x9 << ICE_AQC_OP_CODE_SHIFT)
#define ICE_AQC_OP_CODE_TX_EQU (0x10 << ICE_AQC_OP_CODE_SHIFT)
__le32 reserved[3];
};
struct ice_aqc_dnl_equa_respon {
/* Equalization value can be negative */
int val;
__le32 reserved[3];
};
/* DNL call command/response buffer (indirect 0x0682) */
struct ice_aqc_dnl_call {
union {
struct ice_aqc_dnl_equa_param txrx_equa_reqs;
__le32 stores[4];
struct ice_aqc_dnl_equa_respon txrx_equa_resp;
} sto;
};
struct ice_aqc_link_topo_params {
u8 lport_num;
u8 lport_num_valid;
......@@ -2564,6 +2613,7 @@ struct ice_aq_desc {
struct ice_aqc_get_link_status get_link_status;
struct ice_aqc_event_lan_overflow lan_overflow;
struct ice_aqc_get_link_topo get_link_topo;
struct ice_aqc_dnl_call_command dnl_call;
struct ice_aqc_i2c read_write_i2c;
struct ice_aqc_read_i2c_resp read_i2c_resp;
struct ice_aqc_get_set_tx_topo get_set_tx_topo;
......@@ -2688,6 +2738,7 @@ enum ice_adminq_opc {
ice_aqc_opc_set_phy_rec_clk_out = 0x0630,
ice_aqc_opc_get_phy_rec_clk_out = 0x0631,
ice_aqc_opc_get_sensor_reading = 0x0632,
ice_aqc_opc_dnl_call = 0x0682,
ice_aqc_opc_get_link_topo = 0x06E0,
ice_aqc_opc_read_i2c = 0x06E2,
ice_aqc_opc_write_i2c = 0x06E3,
......
......@@ -1497,8 +1497,9 @@ ice_sbq_send_cmd(struct ice_hw *hw, struct ice_sbq_cmd_desc *desc,
* ice_sbq_rw_reg - Fill Sideband Queue command
* @hw: pointer to the HW struct
* @in: message info to be filled in descriptor
* @flags: control queue descriptor flags
*/
int ice_sbq_rw_reg(struct ice_hw *hw, struct ice_sbq_msg_input *in)
int ice_sbq_rw_reg(struct ice_hw *hw, struct ice_sbq_msg_input *in, u16 flags)
{
struct ice_sbq_cmd_desc desc = {0};
struct ice_sbq_msg_req msg = {0};
......@@ -1522,7 +1523,7 @@ int ice_sbq_rw_reg(struct ice_hw *hw, struct ice_sbq_msg_input *in)
*/
msg_len -= sizeof(msg.data);
desc.flags = cpu_to_le16(ICE_AQ_FLAG_RD);
desc.flags = cpu_to_le16(flags);
desc.opcode = cpu_to_le16(ice_sbq_opc_neigh_dev_req);
desc.param0.cmd_len = cpu_to_le16(msg_len);
status = ice_sbq_send_cmd(hw, &desc, &msg, msg_len, NULL);
......@@ -3370,6 +3371,100 @@ int ice_update_link_info(struct ice_port_info *pi)
return status;
}
/**
* ice_aq_get_phy_equalization - function to read serdes equaliser
* value from firmware using admin queue command.
* @hw: pointer to the HW struct
* @data_in: represents the serdes equalization parameter requested
* @op_code: represents the serdes number and flag to represent tx or rx
* @serdes_num: represents the serdes number
* @output: pointer to the caller-supplied buffer to return serdes equaliser
*
* Return: non-zero status on error and 0 on success.
*/
int ice_aq_get_phy_equalization(struct ice_hw *hw, u16 data_in, u16 op_code,
u8 serdes_num, int *output)
{
struct ice_aqc_dnl_call_command *cmd;
struct ice_aqc_dnl_call buf = {};
struct ice_aq_desc desc;
int err;
buf.sto.txrx_equa_reqs.data_in = cpu_to_le16(data_in);
buf.sto.txrx_equa_reqs.op_code_serdes_sel =
cpu_to_le16(op_code | (serdes_num & 0xF));
cmd = &desc.params.dnl_call;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_dnl_call);
desc.flags |= cpu_to_le16(ICE_AQ_FLAG_BUF |
ICE_AQ_FLAG_RD |
ICE_AQ_FLAG_SI);
desc.datalen = cpu_to_le16(sizeof(struct ice_aqc_dnl_call));
cmd->activity_id = cpu_to_le16(ICE_AQC_ACT_ID_DNL);
err = ice_aq_send_cmd(hw, &desc, &buf, sizeof(struct ice_aqc_dnl_call),
NULL);
*output = err ? 0 : buf.sto.txrx_equa_resp.val;
return err;
}
#define FEC_REG_PORT(port) { \
FEC_CORR_LOW_REG_PORT##port, \
FEC_CORR_HIGH_REG_PORT##port, \
FEC_UNCORR_LOW_REG_PORT##port, \
FEC_UNCORR_HIGH_REG_PORT##port, \
}
static const u32 fec_reg[][ICE_FEC_MAX] = {
FEC_REG_PORT(0),
FEC_REG_PORT(1),
FEC_REG_PORT(2),
FEC_REG_PORT(3)
};
/**
* ice_aq_get_fec_stats - reads fec stats from phy
* @hw: pointer to the HW struct
* @pcs_quad: represents pcsquad of user input serdes
* @pcs_port: represents the pcs port number part of above pcs quad
* @fec_type: represents FEC stats type
* @output: pointer to the caller-supplied buffer to return requested fec stats
*
* Return: non-zero status on error and 0 on success.
*/
int ice_aq_get_fec_stats(struct ice_hw *hw, u16 pcs_quad, u16 pcs_port,
enum ice_fec_stats_types fec_type, u32 *output)
{
u16 flag = (ICE_AQ_FLAG_RD | ICE_AQ_FLAG_BUF | ICE_AQ_FLAG_SI);
struct ice_sbq_msg_input msg = {};
u32 receiver_id, reg_offset;
int err;
if (pcs_port > 3)
return -EINVAL;
reg_offset = fec_reg[pcs_port][fec_type];
if (pcs_quad == 0)
receiver_id = FEC_RECEIVER_ID_PCS0;
else if (pcs_quad == 1)
receiver_id = FEC_RECEIVER_ID_PCS1;
else
return -EINVAL;
msg.msg_addr_low = lower_16_bits(reg_offset);
msg.msg_addr_high = receiver_id;
msg.opcode = ice_sbq_msg_rd;
msg.dest_dev = rmn_0;
err = ice_sbq_rw_reg(hw, &msg, flag);
if (err)
return err;
*output = msg.data;
return 0;
}
/**
* ice_cache_phy_user_req
* @pi: port information structure
......
......@@ -17,6 +17,27 @@
#define ICE_SQ_SEND_DELAY_TIME_MS 10
#define ICE_SQ_SEND_MAX_EXECUTE 3
#define FEC_REG_SHIFT 2
#define FEC_RECV_ID_SHIFT 4
#define FEC_CORR_LOW_REG_PORT0 (0x02 << FEC_REG_SHIFT)
#define FEC_CORR_HIGH_REG_PORT0 (0x03 << FEC_REG_SHIFT)
#define FEC_UNCORR_LOW_REG_PORT0 (0x04 << FEC_REG_SHIFT)
#define FEC_UNCORR_HIGH_REG_PORT0 (0x05 << FEC_REG_SHIFT)
#define FEC_CORR_LOW_REG_PORT1 (0x42 << FEC_REG_SHIFT)
#define FEC_CORR_HIGH_REG_PORT1 (0x43 << FEC_REG_SHIFT)
#define FEC_UNCORR_LOW_REG_PORT1 (0x44 << FEC_REG_SHIFT)
#define FEC_UNCORR_HIGH_REG_PORT1 (0x45 << FEC_REG_SHIFT)
#define FEC_CORR_LOW_REG_PORT2 (0x4A << FEC_REG_SHIFT)
#define FEC_CORR_HIGH_REG_PORT2 (0x4B << FEC_REG_SHIFT)
#define FEC_UNCORR_LOW_REG_PORT2 (0x4C << FEC_REG_SHIFT)
#define FEC_UNCORR_HIGH_REG_PORT2 (0x4D << FEC_REG_SHIFT)
#define FEC_CORR_LOW_REG_PORT3 (0x52 << FEC_REG_SHIFT)
#define FEC_CORR_HIGH_REG_PORT3 (0x53 << FEC_REG_SHIFT)
#define FEC_UNCORR_LOW_REG_PORT3 (0x54 << FEC_REG_SHIFT)
#define FEC_UNCORR_HIGH_REG_PORT3 (0x55 << FEC_REG_SHIFT)
#define FEC_RECEIVER_ID_PCS0 (0x33 << FEC_RECV_ID_SHIFT)
#define FEC_RECEIVER_ID_PCS1 (0x34 << FEC_RECV_ID_SHIFT)
int ice_init_hw(struct ice_hw *hw);
void ice_deinit_hw(struct ice_hw *hw);
int ice_check_reset(struct ice_hw *hw);
......@@ -121,6 +142,11 @@ int
ice_get_link_default_override(struct ice_link_default_override_tlv *ldo,
struct ice_port_info *pi);
bool ice_is_phy_caps_an_enabled(struct ice_aqc_get_phy_caps_data *caps);
int ice_aq_get_phy_equalization(struct ice_hw *hw, u16 data_in, u16 op_code,
u8 serdes_num, int *output);
int
ice_aq_get_fec_stats(struct ice_hw *hw, u16 pcs_quad, u16 pcs_port,
enum ice_fec_stats_types fec_type, u32 *output);
enum ice_fc_mode ice_caps_to_fc_mode(u8 caps);
enum ice_fec_mode ice_caps_to_fec_mode(u8 caps, u8 fec_options);
......@@ -201,7 +227,7 @@ int ice_replay_vsi(struct ice_hw *hw, u16 vsi_handle);
void ice_replay_post(struct ice_hw *hw);
struct ice_q_ctx *
ice_get_lan_q_ctx(struct ice_hw *hw, u16 vsi_handle, u8 tc, u16 q_handle);
int ice_sbq_rw_reg(struct ice_hw *hw, struct ice_sbq_msg_input *in);
int ice_sbq_rw_reg(struct ice_hw *hw, struct ice_sbq_msg_input *in, u16 flag);
int
ice_aq_get_cgu_abilities(struct ice_hw *hw,
struct ice_aqc_get_cgu_abilities *abilities);
......
......@@ -9,6 +9,35 @@ struct ice_phy_type_to_ethtool {
u8 link_mode;
};
struct ice_serdes_equalization_to_ethtool {
int rx_equalization_pre2;
int rx_equalization_pre1;
int rx_equalization_post1;
int rx_equalization_bflf;
int rx_equalization_bfhf;
int rx_equalization_drate;
int tx_equalization_pre1;
int tx_equalization_pre3;
int tx_equalization_atten;
int tx_equalization_post1;
int tx_equalization_pre2;
};
struct ice_regdump_to_ethtool {
/* A multilane port can have max 4 serdes */
struct ice_serdes_equalization_to_ethtool equalization[4];
};
/* Port topology from lport i.e.
* serdes mapping, pcsquad, macport, cage etc...
*/
struct ice_port_topology {
u16 pcs_port;
u16 primary_serdes_lane;
u16 serdes_lane_count;
u16 pcs_quad_select;
};
/* Macro to make PHY type to Ethtool link mode table entry.
* The index is the PHY type.
*/
......
......@@ -247,7 +247,7 @@ static int ice_read_cgu_reg_e82x(struct ice_hw *hw, u32 addr, u32 *val)
};
int err;
err = ice_sbq_rw_reg(hw, &cgu_msg);
err = ice_sbq_rw_reg(hw, &cgu_msg, ICE_AQ_FLAG_RD);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read CGU register 0x%04x, err %d\n",
addr, err);
......@@ -280,7 +280,7 @@ static int ice_write_cgu_reg_e82x(struct ice_hw *hw, u32 addr, u32 val)
};
int err;
err = ice_sbq_rw_reg(hw, &cgu_msg);
err = ice_sbq_rw_reg(hw, &cgu_msg, ICE_AQ_FLAG_RD);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to write CGU register 0x%04x, err %d\n",
addr, err);
......@@ -902,7 +902,7 @@ static int ice_write_phy_eth56g(struct ice_hw *hw, u8 phy_idx, u32 addr,
phy_msg.data = val;
phy_msg.dest_dev = hw->ptp.phy.eth56g.phy_addr[phy_idx];
err = ice_sbq_rw_reg(hw, &phy_msg);
err = ice_sbq_rw_reg(hw, &phy_msg, ICE_AQ_FLAG_RD);
if (err)
ice_debug(hw, ICE_DBG_PTP, "PTP failed to send msg to phy %d\n",
......@@ -934,7 +934,7 @@ static int ice_read_phy_eth56g(struct ice_hw *hw, u8 phy_idx, u32 addr,
phy_msg.data = 0;
phy_msg.dest_dev = hw->ptp.phy.eth56g.phy_addr[phy_idx];
err = ice_sbq_rw_reg(hw, &phy_msg);
err = ice_sbq_rw_reg(hw, &phy_msg, ICE_AQ_FLAG_RD);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "PTP failed to send msg to phy %d\n",
err);
......@@ -2855,7 +2855,7 @@ ice_read_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 offset, u32 *val)
ice_fill_phy_msg_e82x(hw, &msg, port, offset);
msg.opcode = ice_sbq_msg_rd;
err = ice_sbq_rw_reg(hw, &msg);
err = ice_sbq_rw_reg(hw, &msg, ICE_AQ_FLAG_RD);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
err);
......@@ -2933,7 +2933,7 @@ ice_write_phy_reg_e82x(struct ice_hw *hw, u8 port, u16 offset, u32 val)
msg.opcode = ice_sbq_msg_wr;
msg.data = val;
err = ice_sbq_rw_reg(hw, &msg);
err = ice_sbq_rw_reg(hw, &msg, ICE_AQ_FLAG_RD);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
err);
......@@ -3094,7 +3094,7 @@ ice_read_quad_reg_e82x(struct ice_hw *hw, u8 quad, u16 offset, u32 *val)
msg.opcode = ice_sbq_msg_rd;
err = ice_sbq_rw_reg(hw, &msg);
err = ice_sbq_rw_reg(hw, &msg, ICE_AQ_FLAG_RD);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
err);
......@@ -3129,7 +3129,7 @@ ice_write_quad_reg_e82x(struct ice_hw *hw, u8 quad, u16 offset, u32 val)
msg.opcode = ice_sbq_msg_wr;
msg.data = val;
err = ice_sbq_rw_reg(hw, &msg);
err = ice_sbq_rw_reg(hw, &msg, ICE_AQ_FLAG_RD);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
err);
......@@ -4780,7 +4780,7 @@ static int ice_read_phy_reg_e810(struct ice_hw *hw, u32 addr, u32 *val)
msg.opcode = ice_sbq_msg_rd;
msg.dest_dev = rmn_0;
err = ice_sbq_rw_reg(hw, &msg);
err = ice_sbq_rw_reg(hw, &msg, ICE_AQ_FLAG_RD);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
err);
......@@ -4811,7 +4811,7 @@ static int ice_write_phy_reg_e810(struct ice_hw *hw, u32 addr, u32 val)
msg.dest_dev = rmn_0;
msg.data = val;
err = ice_sbq_rw_reg(hw, &msg);
err = ice_sbq_rw_reg(hw, &msg, ICE_AQ_FLAG_RD);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to send message to PHY, err %d\n",
err);
......
......@@ -71,6 +71,14 @@ enum ice_aq_res_ids {
ICE_GLOBAL_CFG_LOCK_RES_ID
};
enum ice_fec_stats_types {
ICE_FEC_CORR_LOW,
ICE_FEC_CORR_HIGH,
ICE_FEC_UNCORR_LOW,
ICE_FEC_UNCORR_HIGH,
ICE_FEC_MAX
};
/* FW update timeout definitions are in milliseconds */
#define ICE_NVM_TIMEOUT 180000
#define ICE_CHANGE_LOCK_TIMEOUT 1000
......
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