| // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
| /* Copyright (c) 2020 Mellanox Technologies. All rights reserved */ |
| |
| #include "reg.h" |
| #include "core.h" |
| #include "spectrum.h" |
| #include "core_env.h" |
| |
| static const char mlxsw_sp_driver_version[] = "1.0"; |
| |
| static void mlxsw_sp_port_get_drvinfo(struct net_device *dev, |
| struct ethtool_drvinfo *drvinfo) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| |
| strlcpy(drvinfo->driver, mlxsw_sp->bus_info->device_kind, |
| sizeof(drvinfo->driver)); |
| strlcpy(drvinfo->version, mlxsw_sp_driver_version, |
| sizeof(drvinfo->version)); |
| snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), |
| "%d.%d.%d", |
| mlxsw_sp->bus_info->fw_rev.major, |
| mlxsw_sp->bus_info->fw_rev.minor, |
| mlxsw_sp->bus_info->fw_rev.subminor); |
| strlcpy(drvinfo->bus_info, mlxsw_sp->bus_info->device_name, |
| sizeof(drvinfo->bus_info)); |
| } |
| |
| struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping { |
| u32 status_opcode; |
| enum ethtool_link_ext_state link_ext_state; |
| u8 link_ext_substate; |
| }; |
| |
| static const struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping |
| mlxsw_sp_link_ext_state_opcode_map[] = { |
| {2, ETHTOOL_LINK_EXT_STATE_AUTONEG, |
| ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED}, |
| {3, ETHTOOL_LINK_EXT_STATE_AUTONEG, |
| ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED}, |
| {4, ETHTOOL_LINK_EXT_STATE_AUTONEG, |
| ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED}, |
| {36, ETHTOOL_LINK_EXT_STATE_AUTONEG, |
| ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE}, |
| {38, ETHTOOL_LINK_EXT_STATE_AUTONEG, |
| ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE}, |
| {39, ETHTOOL_LINK_EXT_STATE_AUTONEG, |
| ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD}, |
| |
| {5, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE, |
| ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED}, |
| {6, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE, |
| ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT}, |
| {7, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE, |
| ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY}, |
| {8, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE, 0}, |
| {14, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE, |
| ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT}, |
| |
| {9, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH, |
| ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK}, |
| {10, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH, |
| ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK}, |
| {11, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH, |
| ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS}, |
| {12, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH, |
| ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED}, |
| {13, ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH, |
| ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED}, |
| |
| {15, ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY, 0}, |
| {17, ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY, |
| ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS}, |
| {42, ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY, |
| ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE}, |
| |
| {1024, ETHTOOL_LINK_EXT_STATE_NO_CABLE, 0}, |
| |
| {16, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE, |
| ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE}, |
| {20, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE, |
| ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE}, |
| {29, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE, |
| ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE}, |
| {1025, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE, |
| ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE}, |
| {1029, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE, |
| ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE}, |
| {1031, ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE, 0}, |
| |
| {1027, ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE, 0}, |
| |
| {23, ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE, 0}, |
| |
| {1032, ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED, 0}, |
| |
| {1030, ETHTOOL_LINK_EXT_STATE_OVERHEAT, 0}, |
| |
| {1042, ETHTOOL_LINK_EXT_STATE_MODULE, |
| ETHTOOL_LINK_EXT_SUBSTATE_MODULE_CMIS_NOT_READY}, |
| }; |
| |
| static void |
| mlxsw_sp_port_set_link_ext_state(struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping |
| link_ext_state_mapping, |
| struct ethtool_link_ext_state_info *link_ext_state_info) |
| { |
| switch (link_ext_state_mapping.link_ext_state) { |
| case ETHTOOL_LINK_EXT_STATE_AUTONEG: |
| link_ext_state_info->autoneg = |
| link_ext_state_mapping.link_ext_substate; |
| break; |
| case ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE: |
| link_ext_state_info->link_training = |
| link_ext_state_mapping.link_ext_substate; |
| break; |
| case ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH: |
| link_ext_state_info->link_logical_mismatch = |
| link_ext_state_mapping.link_ext_substate; |
| break; |
| case ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY: |
| link_ext_state_info->bad_signal_integrity = |
| link_ext_state_mapping.link_ext_substate; |
| break; |
| case ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE: |
| link_ext_state_info->cable_issue = |
| link_ext_state_mapping.link_ext_substate; |
| break; |
| case ETHTOOL_LINK_EXT_STATE_MODULE: |
| link_ext_state_info->module = |
| link_ext_state_mapping.link_ext_substate; |
| break; |
| default: |
| break; |
| } |
| |
| link_ext_state_info->link_ext_state = link_ext_state_mapping.link_ext_state; |
| } |
| |
| static int |
| mlxsw_sp_port_get_link_ext_state(struct net_device *dev, |
| struct ethtool_link_ext_state_info *link_ext_state_info) |
| { |
| struct mlxsw_sp_ethtool_link_ext_state_opcode_mapping link_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; |
| |
| if (netif_carrier_ok(dev)) |
| return -ENODATA; |
| |
| 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_link_ext_state_opcode_map); i++) { |
| link_ext_state_mapping = mlxsw_sp_link_ext_state_opcode_map[i]; |
| if (link_ext_state_mapping.status_opcode == status_opcode) { |
| mlxsw_sp_port_set_link_ext_state(link_ext_state_mapping, |
| link_ext_state_info); |
| return 0; |
| } |
| } |
| |
| return -ENODATA; |
| } |
| |
| static void mlxsw_sp_port_get_pauseparam(struct net_device *dev, |
| struct ethtool_pauseparam *pause) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| |
| pause->rx_pause = mlxsw_sp_port->link.rx_pause; |
| pause->tx_pause = mlxsw_sp_port->link.tx_pause; |
| } |
| |
| static int mlxsw_sp_port_pause_set(struct mlxsw_sp_port *mlxsw_sp_port, |
| struct ethtool_pauseparam *pause) |
| { |
| char pfcc_pl[MLXSW_REG_PFCC_LEN]; |
| |
| mlxsw_reg_pfcc_pack(pfcc_pl, mlxsw_sp_port->local_port); |
| mlxsw_reg_pfcc_pprx_set(pfcc_pl, pause->rx_pause); |
| mlxsw_reg_pfcc_pptx_set(pfcc_pl, pause->tx_pause); |
| |
| return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pfcc), |
| pfcc_pl); |
| } |
| |
| /* Maximum delay buffer needed in case of PAUSE frames. Similar to PFC delay, but is |
| * measured in bytes. Assumes 100m cable and does not take into account MTU. |
| */ |
| #define MLXSW_SP_PAUSE_DELAY_BYTES 19476 |
| |
| static int mlxsw_sp_port_set_pauseparam(struct net_device *dev, |
| struct ethtool_pauseparam *pause) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| bool pause_en = pause->tx_pause || pause->rx_pause; |
| struct mlxsw_sp_hdroom orig_hdroom; |
| struct mlxsw_sp_hdroom hdroom; |
| int prio; |
| int err; |
| |
| if (mlxsw_sp_port->dcb.pfc && mlxsw_sp_port->dcb.pfc->pfc_en) { |
| netdev_err(dev, "PFC already enabled on port\n"); |
| return -EINVAL; |
| } |
| |
| if (pause->autoneg) { |
| netdev_err(dev, "PAUSE frames autonegotiation isn't supported\n"); |
| return -EINVAL; |
| } |
| |
| orig_hdroom = *mlxsw_sp_port->hdroom; |
| |
| hdroom = orig_hdroom; |
| if (pause_en) |
| hdroom.delay_bytes = MLXSW_SP_PAUSE_DELAY_BYTES; |
| else |
| hdroom.delay_bytes = 0; |
| |
| for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++) |
| hdroom.prios.prio[prio].lossy = !pause_en; |
| |
| mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom); |
| mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom); |
| |
| err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom); |
| if (err) { |
| netdev_err(dev, "Failed to configure port's headroom\n"); |
| return err; |
| } |
| |
| err = mlxsw_sp_port_pause_set(mlxsw_sp_port, pause); |
| if (err) { |
| netdev_err(dev, "Failed to set PAUSE parameters\n"); |
| goto err_port_pause_configure; |
| } |
| |
| mlxsw_sp_port->link.rx_pause = pause->rx_pause; |
| mlxsw_sp_port->link.tx_pause = pause->tx_pause; |
| |
| return 0; |
| |
| err_port_pause_configure: |
| mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom); |
| return err; |
| } |
| |
| struct mlxsw_sp_port_hw_stats { |
| char str[ETH_GSTRING_LEN]; |
| u64 (*getter)(const char *payload); |
| bool cells_bytes; |
| }; |
| |
| static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = { |
| { |
| .str = "a_frames_transmitted_ok", |
| .getter = mlxsw_reg_ppcnt_a_frames_transmitted_ok_get, |
| }, |
| { |
| .str = "a_frames_received_ok", |
| .getter = mlxsw_reg_ppcnt_a_frames_received_ok_get, |
| }, |
| { |
| .str = "a_frame_check_sequence_errors", |
| .getter = mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get, |
| }, |
| { |
| .str = "a_alignment_errors", |
| .getter = mlxsw_reg_ppcnt_a_alignment_errors_get, |
| }, |
| { |
| .str = "a_octets_transmitted_ok", |
| .getter = mlxsw_reg_ppcnt_a_octets_transmitted_ok_get, |
| }, |
| { |
| .str = "a_octets_received_ok", |
| .getter = mlxsw_reg_ppcnt_a_octets_received_ok_get, |
| }, |
| { |
| .str = "a_multicast_frames_xmitted_ok", |
| .getter = mlxsw_reg_ppcnt_a_multicast_frames_xmitted_ok_get, |
| }, |
| { |
| .str = "a_broadcast_frames_xmitted_ok", |
| .getter = mlxsw_reg_ppcnt_a_broadcast_frames_xmitted_ok_get, |
| }, |
| { |
| .str = "a_multicast_frames_received_ok", |
| .getter = mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get, |
| }, |
| { |
| .str = "a_broadcast_frames_received_ok", |
| .getter = mlxsw_reg_ppcnt_a_broadcast_frames_received_ok_get, |
| }, |
| { |
| .str = "a_in_range_length_errors", |
| .getter = mlxsw_reg_ppcnt_a_in_range_length_errors_get, |
| }, |
| { |
| .str = "a_out_of_range_length_field", |
| .getter = mlxsw_reg_ppcnt_a_out_of_range_length_field_get, |
| }, |
| { |
| .str = "a_frame_too_long_errors", |
| .getter = mlxsw_reg_ppcnt_a_frame_too_long_errors_get, |
| }, |
| { |
| .str = "a_symbol_error_during_carrier", |
| .getter = mlxsw_reg_ppcnt_a_symbol_error_during_carrier_get, |
| }, |
| { |
| .str = "a_mac_control_frames_transmitted", |
| .getter = mlxsw_reg_ppcnt_a_mac_control_frames_transmitted_get, |
| }, |
| { |
| .str = "a_mac_control_frames_received", |
| .getter = mlxsw_reg_ppcnt_a_mac_control_frames_received_get, |
| }, |
| { |
| .str = "a_unsupported_opcodes_received", |
| .getter = mlxsw_reg_ppcnt_a_unsupported_opcodes_received_get, |
| }, |
| { |
| .str = "a_pause_mac_ctrl_frames_received", |
| .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_received_get, |
| }, |
| { |
| .str = "a_pause_mac_ctrl_frames_xmitted", |
| .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_transmitted_get, |
| }, |
| }; |
| |
| #define MLXSW_SP_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_stats) |
| |
| static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2863_stats[] = { |
| { |
| .str = "if_in_discards", |
| .getter = mlxsw_reg_ppcnt_if_in_discards_get, |
| }, |
| { |
| .str = "if_out_discards", |
| .getter = mlxsw_reg_ppcnt_if_out_discards_get, |
| }, |
| { |
| .str = "if_out_errors", |
| .getter = mlxsw_reg_ppcnt_if_out_errors_get, |
| }, |
| }; |
| |
| #define MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN \ |
| ARRAY_SIZE(mlxsw_sp_port_hw_rfc_2863_stats) |
| |
| static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_2819_stats[] = { |
| { |
| .str = "ether_stats_undersize_pkts", |
| .getter = mlxsw_reg_ppcnt_ether_stats_undersize_pkts_get, |
| }, |
| { |
| .str = "ether_stats_oversize_pkts", |
| .getter = mlxsw_reg_ppcnt_ether_stats_oversize_pkts_get, |
| }, |
| { |
| .str = "ether_stats_fragments", |
| .getter = mlxsw_reg_ppcnt_ether_stats_fragments_get, |
| }, |
| { |
| .str = "ether_pkts64octets", |
| .getter = mlxsw_reg_ppcnt_ether_stats_pkts64octets_get, |
| }, |
| { |
| .str = "ether_pkts65to127octets", |
| .getter = mlxsw_reg_ppcnt_ether_stats_pkts65to127octets_get, |
| }, |
| { |
| .str = "ether_pkts128to255octets", |
| .getter = mlxsw_reg_ppcnt_ether_stats_pkts128to255octets_get, |
| }, |
| { |
| .str = "ether_pkts256to511octets", |
| .getter = mlxsw_reg_ppcnt_ether_stats_pkts256to511octets_get, |
| }, |
| { |
| .str = "ether_pkts512to1023octets", |
| .getter = mlxsw_reg_ppcnt_ether_stats_pkts512to1023octets_get, |
| }, |
| { |
| .str = "ether_pkts1024to1518octets", |
| .getter = mlxsw_reg_ppcnt_ether_stats_pkts1024to1518octets_get, |
| }, |
| { |
| .str = "ether_pkts1519to2047octets", |
| .getter = mlxsw_reg_ppcnt_ether_stats_pkts1519to2047octets_get, |
| }, |
| { |
| .str = "ether_pkts2048to4095octets", |
| .getter = mlxsw_reg_ppcnt_ether_stats_pkts2048to4095octets_get, |
| }, |
| { |
| .str = "ether_pkts4096to8191octets", |
| .getter = mlxsw_reg_ppcnt_ether_stats_pkts4096to8191octets_get, |
| }, |
| { |
| .str = "ether_pkts8192to10239octets", |
| .getter = mlxsw_reg_ppcnt_ether_stats_pkts8192to10239octets_get, |
| }, |
| }; |
| |
| #define MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN \ |
| ARRAY_SIZE(mlxsw_sp_port_hw_rfc_2819_stats) |
| |
| static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_rfc_3635_stats[] = { |
| { |
| .str = "dot3stats_fcs_errors", |
| .getter = mlxsw_reg_ppcnt_dot3stats_fcs_errors_get, |
| }, |
| { |
| .str = "dot3stats_symbol_errors", |
| .getter = mlxsw_reg_ppcnt_dot3stats_symbol_errors_get, |
| }, |
| { |
| .str = "dot3control_in_unknown_opcodes", |
| .getter = mlxsw_reg_ppcnt_dot3control_in_unknown_opcodes_get, |
| }, |
| { |
| .str = "dot3in_pause_frames", |
| .getter = mlxsw_reg_ppcnt_dot3in_pause_frames_get, |
| }, |
| }; |
| |
| #define MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN \ |
| ARRAY_SIZE(mlxsw_sp_port_hw_rfc_3635_stats) |
| |
| static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_ext_stats[] = { |
| { |
| .str = "ecn_marked", |
| .getter = mlxsw_reg_ppcnt_ecn_marked_get, |
| }, |
| }; |
| |
| #define MLXSW_SP_PORT_HW_EXT_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_ext_stats) |
| |
| static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_discard_stats[] = { |
| { |
| .str = "discard_ingress_general", |
| .getter = mlxsw_reg_ppcnt_ingress_general_get, |
| }, |
| { |
| .str = "discard_ingress_policy_engine", |
| .getter = mlxsw_reg_ppcnt_ingress_policy_engine_get, |
| }, |
| { |
| .str = "discard_ingress_vlan_membership", |
| .getter = mlxsw_reg_ppcnt_ingress_vlan_membership_get, |
| }, |
| { |
| .str = "discard_ingress_tag_frame_type", |
| .getter = mlxsw_reg_ppcnt_ingress_tag_frame_type_get, |
| }, |
| { |
| .str = "discard_egress_vlan_membership", |
| .getter = mlxsw_reg_ppcnt_egress_vlan_membership_get, |
| }, |
| { |
| .str = "discard_loopback_filter", |
| .getter = mlxsw_reg_ppcnt_loopback_filter_get, |
| }, |
| { |
| .str = "discard_egress_general", |
| .getter = mlxsw_reg_ppcnt_egress_general_get, |
| }, |
| { |
| .str = "discard_egress_hoq", |
| .getter = mlxsw_reg_ppcnt_egress_hoq_get, |
| }, |
| { |
| .str = "discard_egress_policy_engine", |
| .getter = mlxsw_reg_ppcnt_egress_policy_engine_get, |
| }, |
| { |
| .str = "discard_ingress_tx_link_down", |
| .getter = mlxsw_reg_ppcnt_ingress_tx_link_down_get, |
| }, |
| { |
| .str = "discard_egress_stp_filter", |
| .getter = mlxsw_reg_ppcnt_egress_stp_filter_get, |
| }, |
| { |
| .str = "discard_egress_sll", |
| .getter = mlxsw_reg_ppcnt_egress_sll_get, |
| }, |
| }; |
| |
| #define MLXSW_SP_PORT_HW_DISCARD_STATS_LEN \ |
| ARRAY_SIZE(mlxsw_sp_port_hw_discard_stats) |
| |
| static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_prio_stats[] = { |
| { |
| .str = "rx_octets_prio", |
| .getter = mlxsw_reg_ppcnt_rx_octets_get, |
| }, |
| { |
| .str = "rx_frames_prio", |
| .getter = mlxsw_reg_ppcnt_rx_frames_get, |
| }, |
| { |
| .str = "tx_octets_prio", |
| .getter = mlxsw_reg_ppcnt_tx_octets_get, |
| }, |
| { |
| .str = "tx_frames_prio", |
| .getter = mlxsw_reg_ppcnt_tx_frames_get, |
| }, |
| { |
| .str = "rx_pause_prio", |
| .getter = mlxsw_reg_ppcnt_rx_pause_get, |
| }, |
| { |
| .str = "rx_pause_duration_prio", |
| .getter = mlxsw_reg_ppcnt_rx_pause_duration_get, |
| }, |
| { |
| .str = "tx_pause_prio", |
| .getter = mlxsw_reg_ppcnt_tx_pause_get, |
| }, |
| { |
| .str = "tx_pause_duration_prio", |
| .getter = mlxsw_reg_ppcnt_tx_pause_duration_get, |
| }, |
| }; |
| |
| #define MLXSW_SP_PORT_HW_PRIO_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_prio_stats) |
| |
| static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_tc_stats[] = { |
| { |
| .str = "tc_transmit_queue_tc", |
| .getter = mlxsw_reg_ppcnt_tc_transmit_queue_get, |
| .cells_bytes = true, |
| }, |
| { |
| .str = "tc_no_buffer_discard_uc_tc", |
| .getter = mlxsw_reg_ppcnt_tc_no_buffer_discard_uc_get, |
| }, |
| }; |
| |
| #define MLXSW_SP_PORT_HW_TC_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_tc_stats) |
| |
| struct mlxsw_sp_port_stats { |
| char str[ETH_GSTRING_LEN]; |
| u64 (*getter)(struct mlxsw_sp_port *mlxsw_sp_port); |
| }; |
| |
| static u64 |
| mlxsw_sp_port_get_transceiver_overheat_stats(struct mlxsw_sp_port *mlxsw_sp_port) |
| { |
| struct mlxsw_sp_port_mapping port_mapping = mlxsw_sp_port->mapping; |
| struct mlxsw_core *mlxsw_core = mlxsw_sp_port->mlxsw_sp->core; |
| u64 stats; |
| int err; |
| |
| err = mlxsw_env_module_overheat_counter_get(mlxsw_core, |
| port_mapping.module, |
| &stats); |
| if (err) |
| return mlxsw_sp_port->module_overheat_initial_val; |
| |
| return stats - mlxsw_sp_port->module_overheat_initial_val; |
| } |
| |
| static struct mlxsw_sp_port_stats mlxsw_sp_port_transceiver_stats[] = { |
| { |
| .str = "transceiver_overheat", |
| .getter = mlxsw_sp_port_get_transceiver_overheat_stats, |
| }, |
| }; |
| |
| #define MLXSW_SP_PORT_HW_TRANSCEIVER_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_transceiver_stats) |
| |
| #define MLXSW_SP_PORT_ETHTOOL_STATS_LEN (MLXSW_SP_PORT_HW_STATS_LEN + \ |
| MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN + \ |
| MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN + \ |
| MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN + \ |
| MLXSW_SP_PORT_HW_EXT_STATS_LEN + \ |
| MLXSW_SP_PORT_HW_DISCARD_STATS_LEN + \ |
| (MLXSW_SP_PORT_HW_PRIO_STATS_LEN * \ |
| IEEE_8021QAZ_MAX_TCS) + \ |
| (MLXSW_SP_PORT_HW_TC_STATS_LEN * \ |
| TC_MAX_QUEUE) + \ |
| MLXSW_SP_PORT_HW_TRANSCEIVER_STATS_LEN) |
| |
| static void mlxsw_sp_port_get_prio_strings(u8 **p, int prio) |
| { |
| int i; |
| |
| for (i = 0; i < MLXSW_SP_PORT_HW_PRIO_STATS_LEN; i++) { |
| snprintf(*p, ETH_GSTRING_LEN, "%.29s_%.1d", |
| mlxsw_sp_port_hw_prio_stats[i].str, prio); |
| *p += ETH_GSTRING_LEN; |
| } |
| } |
| |
| static void mlxsw_sp_port_get_tc_strings(u8 **p, int tc) |
| { |
| int i; |
| |
| for (i = 0; i < MLXSW_SP_PORT_HW_TC_STATS_LEN; i++) { |
| snprintf(*p, ETH_GSTRING_LEN, "%.29s_%.1d", |
| mlxsw_sp_port_hw_tc_stats[i].str, tc); |
| *p += ETH_GSTRING_LEN; |
| } |
| } |
| |
| static void mlxsw_sp_port_get_strings(struct net_device *dev, |
| u32 stringset, u8 *data) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| u8 *p = data; |
| int i; |
| |
| switch (stringset) { |
| case ETH_SS_STATS: |
| for (i = 0; i < MLXSW_SP_PORT_HW_STATS_LEN; i++) { |
| memcpy(p, mlxsw_sp_port_hw_stats[i].str, |
| ETH_GSTRING_LEN); |
| p += ETH_GSTRING_LEN; |
| } |
| |
| for (i = 0; i < MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN; i++) { |
| memcpy(p, mlxsw_sp_port_hw_rfc_2863_stats[i].str, |
| ETH_GSTRING_LEN); |
| p += ETH_GSTRING_LEN; |
| } |
| |
| for (i = 0; i < MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN; i++) { |
| memcpy(p, mlxsw_sp_port_hw_rfc_2819_stats[i].str, |
| ETH_GSTRING_LEN); |
| p += ETH_GSTRING_LEN; |
| } |
| |
| for (i = 0; i < MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN; i++) { |
| memcpy(p, mlxsw_sp_port_hw_rfc_3635_stats[i].str, |
| ETH_GSTRING_LEN); |
| p += ETH_GSTRING_LEN; |
| } |
| |
| for (i = 0; i < MLXSW_SP_PORT_HW_EXT_STATS_LEN; i++) { |
| memcpy(p, mlxsw_sp_port_hw_ext_stats[i].str, |
| ETH_GSTRING_LEN); |
| p += ETH_GSTRING_LEN; |
| } |
| |
| for (i = 0; i < MLXSW_SP_PORT_HW_DISCARD_STATS_LEN; i++) { |
| memcpy(p, mlxsw_sp_port_hw_discard_stats[i].str, |
| ETH_GSTRING_LEN); |
| p += ETH_GSTRING_LEN; |
| } |
| |
| for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) |
| mlxsw_sp_port_get_prio_strings(&p, i); |
| |
| for (i = 0; i < TC_MAX_QUEUE; i++) |
| mlxsw_sp_port_get_tc_strings(&p, i); |
| |
| mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats_strings(&p); |
| |
| for (i = 0; i < MLXSW_SP_PORT_HW_TRANSCEIVER_STATS_LEN; i++) { |
| memcpy(p, mlxsw_sp_port_transceiver_stats[i].str, |
| ETH_GSTRING_LEN); |
| p += ETH_GSTRING_LEN; |
| } |
| break; |
| } |
| } |
| |
| static int mlxsw_sp_port_set_phys_id(struct net_device *dev, |
| enum ethtool_phys_id_state state) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| char mlcr_pl[MLXSW_REG_MLCR_LEN]; |
| bool active; |
| |
| switch (state) { |
| case ETHTOOL_ID_ACTIVE: |
| active = true; |
| break; |
| case ETHTOOL_ID_INACTIVE: |
| active = false; |
| break; |
| default: |
| return -EOPNOTSUPP; |
| } |
| |
| mlxsw_reg_mlcr_pack(mlcr_pl, mlxsw_sp_port->local_port, active); |
| return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mlcr), mlcr_pl); |
| } |
| |
| static int |
| mlxsw_sp_get_hw_stats_by_group(struct mlxsw_sp_port_hw_stats **p_hw_stats, |
| int *p_len, enum mlxsw_reg_ppcnt_grp grp) |
| { |
| switch (grp) { |
| case MLXSW_REG_PPCNT_IEEE_8023_CNT: |
| *p_hw_stats = mlxsw_sp_port_hw_stats; |
| *p_len = MLXSW_SP_PORT_HW_STATS_LEN; |
| break; |
| case MLXSW_REG_PPCNT_RFC_2863_CNT: |
| *p_hw_stats = mlxsw_sp_port_hw_rfc_2863_stats; |
| *p_len = MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN; |
| break; |
| case MLXSW_REG_PPCNT_RFC_2819_CNT: |
| *p_hw_stats = mlxsw_sp_port_hw_rfc_2819_stats; |
| *p_len = MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN; |
| break; |
| case MLXSW_REG_PPCNT_RFC_3635_CNT: |
| *p_hw_stats = mlxsw_sp_port_hw_rfc_3635_stats; |
| *p_len = MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN; |
| break; |
| case MLXSW_REG_PPCNT_EXT_CNT: |
| *p_hw_stats = mlxsw_sp_port_hw_ext_stats; |
| *p_len = MLXSW_SP_PORT_HW_EXT_STATS_LEN; |
| break; |
| case MLXSW_REG_PPCNT_DISCARD_CNT: |
| *p_hw_stats = mlxsw_sp_port_hw_discard_stats; |
| *p_len = MLXSW_SP_PORT_HW_DISCARD_STATS_LEN; |
| break; |
| case MLXSW_REG_PPCNT_PRIO_CNT: |
| *p_hw_stats = mlxsw_sp_port_hw_prio_stats; |
| *p_len = MLXSW_SP_PORT_HW_PRIO_STATS_LEN; |
| break; |
| case MLXSW_REG_PPCNT_TC_CNT: |
| *p_hw_stats = mlxsw_sp_port_hw_tc_stats; |
| *p_len = MLXSW_SP_PORT_HW_TC_STATS_LEN; |
| break; |
| default: |
| WARN_ON(1); |
| return -EOPNOTSUPP; |
| } |
| return 0; |
| } |
| |
| static void __mlxsw_sp_port_get_stats(struct net_device *dev, |
| enum mlxsw_reg_ppcnt_grp grp, int prio, |
| u64 *data, int data_index) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| struct mlxsw_sp_port_hw_stats *hw_stats; |
| char ppcnt_pl[MLXSW_REG_PPCNT_LEN]; |
| int i, len; |
| int err; |
| |
| err = mlxsw_sp_get_hw_stats_by_group(&hw_stats, &len, grp); |
| if (err) |
| return; |
| mlxsw_sp_port_get_stats_raw(dev, grp, prio, ppcnt_pl); |
| for (i = 0; i < len; i++) { |
| data[data_index + i] = hw_stats[i].getter(ppcnt_pl); |
| if (!hw_stats[i].cells_bytes) |
| continue; |
| data[data_index + i] = mlxsw_sp_cells_bytes(mlxsw_sp, |
| data[data_index + i]); |
| } |
| } |
| |
| static void __mlxsw_sp_port_get_env_stats(struct net_device *dev, u64 *data, int data_index, |
| struct mlxsw_sp_port_stats *port_stats, |
| int len) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| int i; |
| |
| for (i = 0; i < len; i++) |
| data[data_index + i] = port_stats[i].getter(mlxsw_sp_port); |
| } |
| |
| static void mlxsw_sp_port_get_stats(struct net_device *dev, |
| struct ethtool_stats *stats, u64 *data) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| int i, data_index = 0; |
| |
| /* IEEE 802.3 Counters */ |
| __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT, 0, |
| data, data_index); |
| data_index = MLXSW_SP_PORT_HW_STATS_LEN; |
| |
| /* RFC 2863 Counters */ |
| __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_2863_CNT, 0, |
| data, data_index); |
| data_index += MLXSW_SP_PORT_HW_RFC_2863_STATS_LEN; |
| |
| /* RFC 2819 Counters */ |
| __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_2819_CNT, 0, |
| data, data_index); |
| data_index += MLXSW_SP_PORT_HW_RFC_2819_STATS_LEN; |
| |
| /* RFC 3635 Counters */ |
| __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_RFC_3635_CNT, 0, |
| data, data_index); |
| data_index += MLXSW_SP_PORT_HW_RFC_3635_STATS_LEN; |
| |
| /* Extended Counters */ |
| __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_EXT_CNT, 0, |
| data, data_index); |
| data_index += MLXSW_SP_PORT_HW_EXT_STATS_LEN; |
| |
| /* Discard Counters */ |
| __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_DISCARD_CNT, 0, |
| data, data_index); |
| data_index += MLXSW_SP_PORT_HW_DISCARD_STATS_LEN; |
| |
| /* Per-Priority Counters */ |
| for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { |
| __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_PRIO_CNT, i, |
| data, data_index); |
| data_index += MLXSW_SP_PORT_HW_PRIO_STATS_LEN; |
| } |
| |
| /* Per-TC Counters */ |
| for (i = 0; i < TC_MAX_QUEUE; i++) { |
| __mlxsw_sp_port_get_stats(dev, MLXSW_REG_PPCNT_TC_CNT, i, |
| data, data_index); |
| data_index += MLXSW_SP_PORT_HW_TC_STATS_LEN; |
| } |
| |
| /* PTP counters */ |
| mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats(mlxsw_sp_port, |
| data, data_index); |
| data_index += mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats_count(); |
| |
| /* Transceiver counters */ |
| __mlxsw_sp_port_get_env_stats(dev, data, data_index, mlxsw_sp_port_transceiver_stats, |
| MLXSW_SP_PORT_HW_TRANSCEIVER_STATS_LEN); |
| data_index += MLXSW_SP_PORT_HW_TRANSCEIVER_STATS_LEN; |
| } |
| |
| static int mlxsw_sp_port_get_sset_count(struct net_device *dev, int sset) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| |
| switch (sset) { |
| case ETH_SS_STATS: |
| return MLXSW_SP_PORT_ETHTOOL_STATS_LEN + |
| mlxsw_sp_port->mlxsw_sp->ptp_ops->get_stats_count(); |
| default: |
| return -EOPNOTSUPP; |
| } |
| } |
| |
| static void |
| mlxsw_sp_port_get_link_supported(struct mlxsw_sp *mlxsw_sp, u32 eth_proto_cap, |
| struct ethtool_link_ksettings *cmd) |
| { |
| const struct mlxsw_sp_port_type_speed_ops *ops; |
| |
| ops = mlxsw_sp->port_type_speed_ops; |
| |
| ethtool_link_ksettings_add_link_mode(cmd, supported, Asym_Pause); |
| ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg); |
| ethtool_link_ksettings_add_link_mode(cmd, supported, Pause); |
| |
| ops->from_ptys_supported_port(mlxsw_sp, eth_proto_cap, cmd); |
| ops->from_ptys_link(mlxsw_sp, eth_proto_cap, |
| cmd->link_modes.supported); |
| } |
| |
| static void |
| mlxsw_sp_port_get_link_advertise(struct mlxsw_sp *mlxsw_sp, |
| u32 eth_proto_admin, bool autoneg, |
| struct ethtool_link_ksettings *cmd) |
| { |
| const struct mlxsw_sp_port_type_speed_ops *ops; |
| |
| ops = mlxsw_sp->port_type_speed_ops; |
| |
| if (!autoneg) |
| return; |
| |
| ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg); |
| ops->from_ptys_link(mlxsw_sp, eth_proto_admin, |
| cmd->link_modes.advertising); |
| } |
| |
| static u8 |
| mlxsw_sp_port_connector_port(enum mlxsw_reg_ptys_connector_type connector_type) |
| { |
| switch (connector_type) { |
| case MLXSW_REG_PTYS_CONNECTOR_TYPE_UNKNOWN_OR_NO_CONNECTOR: |
| return PORT_OTHER; |
| case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_NONE: |
| return PORT_NONE; |
| case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_TP: |
| return PORT_TP; |
| case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_AUI: |
| return PORT_AUI; |
| case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_BNC: |
| return PORT_BNC; |
| case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_MII: |
| return PORT_MII; |
| case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_FIBRE: |
| return PORT_FIBRE; |
| case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_DA: |
| return PORT_DA; |
| case MLXSW_REG_PTYS_CONNECTOR_TYPE_PORT_OTHER: |
| return PORT_OTHER; |
| default: |
| WARN_ON_ONCE(1); |
| return PORT_OTHER; |
| } |
| } |
| |
| static int mlxsw_sp_port_ptys_query(struct mlxsw_sp_port *mlxsw_sp_port, |
| u32 *p_eth_proto_cap, u32 *p_eth_proto_admin, |
| u32 *p_eth_proto_oper, u8 *p_connector_type) |
| { |
| struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| const struct mlxsw_sp_port_type_speed_ops *ops; |
| char ptys_pl[MLXSW_REG_PTYS_LEN]; |
| int err; |
| |
| ops = mlxsw_sp->port_type_speed_ops; |
| |
| ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port, 0, false); |
| err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); |
| if (err) |
| return err; |
| |
| ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, p_eth_proto_cap, p_eth_proto_admin, |
| p_eth_proto_oper); |
| if (p_connector_type) |
| *p_connector_type = mlxsw_reg_ptys_connector_type_get(ptys_pl); |
| return 0; |
| } |
| |
| static int mlxsw_sp_port_get_link_ksettings(struct net_device *dev, |
| struct ethtool_link_ksettings *cmd) |
| { |
| u32 eth_proto_cap, eth_proto_admin, eth_proto_oper; |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| const struct mlxsw_sp_port_type_speed_ops *ops; |
| u8 connector_type; |
| bool autoneg; |
| int err; |
| |
| err = mlxsw_sp_port_ptys_query(mlxsw_sp_port, ð_proto_cap, ð_proto_admin, |
| ð_proto_oper, &connector_type); |
| if (err) |
| return err; |
| |
| ops = mlxsw_sp->port_type_speed_ops; |
| autoneg = mlxsw_sp_port->link.autoneg; |
| |
| mlxsw_sp_port_get_link_supported(mlxsw_sp, eth_proto_cap, cmd); |
| |
| mlxsw_sp_port_get_link_advertise(mlxsw_sp, eth_proto_admin, autoneg, cmd); |
| |
| cmd->base.autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; |
| cmd->base.port = mlxsw_sp_port_connector_port(connector_type); |
| ops->from_ptys_link_mode(mlxsw_sp, netif_carrier_ok(dev), |
| eth_proto_oper, cmd); |
| |
| return 0; |
| } |
| |
| static int |
| mlxsw_sp_port_set_link_ksettings(struct net_device *dev, |
| const struct ethtool_link_ksettings *cmd) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| const struct mlxsw_sp_port_type_speed_ops *ops; |
| char ptys_pl[MLXSW_REG_PTYS_LEN]; |
| u32 eth_proto_cap, eth_proto_new; |
| bool autoneg; |
| int err; |
| |
| ops = mlxsw_sp->port_type_speed_ops; |
| |
| ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port, |
| 0, false); |
| err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); |
| if (err) |
| return err; |
| ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, ð_proto_cap, NULL, NULL); |
| |
| autoneg = cmd->base.autoneg == AUTONEG_ENABLE; |
| eth_proto_new = autoneg ? |
| ops->to_ptys_advert_link(mlxsw_sp, cmd) : |
| ops->to_ptys_speed_lanes(mlxsw_sp, mlxsw_sp_port->mapping.width, |
| cmd); |
| |
| eth_proto_new = eth_proto_new & eth_proto_cap; |
| if (!eth_proto_new) { |
| netdev_err(dev, "No supported speed or lanes requested\n"); |
| return -EINVAL; |
| } |
| |
| ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port, |
| eth_proto_new, autoneg); |
| err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); |
| if (err) |
| return err; |
| |
| mlxsw_sp_port->link.autoneg = autoneg; |
| |
| if (!netif_running(dev)) |
| return 0; |
| |
| mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false); |
| mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true); |
| |
| return 0; |
| } |
| |
| static int mlxsw_sp_get_module_info(struct net_device *netdev, |
| struct ethtool_modinfo *modinfo) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev); |
| struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| int err; |
| |
| err = mlxsw_env_get_module_info(mlxsw_sp->core, |
| mlxsw_sp_port->mapping.module, |
| modinfo); |
| |
| return err; |
| } |
| |
| static int mlxsw_sp_get_module_eeprom(struct net_device *netdev, |
| struct ethtool_eeprom *ee, u8 *data) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev); |
| struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| int err; |
| |
| err = mlxsw_env_get_module_eeprom(netdev, mlxsw_sp->core, |
| mlxsw_sp_port->mapping.module, ee, |
| data); |
| |
| return err; |
| } |
| |
| static int |
| mlxsw_sp_get_module_eeprom_by_page(struct net_device *dev, |
| const struct ethtool_module_eeprom *page, |
| struct netlink_ext_ack *extack) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| u8 module = mlxsw_sp_port->mapping.module; |
| |
| return mlxsw_env_get_module_eeprom_by_page(mlxsw_sp->core, module, page, |
| extack); |
| } |
| |
| static int |
| mlxsw_sp_get_ts_info(struct net_device *netdev, struct ethtool_ts_info *info) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev); |
| struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| |
| return mlxsw_sp->ptp_ops->get_ts_info(mlxsw_sp, info); |
| } |
| |
| static void |
| mlxsw_sp_get_eth_phy_stats(struct net_device *dev, |
| struct ethtool_eth_phy_stats *phy_stats) |
| { |
| char ppcnt_pl[MLXSW_REG_PPCNT_LEN]; |
| |
| if (mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT, |
| 0, ppcnt_pl)) |
| return; |
| |
| phy_stats->SymbolErrorDuringCarrier = |
| mlxsw_reg_ppcnt_a_symbol_error_during_carrier_get(ppcnt_pl); |
| } |
| |
| static void |
| mlxsw_sp_get_eth_mac_stats(struct net_device *dev, |
| struct ethtool_eth_mac_stats *mac_stats) |
| { |
| char ppcnt_pl[MLXSW_REG_PPCNT_LEN]; |
| |
| if (mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT, |
| 0, ppcnt_pl)) |
| return; |
| |
| mac_stats->FramesTransmittedOK = |
| mlxsw_reg_ppcnt_a_frames_transmitted_ok_get(ppcnt_pl); |
| mac_stats->FramesReceivedOK = |
| mlxsw_reg_ppcnt_a_frames_received_ok_get(ppcnt_pl); |
| mac_stats->FrameCheckSequenceErrors = |
| mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get(ppcnt_pl); |
| mac_stats->AlignmentErrors = |
| mlxsw_reg_ppcnt_a_alignment_errors_get(ppcnt_pl); |
| mac_stats->OctetsTransmittedOK = |
| mlxsw_reg_ppcnt_a_octets_transmitted_ok_get(ppcnt_pl); |
| mac_stats->OctetsReceivedOK = |
| mlxsw_reg_ppcnt_a_octets_received_ok_get(ppcnt_pl); |
| mac_stats->MulticastFramesXmittedOK = |
| mlxsw_reg_ppcnt_a_multicast_frames_xmitted_ok_get(ppcnt_pl); |
| mac_stats->BroadcastFramesXmittedOK = |
| mlxsw_reg_ppcnt_a_broadcast_frames_xmitted_ok_get(ppcnt_pl); |
| mac_stats->MulticastFramesReceivedOK = |
| mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get(ppcnt_pl); |
| mac_stats->BroadcastFramesReceivedOK = |
| mlxsw_reg_ppcnt_a_broadcast_frames_received_ok_get(ppcnt_pl); |
| mac_stats->InRangeLengthErrors = |
| mlxsw_reg_ppcnt_a_in_range_length_errors_get(ppcnt_pl); |
| mac_stats->OutOfRangeLengthField = |
| mlxsw_reg_ppcnt_a_out_of_range_length_field_get(ppcnt_pl); |
| mac_stats->FrameTooLongErrors = |
| mlxsw_reg_ppcnt_a_frame_too_long_errors_get(ppcnt_pl); |
| } |
| |
| static void |
| mlxsw_sp_get_eth_ctrl_stats(struct net_device *dev, |
| struct ethtool_eth_ctrl_stats *ctrl_stats) |
| { |
| char ppcnt_pl[MLXSW_REG_PPCNT_LEN]; |
| |
| if (mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT, |
| 0, ppcnt_pl)) |
| return; |
| |
| ctrl_stats->MACControlFramesTransmitted = |
| mlxsw_reg_ppcnt_a_mac_control_frames_transmitted_get(ppcnt_pl); |
| ctrl_stats->MACControlFramesReceived = |
| mlxsw_reg_ppcnt_a_mac_control_frames_received_get(ppcnt_pl); |
| ctrl_stats->UnsupportedOpcodesReceived = |
| mlxsw_reg_ppcnt_a_unsupported_opcodes_received_get(ppcnt_pl); |
| } |
| |
| static const struct ethtool_rmon_hist_range mlxsw_rmon_ranges[] = { |
| { 0, 64 }, |
| { 65, 127 }, |
| { 128, 255 }, |
| { 256, 511 }, |
| { 512, 1023 }, |
| { 1024, 1518 }, |
| { 1519, 2047 }, |
| { 2048, 4095 }, |
| { 4096, 8191 }, |
| { 8192, 10239 }, |
| {} |
| }; |
| |
| static void |
| mlxsw_sp_get_rmon_stats(struct net_device *dev, |
| struct ethtool_rmon_stats *rmon, |
| const struct ethtool_rmon_hist_range **ranges) |
| { |
| char ppcnt_pl[MLXSW_REG_PPCNT_LEN]; |
| |
| if (mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_RFC_2819_CNT, |
| 0, ppcnt_pl)) |
| return; |
| |
| rmon->undersize_pkts = |
| mlxsw_reg_ppcnt_ether_stats_undersize_pkts_get(ppcnt_pl); |
| rmon->oversize_pkts = |
| mlxsw_reg_ppcnt_ether_stats_oversize_pkts_get(ppcnt_pl); |
| rmon->fragments = |
| mlxsw_reg_ppcnt_ether_stats_fragments_get(ppcnt_pl); |
| |
| rmon->hist[0] = mlxsw_reg_ppcnt_ether_stats_pkts64octets_get(ppcnt_pl); |
| rmon->hist[1] = |
| mlxsw_reg_ppcnt_ether_stats_pkts65to127octets_get(ppcnt_pl); |
| rmon->hist[2] = |
| mlxsw_reg_ppcnt_ether_stats_pkts128to255octets_get(ppcnt_pl); |
| rmon->hist[3] = |
| mlxsw_reg_ppcnt_ether_stats_pkts256to511octets_get(ppcnt_pl); |
| rmon->hist[4] = |
| mlxsw_reg_ppcnt_ether_stats_pkts512to1023octets_get(ppcnt_pl); |
| rmon->hist[5] = |
| mlxsw_reg_ppcnt_ether_stats_pkts1024to1518octets_get(ppcnt_pl); |
| rmon->hist[6] = |
| mlxsw_reg_ppcnt_ether_stats_pkts1519to2047octets_get(ppcnt_pl); |
| rmon->hist[7] = |
| mlxsw_reg_ppcnt_ether_stats_pkts2048to4095octets_get(ppcnt_pl); |
| rmon->hist[8] = |
| mlxsw_reg_ppcnt_ether_stats_pkts4096to8191octets_get(ppcnt_pl); |
| rmon->hist[9] = |
| mlxsw_reg_ppcnt_ether_stats_pkts8192to10239octets_get(ppcnt_pl); |
| |
| *ranges = mlxsw_rmon_ranges; |
| } |
| |
| static int mlxsw_sp_reset(struct net_device *dev, u32 *flags) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| u8 module = mlxsw_sp_port->mapping.module; |
| |
| return mlxsw_env_reset_module(dev, mlxsw_sp->core, module, flags); |
| } |
| |
| static int |
| mlxsw_sp_get_module_power_mode(struct net_device *dev, |
| struct ethtool_module_power_mode_params *params, |
| struct netlink_ext_ack *extack) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| u8 module = mlxsw_sp_port->mapping.module; |
| |
| return mlxsw_env_get_module_power_mode(mlxsw_sp->core, module, params, |
| extack); |
| } |
| |
| static int |
| mlxsw_sp_set_module_power_mode(struct net_device *dev, |
| const struct ethtool_module_power_mode_params *params, |
| struct netlink_ext_ack *extack) |
| { |
| struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| u8 module = mlxsw_sp_port->mapping.module; |
| |
| return mlxsw_env_set_module_power_mode(mlxsw_sp->core, module, |
| params->policy, extack); |
| } |
| |
| const struct ethtool_ops mlxsw_sp_port_ethtool_ops = { |
| .cap_link_lanes_supported = true, |
| .get_drvinfo = mlxsw_sp_port_get_drvinfo, |
| .get_link = ethtool_op_get_link, |
| .get_link_ext_state = mlxsw_sp_port_get_link_ext_state, |
| .get_pauseparam = mlxsw_sp_port_get_pauseparam, |
| .set_pauseparam = mlxsw_sp_port_set_pauseparam, |
| .get_strings = mlxsw_sp_port_get_strings, |
| .set_phys_id = mlxsw_sp_port_set_phys_id, |
| .get_ethtool_stats = mlxsw_sp_port_get_stats, |
| .get_sset_count = mlxsw_sp_port_get_sset_count, |
| .get_link_ksettings = mlxsw_sp_port_get_link_ksettings, |
| .set_link_ksettings = mlxsw_sp_port_set_link_ksettings, |
| .get_module_info = mlxsw_sp_get_module_info, |
| .get_module_eeprom = mlxsw_sp_get_module_eeprom, |
| .get_module_eeprom_by_page = mlxsw_sp_get_module_eeprom_by_page, |
| .get_ts_info = mlxsw_sp_get_ts_info, |
| .get_eth_phy_stats = mlxsw_sp_get_eth_phy_stats, |
| .get_eth_mac_stats = mlxsw_sp_get_eth_mac_stats, |
| .get_eth_ctrl_stats = mlxsw_sp_get_eth_ctrl_stats, |
| .get_rmon_stats = mlxsw_sp_get_rmon_stats, |
| .reset = mlxsw_sp_reset, |
| .get_module_power_mode = mlxsw_sp_get_module_power_mode, |
| .set_module_power_mode = mlxsw_sp_set_module_power_mode, |
| }; |
| |
| struct mlxsw_sp1_port_link_mode { |
| enum ethtool_link_mode_bit_indices mask_ethtool; |
| u32 mask; |
| u32 speed; |
| }; |
| |
| static const struct mlxsw_sp1_port_link_mode mlxsw_sp1_port_link_mode[] = { |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_SGMII | |
| MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX, |
| .mask_ethtool = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, |
| .speed = SPEED_1000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CX4 | |
| MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4, |
| .mask_ethtool = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, |
| .speed = SPEED_10000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR | |
| MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR | |
| MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR | |
| MLXSW_REG_PTYS_ETH_SPEED_10GBASE_ER_LR, |
| .mask_ethtool = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, |
| .speed = SPEED_10000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4, |
| .mask_ethtool = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, |
| .speed = SPEED_40000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4, |
| .mask_ethtool = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, |
| .speed = SPEED_40000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4, |
| .mask_ethtool = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, |
| .speed = SPEED_40000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_LR4_ER4, |
| .mask_ethtool = ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, |
| .speed = SPEED_40000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_CR, |
| .mask_ethtool = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, |
| .speed = SPEED_25000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_KR, |
| .mask_ethtool = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, |
| .speed = SPEED_25000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_SR, |
| .mask_ethtool = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, |
| .speed = SPEED_25000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_CR2, |
| .mask_ethtool = ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, |
| .speed = SPEED_50000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR2, |
| .mask_ethtool = ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, |
| .speed = SPEED_50000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_SR2, |
| .mask_ethtool = ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, |
| .speed = SPEED_50000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4, |
| .mask_ethtool = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, |
| .speed = SPEED_100000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4, |
| .mask_ethtool = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, |
| .speed = SPEED_100000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4, |
| .mask_ethtool = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, |
| .speed = SPEED_100000, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_LR4_ER4, |
| .mask_ethtool = ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, |
| .speed = SPEED_100000, |
| }, |
| }; |
| |
| #define MLXSW_SP1_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sp1_port_link_mode) |
| |
| static void |
| mlxsw_sp1_from_ptys_supported_port(struct mlxsw_sp *mlxsw_sp, |
| u32 ptys_eth_proto, |
| struct ethtool_link_ksettings *cmd) |
| { |
| if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR | |
| MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR | |
| MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 | |
| MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 | |
| MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 | |
| MLXSW_REG_PTYS_ETH_SPEED_SGMII)) |
| ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); |
| |
| if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR | |
| MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 | |
| MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 | |
| MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 | |
| MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX)) |
| ethtool_link_ksettings_add_link_mode(cmd, supported, Backplane); |
| } |
| |
| static void |
| mlxsw_sp1_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto, |
| unsigned long *mode) |
| { |
| int i; |
| |
| for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) { |
| if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask) |
| __set_bit(mlxsw_sp1_port_link_mode[i].mask_ethtool, |
| mode); |
| } |
| } |
| |
| static u32 |
| mlxsw_sp1_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto) |
| { |
| int i; |
| |
| for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) { |
| if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask) |
| return mlxsw_sp1_port_link_mode[i].speed; |
| } |
| |
| return SPEED_UNKNOWN; |
| } |
| |
| static void |
| mlxsw_sp1_from_ptys_link_mode(struct mlxsw_sp *mlxsw_sp, bool carrier_ok, |
| u32 ptys_eth_proto, |
| struct ethtool_link_ksettings *cmd) |
| { |
| struct mlxsw_sp1_port_link_mode link; |
| int i; |
| |
| cmd->base.speed = SPEED_UNKNOWN; |
| cmd->base.duplex = DUPLEX_UNKNOWN; |
| cmd->lanes = 0; |
| |
| if (!carrier_ok) |
| return; |
| |
| for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) { |
| if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask) { |
| link = mlxsw_sp1_port_link_mode[i]; |
| ethtool_params_from_link_mode(cmd, |
| link.mask_ethtool); |
| } |
| } |
| } |
| |
| static int mlxsw_sp1_ptys_max_speed(struct mlxsw_sp_port *mlxsw_sp_port, u32 *p_max_speed) |
| { |
| u32 eth_proto_cap; |
| u32 max_speed = 0; |
| int err; |
| int i; |
| |
| err = mlxsw_sp_port_ptys_query(mlxsw_sp_port, ð_proto_cap, NULL, NULL, NULL); |
| if (err) |
| return err; |
| |
| for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) { |
| if ((eth_proto_cap & mlxsw_sp1_port_link_mode[i].mask) && |
| mlxsw_sp1_port_link_mode[i].speed > max_speed) |
| max_speed = mlxsw_sp1_port_link_mode[i].speed; |
| } |
| |
| *p_max_speed = max_speed; |
| return 0; |
| } |
| |
| static u32 |
| mlxsw_sp1_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp, |
| const struct ethtool_link_ksettings *cmd) |
| { |
| u32 ptys_proto = 0; |
| int i; |
| |
| for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) { |
| if (test_bit(mlxsw_sp1_port_link_mode[i].mask_ethtool, |
| cmd->link_modes.advertising)) |
| ptys_proto |= mlxsw_sp1_port_link_mode[i].mask; |
| } |
| return ptys_proto; |
| } |
| |
| static u32 mlxsw_sp1_to_ptys_speed_lanes(struct mlxsw_sp *mlxsw_sp, u8 width, |
| const struct ethtool_link_ksettings *cmd) |
| { |
| u32 ptys_proto = 0; |
| int i; |
| |
| if (cmd->lanes > width) |
| return ptys_proto; |
| |
| for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) { |
| if (cmd->base.speed == mlxsw_sp1_port_link_mode[i].speed) |
| ptys_proto |= mlxsw_sp1_port_link_mode[i].mask; |
| } |
| return ptys_proto; |
| } |
| |
| static void |
| mlxsw_sp1_reg_ptys_eth_pack(struct mlxsw_sp *mlxsw_sp, char *payload, |
| u16 local_port, u32 proto_admin, bool autoneg) |
| { |
| mlxsw_reg_ptys_eth_pack(payload, local_port, proto_admin, autoneg); |
| } |
| |
| static void |
| mlxsw_sp1_reg_ptys_eth_unpack(struct mlxsw_sp *mlxsw_sp, char *payload, |
| u32 *p_eth_proto_cap, u32 *p_eth_proto_admin, |
| u32 *p_eth_proto_oper) |
| { |
| mlxsw_reg_ptys_eth_unpack(payload, p_eth_proto_cap, p_eth_proto_admin, |
| p_eth_proto_oper); |
| } |
| |
| static u32 mlxsw_sp1_ptys_proto_cap_masked_get(u32 eth_proto_cap) |
| { |
| u32 ptys_proto_cap_masked = 0; |
| int i; |
| |
| for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) { |
| if (mlxsw_sp1_port_link_mode[i].mask & eth_proto_cap) |
| ptys_proto_cap_masked |= |
| mlxsw_sp1_port_link_mode[i].mask; |
| } |
| |
| return ptys_proto_cap_masked; |
| } |
| |
| const struct mlxsw_sp_port_type_speed_ops mlxsw_sp1_port_type_speed_ops = { |
| .from_ptys_supported_port = mlxsw_sp1_from_ptys_supported_port, |
| .from_ptys_link = mlxsw_sp1_from_ptys_link, |
| .from_ptys_speed = mlxsw_sp1_from_ptys_speed, |
| .from_ptys_link_mode = mlxsw_sp1_from_ptys_link_mode, |
| .ptys_max_speed = mlxsw_sp1_ptys_max_speed, |
| .to_ptys_advert_link = mlxsw_sp1_to_ptys_advert_link, |
| .to_ptys_speed_lanes = mlxsw_sp1_to_ptys_speed_lanes, |
| .reg_ptys_eth_pack = mlxsw_sp1_reg_ptys_eth_pack, |
| .reg_ptys_eth_unpack = mlxsw_sp1_reg_ptys_eth_unpack, |
| .ptys_proto_cap_masked_get = mlxsw_sp1_ptys_proto_cap_masked_get, |
| }; |
| |
| static const enum ethtool_link_mode_bit_indices |
| mlxsw_sp2_mask_ethtool_sgmii_100m[] = { |
| ETHTOOL_LINK_MODE_100baseT_Full_BIT, |
| }; |
| |
| #define MLXSW_SP2_MASK_ETHTOOL_SGMII_100M_LEN \ |
| ARRAY_SIZE(mlxsw_sp2_mask_ethtool_sgmii_100m) |
| |
| static const enum ethtool_link_mode_bit_indices |
| mlxsw_sp2_mask_ethtool_1000base_x_sgmii[] = { |
| ETHTOOL_LINK_MODE_1000baseT_Full_BIT, |
| ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, |
| }; |
| |
| #define MLXSW_SP2_MASK_ETHTOOL_1000BASE_X_SGMII_LEN \ |
| ARRAY_SIZE(mlxsw_sp2_mask_ethtool_1000base_x_sgmii) |
| |
| static const enum ethtool_link_mode_bit_indices |
| mlxsw_sp2_mask_ethtool_5gbase_r[] = { |
| ETHTOOL_LINK_MODE_5000baseT_Full_BIT, |
| }; |
| |
| #define MLXSW_SP2_MASK_ETHTOOL_5GBASE_R_LEN \ |
| ARRAY_SIZE(mlxsw_sp2_mask_ethtool_5gbase_r) |
| |
| static const enum ethtool_link_mode_bit_indices |
| mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g[] = { |
| ETHTOOL_LINK_MODE_10000baseT_Full_BIT, |
| ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, |
| ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, |
| ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, |
| ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, |
| ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, |
| ETHTOOL_LINK_MODE_10000baseER_Full_BIT, |
| }; |
| |
| #define MLXSW_SP2_MASK_ETHTOOL_XFI_XAUI_1_10G_LEN \ |
| ARRAY_SIZE(mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g) |
| |
| static const enum ethtool_link_mode_bit_indices |
| mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g[] = { |
| ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, |
| ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, |
| ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, |
| ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, |
| }; |
| |
| #define MLXSW_SP2_MASK_ETHTOOL_XLAUI_4_XLPPI_4_40G_LEN \ |
| ARRAY_SIZE(mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g) |
| |
| static const enum ethtool_link_mode_bit_indices |
| mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr[] = { |
| ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, |
| ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, |
| ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, |
| }; |
| |
| #define MLXSW_SP2_MASK_ETHTOOL_25GAUI_1_25GBASE_CR_KR_LEN \ |
| ARRAY_SIZE(mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr) |
| |
| static const enum ethtool_link_mode_bit_indices |
| mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2[] = { |
| ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, |
| ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, |
| ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, |
| }; |
| |
| #define MLXSW_SP2_MASK_ETHTOOL_50GAUI_2_LAUI_2_50GBASE_CR2_KR2_LEN \ |
| ARRAY_SIZE(mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2) |
| |
| static const enum ethtool_link_mode_bit_indices |
| mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr[] = { |
| ETHTOOL_LINK_MODE_50000baseKR_Full_BIT, |
| ETHTOOL_LINK_MODE_50000baseSR_Full_BIT, |
| ETHTOOL_LINK_MODE_50000baseCR_Full_BIT, |
| ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT, |
| ETHTOOL_LINK_MODE_50000baseDR_Full_BIT, |
| }; |
| |
| #define MLXSW_SP2_MASK_ETHTOOL_50GAUI_1_LAUI_1_50GBASE_CR_KR_LEN \ |
| ARRAY_SIZE(mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr) |
| |
| static const enum ethtool_link_mode_bit_indices |
| mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4[] = { |
| ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, |
| ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, |
| ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, |
| ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, |
| }; |
| |
| #define MLXSW_SP2_MASK_ETHTOOL_CAUI_4_100GBASE_CR4_KR4_LEN \ |
| ARRAY_SIZE(mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4) |
| |
| static const enum ethtool_link_mode_bit_indices |
| mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2[] = { |
| ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT, |
| ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT, |
| ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT, |
| ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT, |
| ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT, |
| }; |
| |
| #define MLXSW_SP2_MASK_ETHTOOL_100GAUI_2_100GBASE_CR2_KR2_LEN \ |
| ARRAY_SIZE(mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2) |
| |
| static const enum ethtool_link_mode_bit_indices |
| mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4[] = { |
| ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT, |
| ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT, |
| ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT, |
| ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT, |
| ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT, |
| }; |
| |
| #define MLXSW_SP2_MASK_ETHTOOL_200GAUI_4_200GBASE_CR4_KR4_LEN \ |
| ARRAY_SIZE(mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4) |
| |
| static const enum ethtool_link_mode_bit_indices |
| mlxsw_sp2_mask_ethtool_400gaui_8[] = { |
| ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT, |
| ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT, |
| ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT, |
| ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT, |
| ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT, |
| }; |
| |
| #define MLXSW_SP2_MASK_ETHTOOL_400GAUI_8_LEN \ |
| ARRAY_SIZE(mlxsw_sp2_mask_ethtool_400gaui_8) |
| |
| #define MLXSW_SP_PORT_MASK_WIDTH_1X BIT(0) |
| #define MLXSW_SP_PORT_MASK_WIDTH_2X BIT(1) |
| #define MLXSW_SP_PORT_MASK_WIDTH_4X BIT(2) |
| #define MLXSW_SP_PORT_MASK_WIDTH_8X BIT(3) |
| |
| static u8 mlxsw_sp_port_mask_width_get(u8 width) |
| { |
| switch (width) { |
| case 1: |
| return MLXSW_SP_PORT_MASK_WIDTH_1X; |
| case 2: |
| return MLXSW_SP_PORT_MASK_WIDTH_2X; |
| case 4: |
| return MLXSW_SP_PORT_MASK_WIDTH_4X; |
| case 8: |
| return MLXSW_SP_PORT_MASK_WIDTH_8X; |
| default: |
| WARN_ON_ONCE(1); |
| return 0; |
| } |
| } |
| |
| struct mlxsw_sp2_port_link_mode { |
| const enum ethtool_link_mode_bit_indices *mask_ethtool; |
| int m_ethtool_len; |
| u32 mask; |
| u32 speed; |
| u32 width; |
| u8 mask_sup_width; |
| }; |
| |
| static const struct mlxsw_sp2_port_link_mode mlxsw_sp2_port_link_mode[] = { |
| { |
| .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_SGMII_100M, |
| .mask_ethtool = mlxsw_sp2_mask_ethtool_sgmii_100m, |
| .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_SGMII_100M_LEN, |
| .mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_1X | |
| MLXSW_SP_PORT_MASK_WIDTH_2X | |
| MLXSW_SP_PORT_MASK_WIDTH_4X | |
| MLXSW_SP_PORT_MASK_WIDTH_8X, |
| .speed = SPEED_100, |
| .width = 1, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_1000BASE_X_SGMII, |
| .mask_ethtool = mlxsw_sp2_mask_ethtool_1000base_x_sgmii, |
| .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_1000BASE_X_SGMII_LEN, |
| .mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_1X | |
| MLXSW_SP_PORT_MASK_WIDTH_2X | |
| MLXSW_SP_PORT_MASK_WIDTH_4X | |
| MLXSW_SP_PORT_MASK_WIDTH_8X, |
| .speed = SPEED_1000, |
| .width = 1, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_5GBASE_R, |
| .mask_ethtool = mlxsw_sp2_mask_ethtool_5gbase_r, |
| .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_5GBASE_R_LEN, |
| .mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_1X | |
| MLXSW_SP_PORT_MASK_WIDTH_2X | |
| MLXSW_SP_PORT_MASK_WIDTH_4X | |
| MLXSW_SP_PORT_MASK_WIDTH_8X, |
| .speed = SPEED_5000, |
| .width = 1, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_XFI_XAUI_1_10G, |
| .mask_ethtool = mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g, |
| .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_XFI_XAUI_1_10G_LEN, |
| .mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_1X | |
| MLXSW_SP_PORT_MASK_WIDTH_2X | |
| MLXSW_SP_PORT_MASK_WIDTH_4X | |
| MLXSW_SP_PORT_MASK_WIDTH_8X, |
| .speed = SPEED_10000, |
| .width = 1, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_XLAUI_4_XLPPI_4_40G, |
| .mask_ethtool = mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g, |
| .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_XLAUI_4_XLPPI_4_40G_LEN, |
| .mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_4X | |
| MLXSW_SP_PORT_MASK_WIDTH_8X, |
| .speed = SPEED_40000, |
| .width = 4, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_25GAUI_1_25GBASE_CR_KR, |
| .mask_ethtool = mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr, |
| .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_25GAUI_1_25GBASE_CR_KR_LEN, |
| .mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_1X | |
| MLXSW_SP_PORT_MASK_WIDTH_2X | |
| MLXSW_SP_PORT_MASK_WIDTH_4X | |
| MLXSW_SP_PORT_MASK_WIDTH_8X, |
| .speed = SPEED_25000, |
| .width = 1, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_50GAUI_2_LAUI_2_50GBASE_CR2_KR2, |
| .mask_ethtool = mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2, |
| .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_50GAUI_2_LAUI_2_50GBASE_CR2_KR2_LEN, |
| .mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_2X | |
| MLXSW_SP_PORT_MASK_WIDTH_4X | |
| MLXSW_SP_PORT_MASK_WIDTH_8X, |
| .speed = SPEED_50000, |
| .width = 2, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_50GAUI_1_LAUI_1_50GBASE_CR_KR, |
| .mask_ethtool = mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr, |
| .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_50GAUI_1_LAUI_1_50GBASE_CR_KR_LEN, |
| .mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_1X, |
| .speed = SPEED_50000, |
| .width = 1, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_CAUI_4_100GBASE_CR4_KR4, |
| .mask_ethtool = mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4, |
| .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_CAUI_4_100GBASE_CR4_KR4_LEN, |
| .mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_4X | |
| MLXSW_SP_PORT_MASK_WIDTH_8X, |
| .speed = SPEED_100000, |
| .width = 4, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_100GAUI_2_100GBASE_CR2_KR2, |
| .mask_ethtool = mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2, |
| .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_100GAUI_2_100GBASE_CR2_KR2_LEN, |
| .mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_2X, |
| .speed = SPEED_100000, |
| .width = 2, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_200GAUI_4_200GBASE_CR4_KR4, |
| .mask_ethtool = mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4, |
| .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_200GAUI_4_200GBASE_CR4_KR4_LEN, |
| .mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_4X | |
| MLXSW_SP_PORT_MASK_WIDTH_8X, |
| .speed = SPEED_200000, |
| .width = 4, |
| }, |
| { |
| .mask = MLXSW_REG_PTYS_EXT_ETH_SPEED_400GAUI_8, |
| .mask_ethtool = mlxsw_sp2_mask_ethtool_400gaui_8, |
| .m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_400GAUI_8_LEN, |
| .mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_8X, |
| .speed = SPEED_400000, |
| .width = 8, |
| }, |
| }; |
| |
| #define MLXSW_SP2_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sp2_port_link_mode) |
| |
| static void |
| mlxsw_sp2_from_ptys_supported_port(struct mlxsw_sp *mlxsw_sp, |
| u32 ptys_eth_proto, |
| struct ethtool_link_ksettings *cmd) |
| { |
| ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); |
| ethtool_link_ksettings_add_link_mode(cmd, supported, Backplane); |
| } |
| |
| static void |
| mlxsw_sp2_set_bit_ethtool(const struct mlxsw_sp2_port_link_mode *link_mode, |
| unsigned long *mode) |
| { |
| int i; |
| |
| for (i = 0; i < link_mode->m_ethtool_len; i++) |
| __set_bit(link_mode->mask_ethtool[i], mode); |
| } |
| |
| static void |
| mlxsw_sp2_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto, |
| unsigned long *mode) |
| { |
| int i; |
| |
| for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { |
| if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) |
| mlxsw_sp2_set_bit_ethtool(&mlxsw_sp2_port_link_mode[i], |
| mode); |
| } |
| } |
| |
| static u32 |
| mlxsw_sp2_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto) |
| { |
| int i; |
| |
| for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { |
| if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) |
| return mlxsw_sp2_port_link_mode[i].speed; |
| } |
| |
| return SPEED_UNKNOWN; |
| } |
| |
| static void |
| mlxsw_sp2_from_ptys_link_mode(struct mlxsw_sp *mlxsw_sp, bool carrier_ok, |
| u32 ptys_eth_proto, |
| struct ethtool_link_ksettings *cmd) |
| { |
| struct mlxsw_sp2_port_link_mode link; |
| int i; |
| |
| cmd->base.speed = SPEED_UNKNOWN; |
| cmd->base.duplex = DUPLEX_UNKNOWN; |
| cmd->lanes = 0; |
| |
| if (!carrier_ok) |
| return; |
| |
| for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { |
| if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) { |
| link = mlxsw_sp2_port_link_mode[i]; |
| ethtool_params_from_link_mode(cmd, |
| link.mask_ethtool[1]); |
| } |
| } |
| } |
| |
| static int mlxsw_sp2_ptys_max_speed(struct mlxsw_sp_port *mlxsw_sp_port, u32 *p_max_speed) |
| { |
| u32 eth_proto_cap; |
| u32 max_speed = 0; |
| int err; |
| int i; |
| |
| err = mlxsw_sp_port_ptys_query(mlxsw_sp_port, ð_proto_cap, NULL, NULL, NULL); |
| if (err) |
| return err; |
| |
| for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { |
| if ((eth_proto_cap & mlxsw_sp2_port_link_mode[i].mask) && |
| mlxsw_sp2_port_link_mode[i].speed > max_speed) |
| max_speed = mlxsw_sp2_port_link_mode[i].speed; |
| } |
| |
| *p_max_speed = max_speed; |
| return 0; |
| } |
| |
| static bool |
| mlxsw_sp2_test_bit_ethtool(const struct mlxsw_sp2_port_link_mode *link_mode, |
| const unsigned long *mode) |
| { |
| int cnt = 0; |
| int i; |
| |
| for (i = 0; i < link_mode->m_ethtool_len; i++) { |
| if (test_bit(link_mode->mask_ethtool[i], mode)) |
| cnt++; |
| } |
| |
| return cnt == link_mode->m_ethtool_len; |
| } |
| |
| static u32 |
| mlxsw_sp2_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp, |
| const struct ethtool_link_ksettings *cmd) |
| { |
| u32 ptys_proto = 0; |
| int i; |
| |
| for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { |
| if (mlxsw_sp2_test_bit_ethtool(&mlxsw_sp2_port_link_mode[i], |
| cmd->link_modes.advertising)) |
| ptys_proto |= mlxsw_sp2_port_link_mode[i].mask; |
| } |
| return ptys_proto; |
| } |
| |
| static u32 mlxsw_sp2_to_ptys_speed_lanes(struct mlxsw_sp *mlxsw_sp, u8 width, |
| const struct ethtool_link_ksettings *cmd) |
| { |
| u8 mask_width = mlxsw_sp_port_mask_width_get(width); |
| struct mlxsw_sp2_port_link_mode link_mode; |
| u32 ptys_proto = 0; |
| int i; |
| |
| if (cmd->lanes > width) |
| return ptys_proto; |
| |
| for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { |
| if (cmd->base.speed == mlxsw_sp2_port_link_mode[i].speed) { |
| link_mode = mlxsw_sp2_port_link_mode[i]; |
| |
| if (!cmd->lanes) { |
| /* If number of lanes was not set by user space, |
| * choose the link mode that supports the width |
| * of the port. |
| */ |
| if (mask_width & link_mode.mask_sup_width) |
| ptys_proto |= link_mode.mask; |
| } else if (cmd->lanes == link_mode.width) { |
| /* Else if the number of lanes was set, choose |
| * the link mode that its actual width equals to |
| * it. |
| */ |
| ptys_proto |= link_mode.mask; |
| } |
| } |
| } |
| return ptys_proto; |
| } |
| |
| static void |
| mlxsw_sp2_reg_ptys_eth_pack(struct mlxsw_sp *mlxsw_sp, char *payload, |
| u16 local_port, u32 proto_admin, |
| bool autoneg) |
| { |
| mlxsw_reg_ptys_ext_eth_pack(payload, local_port, proto_admin, autoneg); |
| } |
| |
| static void |
| mlxsw_sp2_reg_ptys_eth_unpack(struct mlxsw_sp *mlxsw_sp, char *payload, |
| u32 *p_eth_proto_cap, u32 *p_eth_proto_admin, |
| u32 *p_eth_proto_oper) |
| { |
| mlxsw_reg_ptys_ext_eth_unpack(payload, p_eth_proto_cap, |
| p_eth_proto_admin, p_eth_proto_oper); |
| } |
| |
| static u32 mlxsw_sp2_ptys_proto_cap_masked_get(u32 eth_proto_cap) |
| { |
| u32 ptys_proto_cap_masked = 0; |
| int i; |
| |
| for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { |
| if (mlxsw_sp2_port_link_mode[i].mask & eth_proto_cap) |
| ptys_proto_cap_masked |= |
| mlxsw_sp2_port_link_mode[i].mask; |
| } |
| |
| return ptys_proto_cap_masked; |
| } |
| |
| const struct mlxsw_sp_port_type_speed_ops mlxsw_sp2_port_type_speed_ops = { |
| .from_ptys_supported_port = mlxsw_sp2_from_ptys_supported_port, |
| .from_ptys_link = mlxsw_sp2_from_ptys_link, |
| .from_ptys_speed = mlxsw_sp2_from_ptys_speed, |
| .from_ptys_link_mode = mlxsw_sp2_from_ptys_link_mode, |
| .ptys_max_speed = mlxsw_sp2_ptys_max_speed, |
| .to_ptys_advert_link = mlxsw_sp2_to_ptys_advert_link, |
| .to_ptys_speed_lanes = mlxsw_sp2_to_ptys_speed_lanes, |
| .reg_ptys_eth_pack = mlxsw_sp2_reg_ptys_eth_pack, |
| .reg_ptys_eth_unpack = mlxsw_sp2_reg_ptys_eth_unpack, |
| .ptys_proto_cap_masked_get = mlxsw_sp2_ptys_proto_cap_masked_get, |
| }; |