mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 00:22:00 +02:00
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.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
58f9416d41
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -463,7 +463,354 @@ ice_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
|
|||
|
||||
static int ice_get_regs_len(struct net_device __always_unused *netdev)
|
||||
{
|
||||
return sizeof(ice_regs_dump_list);
|
||||
return (sizeof(ice_regs_dump_list) +
|
||||
sizeof(struct ice_regdump_to_ethtool));
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_ethtool_get_maxspeed - Get the max speed for given lport
|
||||
* @hw: pointer to the HW struct
|
||||
* @lport: logical port for which max speed is requested
|
||||
* @max_speed: return max speed for input lport
|
||||
*
|
||||
* Return: 0 on success, negative on failure.
|
||||
*/
|
||||
static int ice_ethtool_get_maxspeed(struct ice_hw *hw, u8 lport, u8 *max_speed)
|
||||
{
|
||||
struct ice_aqc_get_port_options_elem options[ICE_AQC_PORT_OPT_MAX] = {};
|
||||
bool active_valid = false, pending_valid = true;
|
||||
u8 option_count = ICE_AQC_PORT_OPT_MAX;
|
||||
u8 active_idx = 0, pending_idx = 0;
|
||||
int status;
|
||||
|
||||
status = ice_aq_get_port_options(hw, options, &option_count, lport,
|
||||
true, &active_idx, &active_valid,
|
||||
&pending_idx, &pending_valid);
|
||||
if (status)
|
||||
return -EIO;
|
||||
if (!active_valid)
|
||||
return -EINVAL;
|
||||
|
||||
*max_speed = options[active_idx].max_lane_speed & ICE_AQC_PORT_OPT_MAX_LANE_M;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_is_serdes_muxed - returns whether serdes is muxed in hardware
|
||||
* @hw: pointer to the HW struct
|
||||
*
|
||||
* Return: true when serdes is muxed, false when serdes is not muxed.
|
||||
*/
|
||||
static bool ice_is_serdes_muxed(struct ice_hw *hw)
|
||||
{
|
||||
u32 reg_value = rd32(hw, GLGEN_SWITCH_MODE_CONFIG);
|
||||
|
||||
return FIELD_GET(GLGEN_SWITCH_MODE_CONFIG_25X4_QUAD_M, reg_value);
|
||||
}
|
||||
|
||||
static int ice_map_port_topology_for_sfp(struct ice_port_topology *port_topology,
|
||||
u8 lport, bool is_muxed)
|
||||
{
|
||||
switch (lport) {
|
||||
case 0:
|
||||
port_topology->pcs_quad_select = 0;
|
||||
port_topology->pcs_port = 0;
|
||||
port_topology->primary_serdes_lane = 0;
|
||||
break;
|
||||
case 1:
|
||||
port_topology->pcs_quad_select = 1;
|
||||
port_topology->pcs_port = 0;
|
||||
if (is_muxed)
|
||||
port_topology->primary_serdes_lane = 2;
|
||||
else
|
||||
port_topology->primary_serdes_lane = 4;
|
||||
break;
|
||||
case 2:
|
||||
port_topology->pcs_quad_select = 0;
|
||||
port_topology->pcs_port = 1;
|
||||
port_topology->primary_serdes_lane = 1;
|
||||
break;
|
||||
case 3:
|
||||
port_topology->pcs_quad_select = 1;
|
||||
port_topology->pcs_port = 1;
|
||||
if (is_muxed)
|
||||
port_topology->primary_serdes_lane = 3;
|
||||
else
|
||||
port_topology->primary_serdes_lane = 5;
|
||||
break;
|
||||
case 4:
|
||||
port_topology->pcs_quad_select = 0;
|
||||
port_topology->pcs_port = 2;
|
||||
port_topology->primary_serdes_lane = 2;
|
||||
break;
|
||||
case 5:
|
||||
port_topology->pcs_quad_select = 1;
|
||||
port_topology->pcs_port = 2;
|
||||
port_topology->primary_serdes_lane = 6;
|
||||
break;
|
||||
case 6:
|
||||
port_topology->pcs_quad_select = 0;
|
||||
port_topology->pcs_port = 3;
|
||||
port_topology->primary_serdes_lane = 3;
|
||||
break;
|
||||
case 7:
|
||||
port_topology->pcs_quad_select = 1;
|
||||
port_topology->pcs_port = 3;
|
||||
port_topology->primary_serdes_lane = 7;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ice_map_port_topology_for_qsfp(struct ice_port_topology *port_topology,
|
||||
u8 lport, bool is_muxed)
|
||||
{
|
||||
switch (lport) {
|
||||
case 0:
|
||||
port_topology->pcs_quad_select = 0;
|
||||
port_topology->pcs_port = 0;
|
||||
port_topology->primary_serdes_lane = 0;
|
||||
break;
|
||||
case 1:
|
||||
port_topology->pcs_quad_select = 1;
|
||||
port_topology->pcs_port = 0;
|
||||
if (is_muxed)
|
||||
port_topology->primary_serdes_lane = 2;
|
||||
else
|
||||
port_topology->primary_serdes_lane = 4;
|
||||
break;
|
||||
case 2:
|
||||
port_topology->pcs_quad_select = 0;
|
||||
port_topology->pcs_port = 1;
|
||||
port_topology->primary_serdes_lane = 1;
|
||||
break;
|
||||
case 3:
|
||||
port_topology->pcs_quad_select = 1;
|
||||
port_topology->pcs_port = 1;
|
||||
if (is_muxed)
|
||||
port_topology->primary_serdes_lane = 3;
|
||||
else
|
||||
port_topology->primary_serdes_lane = 5;
|
||||
break;
|
||||
case 4:
|
||||
port_topology->pcs_quad_select = 0;
|
||||
port_topology->pcs_port = 2;
|
||||
port_topology->primary_serdes_lane = 2;
|
||||
break;
|
||||
case 5:
|
||||
port_topology->pcs_quad_select = 1;
|
||||
port_topology->pcs_port = 2;
|
||||
port_topology->primary_serdes_lane = 6;
|
||||
break;
|
||||
case 6:
|
||||
port_topology->pcs_quad_select = 0;
|
||||
port_topology->pcs_port = 3;
|
||||
port_topology->primary_serdes_lane = 3;
|
||||
break;
|
||||
case 7:
|
||||
port_topology->pcs_quad_select = 1;
|
||||
port_topology->pcs_port = 3;
|
||||
port_topology->primary_serdes_lane = 7;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_get_port_topology - returns physical topology like pcsquad, pcsport,
|
||||
* serdes number
|
||||
* @hw: pointer to the HW struct
|
||||
* @lport: logical port for which physical info requested
|
||||
* @port_topology: buffer to hold port topology
|
||||
*
|
||||
* Return: 0 on success, negative on failure.
|
||||
*/
|
||||
static int ice_get_port_topology(struct ice_hw *hw, u8 lport,
|
||||
struct ice_port_topology *port_topology)
|
||||
{
|
||||
struct ice_aqc_get_link_topo cmd = {};
|
||||
u16 node_handle = 0;
|
||||
u8 cage_type = 0;
|
||||
bool is_muxed;
|
||||
int err;
|
||||
u8 ctx;
|
||||
|
||||
ctx = ICE_AQC_LINK_TOPO_NODE_TYPE_CAGE << ICE_AQC_LINK_TOPO_NODE_TYPE_S;
|
||||
ctx |= ICE_AQC_LINK_TOPO_NODE_CTX_PORT << ICE_AQC_LINK_TOPO_NODE_CTX_S;
|
||||
cmd.addr.topo_params.node_type_ctx = ctx;
|
||||
|
||||
err = ice_aq_get_netlist_node(hw, &cmd, &cage_type, &node_handle);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
|
||||
is_muxed = ice_is_serdes_muxed(hw);
|
||||
|
||||
if (cage_type == 0x11 || /* SFP+ */
|
||||
cage_type == 0x12) { /* SFP28 */
|
||||
port_topology->serdes_lane_count = 1;
|
||||
err = ice_map_port_topology_for_sfp(port_topology, lport, is_muxed);
|
||||
if (err)
|
||||
return err;
|
||||
} else if (cage_type == 0x13 || /* QSFP */
|
||||
cage_type == 0x14) { /* QSFP28 */
|
||||
u8 max_speed = 0;
|
||||
|
||||
err = ice_ethtool_get_maxspeed(hw, lport, &max_speed);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (max_speed == ICE_AQC_PORT_OPT_MAX_LANE_100G)
|
||||
port_topology->serdes_lane_count = 4;
|
||||
else if (max_speed == ICE_AQC_PORT_OPT_MAX_LANE_50G)
|
||||
port_topology->serdes_lane_count = 2;
|
||||
else
|
||||
port_topology->serdes_lane_count = 1;
|
||||
|
||||
err = ice_map_port_topology_for_qsfp(port_topology, lport, is_muxed);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_get_tx_rx_equa - read serdes tx rx equaliser param
|
||||
* @hw: pointer to the HW struct
|
||||
* @serdes_num: represents the serdes number
|
||||
* @ptr: structure to read all serdes parameter for given serdes
|
||||
*
|
||||
* Return: all serdes equalization parameter supported per serdes number
|
||||
*/
|
||||
static int ice_get_tx_rx_equa(struct ice_hw *hw, u8 serdes_num,
|
||||
struct ice_serdes_equalization_to_ethtool *ptr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ice_aq_get_phy_equalization(hw, ICE_AQC_TX_EQU_PRE1,
|
||||
ICE_AQC_OP_CODE_TX_EQU, serdes_num,
|
||||
&ptr->tx_equalization_pre1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ice_aq_get_phy_equalization(hw, ICE_AQC_TX_EQU_PRE3,
|
||||
ICE_AQC_OP_CODE_TX_EQU, serdes_num,
|
||||
&ptr->tx_equalization_pre3);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ice_aq_get_phy_equalization(hw, ICE_AQC_TX_EQU_ATTEN,
|
||||
ICE_AQC_OP_CODE_TX_EQU, serdes_num,
|
||||
&ptr->tx_equalization_atten);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ice_aq_get_phy_equalization(hw, ICE_AQC_TX_EQU_POST1,
|
||||
ICE_AQC_OP_CODE_TX_EQU, serdes_num,
|
||||
&ptr->tx_equalization_post1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ice_aq_get_phy_equalization(hw, ICE_AQC_TX_EQU_PRE2,
|
||||
ICE_AQC_OP_CODE_TX_EQU, serdes_num,
|
||||
&ptr->tx_equalization_pre2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ice_aq_get_phy_equalization(hw, ICE_AQC_RX_EQU_PRE2,
|
||||
ICE_AQC_OP_CODE_RX_EQU, serdes_num,
|
||||
&ptr->rx_equalization_pre2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ice_aq_get_phy_equalization(hw, ICE_AQC_RX_EQU_PRE1,
|
||||
ICE_AQC_OP_CODE_RX_EQU, serdes_num,
|
||||
&ptr->rx_equalization_pre1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ice_aq_get_phy_equalization(hw, ICE_AQC_RX_EQU_POST1,
|
||||
ICE_AQC_OP_CODE_RX_EQU, serdes_num,
|
||||
&ptr->rx_equalization_post1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ice_aq_get_phy_equalization(hw, ICE_AQC_RX_EQU_BFLF,
|
||||
ICE_AQC_OP_CODE_RX_EQU, serdes_num,
|
||||
&ptr->rx_equalization_bflf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ice_aq_get_phy_equalization(hw, ICE_AQC_RX_EQU_BFHF,
|
||||
ICE_AQC_OP_CODE_RX_EQU, serdes_num,
|
||||
&ptr->rx_equalization_bfhf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ice_aq_get_phy_equalization(hw, ICE_AQC_RX_EQU_DRATE,
|
||||
ICE_AQC_OP_CODE_RX_EQU, serdes_num,
|
||||
&ptr->rx_equalization_drate);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_get_extended_regs - returns FEC correctable, uncorrectable stats per
|
||||
* pcsquad, pcsport
|
||||
* @netdev: pointer to net device structure
|
||||
* @p: output buffer to fill requested register dump
|
||||
*
|
||||
* Return: 0 on success, negative on failure.
|
||||
*/
|
||||
static int ice_get_extended_regs(struct net_device *netdev, void *p)
|
||||
{
|
||||
struct ice_netdev_priv *np = netdev_priv(netdev);
|
||||
struct ice_regdump_to_ethtool *ice_prv_regs_buf;
|
||||
struct ice_port_topology port_topology = {};
|
||||
struct ice_port_info *pi;
|
||||
struct ice_pf *pf;
|
||||
struct ice_hw *hw;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
pf = np->vsi->back;
|
||||
hw = &pf->hw;
|
||||
pi = np->vsi->port_info;
|
||||
|
||||
/* Serdes parameters are not supported if not the PF VSI */
|
||||
if (np->vsi->type != ICE_VSI_PF || !pi)
|
||||
return -EINVAL;
|
||||
|
||||
err = ice_get_port_topology(hw, pi->lport, &port_topology);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
if (port_topology.serdes_lane_count > 4)
|
||||
return -EINVAL;
|
||||
|
||||
ice_prv_regs_buf = p;
|
||||
|
||||
/* Get serdes equalization parameter for available serdes */
|
||||
for (i = 0; i < port_topology.serdes_lane_count; i++) {
|
||||
u8 serdes_num = 0;
|
||||
|
||||
serdes_num = port_topology.primary_serdes_lane + i;
|
||||
err = ice_get_tx_rx_equa(hw, serdes_num,
|
||||
&ice_prv_regs_buf->equalization[i]);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -475,10 +822,12 @@ ice_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p)
|
|||
u32 *regs_buf = (u32 *)p;
|
||||
unsigned int i;
|
||||
|
||||
regs->version = 1;
|
||||
regs->version = 2;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ice_regs_dump_list); ++i)
|
||||
regs_buf[i] = rd32(hw, ice_regs_dump_list[i]);
|
||||
|
||||
ice_get_extended_regs(netdev, (void *)®s_buf[i]);
|
||||
}
|
||||
|
||||
static u32 ice_get_msglevel(struct net_device *netdev)
|
||||
|
|
@ -4282,6 +4631,94 @@ ice_get_module_eeprom(struct net_device *netdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_get_port_fec_stats - returns FEC correctable, uncorrectable stats per
|
||||
* pcsquad, pcsport
|
||||
* @hw: pointer to the HW struct
|
||||
* @pcs_quad: pcsquad for input port
|
||||
* @pcs_port: pcsport for input port
|
||||
* @fec_stats: buffer to hold FEC statistics for given port
|
||||
*
|
||||
* Return: 0 on success, negative on failure.
|
||||
*/
|
||||
static int ice_get_port_fec_stats(struct ice_hw *hw, u16 pcs_quad, u16 pcs_port,
|
||||
struct ethtool_fec_stats *fec_stats)
|
||||
{
|
||||
u32 fec_uncorr_low_val = 0, fec_uncorr_high_val = 0;
|
||||
u32 fec_corr_low_val = 0, fec_corr_high_val = 0;
|
||||
int err;
|
||||
|
||||
if (pcs_quad > 1 || pcs_port > 3)
|
||||
return -EINVAL;
|
||||
|
||||
err = ice_aq_get_fec_stats(hw, pcs_quad, pcs_port, ICE_FEC_CORR_LOW,
|
||||
&fec_corr_low_val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ice_aq_get_fec_stats(hw, pcs_quad, pcs_port, ICE_FEC_CORR_HIGH,
|
||||
&fec_corr_high_val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ice_aq_get_fec_stats(hw, pcs_quad, pcs_port,
|
||||
ICE_FEC_UNCORR_LOW,
|
||||
&fec_uncorr_low_val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ice_aq_get_fec_stats(hw, pcs_quad, pcs_port,
|
||||
ICE_FEC_UNCORR_HIGH,
|
||||
&fec_uncorr_high_val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
fec_stats->uncorrectable_blocks.total = (fec_corr_high_val << 16) +
|
||||
fec_corr_low_val;
|
||||
fec_stats->corrected_blocks.total = (fec_uncorr_high_val << 16) +
|
||||
fec_uncorr_low_val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_get_fec_stats - returns FEC correctable, uncorrectable stats per netdev
|
||||
* @netdev: network interface device structure
|
||||
* @fec_stats: buffer to hold FEC statistics for given port
|
||||
*
|
||||
*/
|
||||
static void ice_get_fec_stats(struct net_device *netdev,
|
||||
struct ethtool_fec_stats *fec_stats)
|
||||
{
|
||||
struct ice_netdev_priv *np = netdev_priv(netdev);
|
||||
struct ice_port_topology port_topology;
|
||||
struct ice_port_info *pi;
|
||||
struct ice_pf *pf;
|
||||
struct ice_hw *hw;
|
||||
int err;
|
||||
|
||||
pf = np->vsi->back;
|
||||
hw = &pf->hw;
|
||||
pi = np->vsi->port_info;
|
||||
|
||||
/* Serdes parameters are not supported if not the PF VSI */
|
||||
if (np->vsi->type != ICE_VSI_PF || !pi)
|
||||
return;
|
||||
|
||||
err = ice_get_port_topology(hw, pi->lport, &port_topology);
|
||||
if (err) {
|
||||
netdev_info(netdev, "Extended register dump failed Lport %d\n",
|
||||
pi->lport);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get FEC correctable, uncorrectable counter */
|
||||
err = ice_get_port_fec_stats(hw, port_topology.pcs_quad_select,
|
||||
port_topology.pcs_port, fec_stats);
|
||||
if (err)
|
||||
netdev_info(netdev, "FEC stats get failed Lport %d Err %d\n",
|
||||
pi->lport, err);
|
||||
}
|
||||
|
||||
static const struct ethtool_ops ice_ethtool_ops = {
|
||||
.cap_rss_ctx_supported = true,
|
||||
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
|
||||
|
|
@ -4290,6 +4727,7 @@ static const struct ethtool_ops ice_ethtool_ops = {
|
|||
.cap_rss_sym_xor_supported = true,
|
||||
.get_link_ksettings = ice_get_link_ksettings,
|
||||
.set_link_ksettings = ice_set_link_ksettings,
|
||||
.get_fec_stats = ice_get_fec_stats,
|
||||
.get_drvinfo = ice_get_drvinfo,
|
||||
.get_regs_len = ice_get_regs_len,
|
||||
.get_regs = ice_get_regs,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user