// 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}, 
 | 
}; 
 | 
  
 | 
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; 
 | 
    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, 
 | 
                 u8 width, 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, width, 
 | 
                cmd->link_modes.supported); 
 | 
} 
 | 
  
 | 
static void 
 | 
mlxsw_sp_port_get_link_advertise(struct mlxsw_sp *mlxsw_sp, 
 | 
                 u32 eth_proto_admin, bool autoneg, u8 width, 
 | 
                 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, width, 
 | 
                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, 
 | 
                     mlxsw_sp_port->mapping.width, cmd); 
 | 
  
 | 
    mlxsw_sp_port_get_link_advertise(mlxsw_sp, eth_proto_admin, autoneg, 
 | 
                     mlxsw_sp_port->mapping.width, cmd); 
 | 
  
 | 
    cmd->base.autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; 
 | 
    cmd->base.port = mlxsw_sp_port_connector_port(connector_type); 
 | 
    ops->from_ptys_speed_duplex(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, mlxsw_sp_port->mapping.width, 
 | 
                     cmd) : 
 | 
        ops->to_ptys_speed(mlxsw_sp, mlxsw_sp_port->mapping.width, 
 | 
                   cmd->base.speed); 
 | 
  
 | 
    eth_proto_new = eth_proto_new & eth_proto_cap; 
 | 
    if (!eth_proto_new) { 
 | 
        netdev_err(dev, "No supported speed 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_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); 
 | 
} 
 | 
  
 | 
const struct ethtool_ops mlxsw_sp_port_ethtool_ops = { 
 | 
    .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_ts_info        = mlxsw_sp_get_ts_info, 
 | 
}; 
 | 
  
 | 
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, 
 | 
             u8 width, 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_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok, 
 | 
                 u32 ptys_eth_proto, 
 | 
                 struct ethtool_link_ksettings *cmd) 
 | 
{ 
 | 
    cmd->base.speed = SPEED_UNKNOWN; 
 | 
    cmd->base.duplex = DUPLEX_UNKNOWN; 
 | 
  
 | 
    if (!carrier_ok) 
 | 
        return; 
 | 
  
 | 
    cmd->base.speed = mlxsw_sp1_from_ptys_speed(mlxsw_sp, ptys_eth_proto); 
 | 
    if (cmd->base.speed != SPEED_UNKNOWN) 
 | 
        cmd->base.duplex = DUPLEX_FULL; 
 | 
} 
 | 
  
 | 
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, u8 width, 
 | 
                  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(struct mlxsw_sp *mlxsw_sp, u8 width, 
 | 
                   u32 speed) 
 | 
{ 
 | 
    u32 ptys_proto = 0; 
 | 
    int i; 
 | 
  
 | 
    for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) { 
 | 
        if (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, 
 | 
                u8 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_speed_duplex        = mlxsw_sp1_from_ptys_speed_duplex, 
 | 
    .ptys_max_speed            = mlxsw_sp1_ptys_max_speed, 
 | 
    .to_ptys_advert_link        = mlxsw_sp1_to_ptys_advert_link, 
 | 
    .to_ptys_speed            = mlxsw_sp1_to_ptys_speed, 
 | 
    .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; 
 | 
    u8 mask_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_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, 
 | 
    }, 
 | 
    { 
 | 
        .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_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, 
 | 
    }, 
 | 
    { 
 | 
        .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_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, 
 | 
    }, 
 | 
    { 
 | 
        .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_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, 
 | 
    }, 
 | 
    { 
 | 
        .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_width    = MLXSW_SP_PORT_MASK_WIDTH_4X | 
 | 
                  MLXSW_SP_PORT_MASK_WIDTH_8X, 
 | 
        .speed        = SPEED_40000, 
 | 
    }, 
 | 
    { 
 | 
        .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_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, 
 | 
    }, 
 | 
    { 
 | 
        .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_width    = MLXSW_SP_PORT_MASK_WIDTH_2X | 
 | 
                  MLXSW_SP_PORT_MASK_WIDTH_4X | 
 | 
                  MLXSW_SP_PORT_MASK_WIDTH_8X, 
 | 
        .speed        = SPEED_50000, 
 | 
    }, 
 | 
    { 
 | 
        .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_width    = MLXSW_SP_PORT_MASK_WIDTH_1X, 
 | 
        .speed        = SPEED_50000, 
 | 
    }, 
 | 
    { 
 | 
        .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_width    = MLXSW_SP_PORT_MASK_WIDTH_4X | 
 | 
                  MLXSW_SP_PORT_MASK_WIDTH_8X, 
 | 
        .speed        = SPEED_100000, 
 | 
    }, 
 | 
    { 
 | 
        .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_width    = MLXSW_SP_PORT_MASK_WIDTH_2X, 
 | 
        .speed        = SPEED_100000, 
 | 
    }, 
 | 
    { 
 | 
        .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_width    = MLXSW_SP_PORT_MASK_WIDTH_4X | 
 | 
                  MLXSW_SP_PORT_MASK_WIDTH_8X, 
 | 
        .speed        = SPEED_200000, 
 | 
    }, 
 | 
    { 
 | 
        .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_width    = MLXSW_SP_PORT_MASK_WIDTH_8X, 
 | 
        .speed        = SPEED_400000, 
 | 
    }, 
 | 
}; 
 | 
  
 | 
