Implement .get_down_ext_state() as part of ethtool_ops. Query link down reason from PDDR register and convert it to ethtool ext_state.
In case that more information than common ext_state is provided, fill ext_substate also with the appropriate value.
Signed-off-by: Amit Cohen amitc@mellanox.com Reviewed-by: Petr Machata petrm@mellanox.com Reviewed-by: Jiri Pirko jiri@mellanox.com --- .../mellanox/mlxsw/spectrum_ethtool.c | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c index 04e1db604c69..ca0bfd07aab6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c @@ -26,6 +26,147 @@ static void mlxsw_sp_port_get_drvinfo(struct net_device *dev, sizeof(drvinfo->bus_info)); }
+struct mlxsw_sp_ethtool_ext_state_opcode_mapping { + u32 status_opcode; + enum ethtool_ext_state ext_state; + int ext_substate; +}; + +static const struct mlxsw_sp_ethtool_ext_state_opcode_mapping +mlxsw_sp_ext_state_opcode_map[] = { + {2, ETHTOOL_EXT_STATE_AUTONEG_FAILURE, + ETHTOOL_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED}, + {3, ETHTOOL_EXT_STATE_AUTONEG_FAILURE, + ETHTOOL_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED}, + {4, ETHTOOL_EXT_STATE_AUTONEG_FAILURE, + ETHTOOL_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED}, + {36, ETHTOOL_EXT_STATE_AUTONEG_FAILURE, + ETHTOOL_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE}, + {38, ETHTOOL_EXT_STATE_AUTONEG_FAILURE, + ETHTOOL_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE}, + {39, ETHTOOL_EXT_STATE_AUTONEG_FAILURE, + ETHTOOL_EXT_SUBSTATE_AN_NO_HCD}, + + {5, ETHTOOL_EXT_STATE_LINK_TRAINING_FAILURE, + ETHTOOL_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED}, + {6, ETHTOOL_EXT_STATE_LINK_TRAINING_FAILURE, + ETHTOOL_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT}, + {7, ETHTOOL_EXT_STATE_LINK_TRAINING_FAILURE, + ETHTOOL_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY}, + {8, ETHTOOL_EXT_STATE_LINK_TRAINING_FAILURE, 0}, + {14, ETHTOOL_EXT_STATE_LINK_TRAINING_FAILURE, + ETHTOOL_EXT_SUBSTATE_LT_REMOTE_FAULT}, + + {9, ETHTOOL_EXT_STATE_LINK_LOGICAL_MISMATCH, + ETHTOOL_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK}, + {10, ETHTOOL_EXT_STATE_LINK_LOGICAL_MISMATCH, + ETHTOOL_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK}, + {11, ETHTOOL_EXT_STATE_LINK_LOGICAL_MISMATCH, + ETHTOOL_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS}, + {12, ETHTOOL_EXT_STATE_LINK_LOGICAL_MISMATCH, + ETHTOOL_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED}, + {13, ETHTOOL_EXT_STATE_LINK_LOGICAL_MISMATCH, + ETHTOOL_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED}, + + {15, ETHTOOL_EXT_STATE_BAD_SIGNAL_INTEGRITY, 0}, + {17, ETHTOOL_EXT_STATE_BAD_SIGNAL_INTEGRITY, + ETHTOOL_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS}, + {42, ETHTOOL_EXT_STATE_BAD_SIGNAL_INTEGRITY, + ETHTOOL_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE}, + + {1024, ETHTOOL_EXT_STATE_NO_CABLE, 0}, + + {16, ETHTOOL_EXT_STATE_CABLE_ISSUE, + ETHTOOL_EXT_SUBSTATE_UNSUPPORTED_CABLE}, + {20, ETHTOOL_EXT_STATE_CABLE_ISSUE, + ETHTOOL_EXT_SUBSTATE_UNSUPPORTED_CABLE}, + {29, ETHTOOL_EXT_STATE_CABLE_ISSUE, + ETHTOOL_EXT_SUBSTATE_UNSUPPORTED_CABLE}, + {1025, ETHTOOL_EXT_STATE_CABLE_ISSUE, + ETHTOOL_EXT_SUBSTATE_UNSUPPORTED_CABLE}, + {1029, ETHTOOL_EXT_STATE_CABLE_ISSUE, + ETHTOOL_EXT_SUBSTATE_UNSUPPORTED_CABLE}, + {1031, ETHTOOL_EXT_STATE_CABLE_ISSUE, + ETHTOOL_EXT_SUBSTATE_SHORTED_CABLE}, + + {1027, ETHTOOL_EXT_STATE_EEPROM_ISSUE, 0}, + + {23, ETHTOOL_EXT_STATE_CALIBRATION_FAILURE, 0}, + + {1032, ETHTOOL_EXT_STATE_POWER_BUDGET_EXCEEDED, 0}, + + {1030, ETHTOOL_EXT_STATE_OVERHEAT, 0}, +}; + +static void +mlxsw_sp_port_set_ext_state(struct mlxsw_sp_ethtool_ext_state_opcode_mapping ext_state_mapping, + struct ethtool_ext_state_info *ext_state_info) +{ + switch (ext_state_mapping.ext_state) { + case ETHTOOL_EXT_STATE_AUTONEG_FAILURE: + ext_state_info->autoneg = + ext_state_mapping.ext_substate; + break; + case ETHTOOL_EXT_STATE_LINK_TRAINING_FAILURE: + ext_state_info->link_training = + ext_state_mapping.ext_substate; + break; + case ETHTOOL_EXT_STATE_LINK_LOGICAL_MISMATCH: + ext_state_info->link_logical_mismatch = + ext_state_mapping.ext_substate; + break; + case ETHTOOL_EXT_STATE_BAD_SIGNAL_INTEGRITY: + ext_state_info->bad_signal_integrity = + ext_state_mapping.ext_substate; + break; + case ETHTOOL_EXT_STATE_CABLE_ISSUE: + ext_state_info->cable_issue = + ext_state_mapping.ext_substate; + break; + default: + break; + } + + ext_state_info->ext_state = ext_state_mapping.ext_state; +} + +static int +mlxsw_sp_port_get_ext_state(struct net_device *dev, + struct ethtool_ext_state_info *ext_state_info) +{ + struct mlxsw_sp_ethtool_ext_state_opcode_mapping ext_state_mapping; + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + char pddr_pl[MLXSW_REG_PDDR_LEN]; + int opcode, err, i; + u32 status_opcode; + + mlxsw_reg_pddr_pack(pddr_pl, mlxsw_sp_port->local_port, + MLXSW_REG_PDDR_PAGE_SELECT_TROUBLESHOOTING_INFO); + + opcode = MLXSW_REG_PDDR_TRBLSH_GROUP_OPCODE_MONITOR; + mlxsw_reg_pddr_trblsh_group_opcode_set(pddr_pl, opcode); + + err = mlxsw_reg_query(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pddr), + pddr_pl); + if (err) + return err; + + status_opcode = mlxsw_reg_pddr_trblsh_status_opcode_get(pddr_pl); + if (!status_opcode) + return -ENODATA; + + for (i = 0; i < ARRAY_SIZE(mlxsw_sp_ext_state_opcode_map); i++) { + ext_state_mapping = mlxsw_sp_ext_state_opcode_map[i]; + if (ext_state_mapping.status_opcode == status_opcode) { + mlxsw_sp_port_set_ext_state(ext_state_mapping, + ext_state_info); + return 0; + } + } + + return -ENODATA; +} + static void mlxsw_sp_port_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause) { @@ -827,6 +968,7 @@ mlxsw_sp_get_ts_info(struct net_device *netdev, struct ethtool_ts_info *info) const struct ethtool_ops mlxsw_sp_port_ethtool_ops = { .get_drvinfo = mlxsw_sp_port_get_drvinfo, .get_link = ethtool_op_get_link, + .get_ext_state = mlxsw_sp_port_get_ext_state, .get_pauseparam = mlxsw_sp_port_get_pauseparam, .set_pauseparam = mlxsw_sp_port_set_pauseparam, .get_strings = mlxsw_sp_port_get_strings,