#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, 
 | 
             u8 width, unsigned long *mode) 
 | 
{ 
 | 
    u8 mask_width = mlxsw_sp_port_mask_width_get(width); 
 | 
    int i; 
 | 
  
 | 
    for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { 
 | 
        if ((ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) && 
 | 
            (mask_width & mlxsw_sp2_port_link_mode[i].mask_width)) 
 | 
            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_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok, 
 | 
                 u32 ptys_eth_proto, 
 | 
                 struct ethtool_link_ksettings *cmd) 
 | 
{ 
 | 
    cmd->base.speed = SPEED_UNKNOWN; 
 | 
    cmd->base.duplex = DUPLEX_UNKNOWN; 
 | 
  
 | 
    if (!carrier_ok) 
 | 
        return; 
 | 
  
 | 
    cmd->base.speed = mlxsw_sp2_from_ptys_speed(mlxsw_sp, ptys_eth_proto); 
 | 
    if (cmd->base.speed != SPEED_UNKNOWN) 
 | 
        cmd->base.duplex = DUPLEX_FULL; 
 | 
} 
 | 
  
 | 
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, u8 width, 
 | 
                  const struct ethtool_link_ksettings *cmd) 
 | 
{ 
 | 
    u8 mask_width = mlxsw_sp_port_mask_width_get(width); 
 | 
    u32 ptys_proto = 0; 
 | 
    int i; 
 | 
  
 | 
    for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { 
 | 
        if ((mask_width & mlxsw_sp2_port_link_mode[i].mask_width) && 
 | 
            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(struct mlxsw_sp *mlxsw_sp, 
 | 
                   u8 width, u32 speed) 
 | 
{ 
 | 
    u8 mask_width = mlxsw_sp_port_mask_width_get(width); 
 | 
    u32 ptys_proto = 0; 
 | 
    int i; 
 | 
  
 | 
    for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { 
 | 
        if ((speed == mlxsw_sp2_port_link_mode[i].speed) && 
 | 
            (mask_width & mlxsw_sp2_port_link_mode[i].mask_width)) 
 | 
            ptys_proto |= mlxsw_sp2_port_link_mode[i].mask; 
 | 
    } 
 | 
    return ptys_proto; 
 | 
} 
 | 
  
 | 
static void 
 | 
mlxsw_sp2_reg_ptys_eth_pack(struct mlxsw_sp *mlxsw_sp, char *payload, 
 | 
                u8 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_speed_duplex        = mlxsw_sp2_from_ptys_speed_duplex, 
 | 
    .ptys_max_speed            = mlxsw_sp2_ptys_max_speed, 
 | 
    .to_ptys_advert_link        = mlxsw_sp2_to_ptys_advert_link, 
 | 
    .to_ptys_speed            = mlxsw_sp2_to_ptys_speed, 
 | 
    .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, 
 | 
}; 
 |