Commit 2dac017d authored by David S. Miller's avatar David S. Miller

Merge branch 'Add-ethtool-extended-link-state'

Ido Schimmel says:

====================
Add ethtool extended link state

Amit says:

Currently, device drivers can only indicate to user space if the network
link is up or down, without additional information.

This patch set provides an infrastructure that allows these drivers to
expose more information to user space about the link state. The
information can save users' time when trying to understand why a link is
not operationally up, for example.

The above is achieved by extending the existing ethtool LINKSTATE_GET
command with attributes that carry the extended state.

For example, no link due to missing cable:

$ ethtool ethX
...
Link detected: no (No cable)

Beside the general extended state, drivers can pass additional
information about the link state using the sub-state field. For example:

$ ethtool ethX
...
Link detected: no (Autoneg, No partner detected)

In the future the infrastructure can be extended - for example - to
allow PHY drivers to report whether a downshift to a lower speed
occurred. Something like:

$ ethtool ethX
...
Link detected: yes (downshifted)

Patch set overview:

Patches #1-#3 move mlxsw ethtool code to a separate file
Patches #4-#5 add the ethtool infrastructure for extended link state
Patches #6-#7 add support of extended link state in the mlxsw driver
Patches #8-#10 add test cases

Changes since v1:

* In documentation, show ETHTOOL_LINK_EXT_STATE_* and
  ETHTOOL_LINK_EXT_SUBSTATE_* constants instead of user-space strings
* Add `_CI_` to cable_issue substates to be consistent with
  other substates
* Keep the commit messages within 75 columns
* Use u8 variable for __link_ext_substate
* Document the meaning of -ENODATA in get_link_ext_state() callback
  description
* Do not zero data->link_ext_state_provided after getting an error
* Use `ret` variable for error value

Changes since RFC:

* Move documentation patch before ethtool patch
* Add nla_total_size() instead of sizeof() directly
* Return an error code from linkstate_get_ext_state()
* Remove SHORTED_CABLE, add CABLE_TEST_FAILURE instead
* Check if the interface is administratively up before setting ext_state
* Document all sub-states
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5fb62372 7d10bcce
......@@ -443,10 +443,11 @@ supports.
LINKSTATE_GET
=============
Requests link state information. At the moment, only link up/down flag (as
provided by ``ETHTOOL_GLINK`` ioctl command) is provided but some future
extensions are planned (e.g. link down reason). This request does not have any
attributes.
Requests link state information. Link up/down flag (as provided by
``ETHTOOL_GLINK`` ioctl command) is provided. Optionally, extended state might
be provided as well. In general, extended state describes reasons for why a port
is down, or why it operates in some non-obvious mode. This request does not have
any attributes.
Request contents:
......@@ -461,16 +462,135 @@ Kernel response contents:
``ETHTOOL_A_LINKSTATE_LINK`` bool link state (up/down)
``ETHTOOL_A_LINKSTATE_SQI`` u32 Current Signal Quality Index
``ETHTOOL_A_LINKSTATE_SQI_MAX`` u32 Max support SQI value
``ETHTOOL_A_LINKSTATE_EXT_STATE`` u8 link extended state
``ETHTOOL_A_LINKSTATE_EXT_SUBSTATE`` u8 link extended substate
==================================== ====== ============================
For most NIC drivers, the value of ``ETHTOOL_A_LINKSTATE_LINK`` returns
carrier flag provided by ``netif_carrier_ok()`` but there are drivers which
define their own handler.
``ETHTOOL_A_LINKSTATE_EXT_STATE`` and ``ETHTOOL_A_LINKSTATE_EXT_SUBSTATE`` are
optional values. ethtool core can provide either both
``ETHTOOL_A_LINKSTATE_EXT_STATE`` and ``ETHTOOL_A_LINKSTATE_EXT_SUBSTATE``,
or only ``ETHTOOL_A_LINKSTATE_EXT_STATE``, or none of them.
``LINKSTATE_GET`` allows dump requests (kernel returns reply messages for all
devices supporting the request).
Link extended states:
================================================ ============================================
``ETHTOOL_LINK_EXT_STATE_AUTONEG`` States relating to the autonegotiation or
issues therein
``ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE`` Failure during link training
``ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH`` Logical mismatch in physical coding sublayer
or forward error correction sublayer
``ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY`` Signal integrity issues
``ETHTOOL_LINK_EXT_STATE_NO_CABLE`` No cable connected
``ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE`` Failure is related to cable,
e.g., unsupported cable
``ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE`` Failure is related to EEPROM, e.g., failure
during reading or parsing the data
``ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE`` Failure during calibration algorithm
``ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED`` The hardware is not able to provide the
power required from cable or module
``ETHTOOL_LINK_EXT_STATE_OVERHEAT`` The module is overheated
================================================= ============================================
Link extended substates:
Autoneg substates:
============================================================== ================================
``ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED`` Peer side is down
``ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED`` Ack not received from peer side
``ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED`` Next page exchange failed
``ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE`` Peer side is down during force
mode or there is no agreement of
speed
``ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE`` Forward error correction modes
in both sides are mismatched
``ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD`` No Highest Common Denominator
============================================================== ================================
Link training substates:
========================================================================== ====================
``ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED`` Frames were not
recognized, the
lock failed
``ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT`` The lock did not
occur before
timeout
``ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY`` Peer side did not
send ready signal
after training
process
``ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT`` Remote side is not
ready yet
========================================================================== ====================
Link logical mismatch substates:
=============================================================== ===============================
``ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK`` Physical coding sublayer was
not locked in first phase -
block lock
``ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK`` Physical coding sublayer was
not locked in second phase -
alignment markers lock
``ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS`` Physical coding sublayer did
not get align status
``ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED`` FC forward error correction is
not locked
``ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED`` RS forward error correction is
not locked
=============================================================== ===============================
Bad signal integrity substates:
================================================================= =============================
``ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS`` Large number of physical
errors
``ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE`` The system attempted to
operate the cable at a rate
that is not formally
supported, which led to
signal integrity issues
================================================================= =============================
Cable issue substates:
================================================== ============================================
``ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE`` Unsupported cable
``ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE`` Cable test failure
================================================== ============================================
DEBUG_GET
=========
......
......@@ -30,7 +30,8 @@ mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \
spectrum_mr_tcam.o spectrum_mr.o \
spectrum_qdisc.o spectrum_span.o \
spectrum_nve.o spectrum_nve_vxlan.o \
spectrum_dpipe.o spectrum_trap.o
spectrum_dpipe.o spectrum_trap.o \
spectrum_ethtool.o
mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB) += spectrum_dcb.o
mlxsw_spectrum-$(CONFIG_PTP_1588_CLOCK) += spectrum_ptp.o
obj-$(CONFIG_MLXSW_MINIMAL) += mlxsw_minimal.o
......
......@@ -5438,6 +5438,56 @@ static inline void mlxsw_reg_pplr_pack(char *payload, u8 local_port,
MLXSW_REG_PPLR_LB_TYPE_BIT_PHY_LOCAL : 0);
}
/* PDDR - Port Diagnostics Database Register
* -----------------------------------------
* The PDDR enables to read the Phy debug database
*/
#define MLXSW_REG_PDDR_ID 0x5031
#define MLXSW_REG_PDDR_LEN 0x100
MLXSW_REG_DEFINE(pddr, MLXSW_REG_PDDR_ID, MLXSW_REG_PDDR_LEN);
/* reg_pddr_local_port
* Local port number.
* Access: Index
*/
MLXSW_ITEM32(reg, pddr, local_port, 0x00, 16, 8);
enum mlxsw_reg_pddr_page_select {
MLXSW_REG_PDDR_PAGE_SELECT_TROUBLESHOOTING_INFO = 1,
};
/* reg_pddr_page_select
* Page select index.
* Access: Index
*/
MLXSW_ITEM32(reg, pddr, page_select, 0x04, 0, 8);
enum mlxsw_reg_pddr_trblsh_group_opcode {
/* Monitor opcodes */
MLXSW_REG_PDDR_TRBLSH_GROUP_OPCODE_MONITOR,
};
/* reg_pddr_group_opcode
* Group selector.
* Access: Index
*/
MLXSW_ITEM32(reg, pddr, trblsh_group_opcode, 0x08, 0, 16);
/* reg_pddr_status_opcode
* Group selector.
* Access: RO
*/
MLXSW_ITEM32(reg, pddr, trblsh_status_opcode, 0x0C, 0, 16);
static inline void mlxsw_reg_pddr_pack(char *payload, u8 local_port,
u8 page_select)
{
MLXSW_REG_ZERO(pddr, payload);
mlxsw_reg_pddr_local_port_set(payload, local_port);
mlxsw_reg_pddr_page_select_set(payload, page_select);
}
/* PMTM - Port Module Type Mapping Register
* ----------------------------------------
* The PMTM allows query or configuration of module types.
......@@ -10758,6 +10808,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = {
MLXSW_REG(pbmc),
MLXSW_REG(pspa),
MLXSW_REG(pplr),
MLXSW_REG(pddr),
MLXSW_REG(pmtm),
MLXSW_REG(htgt),
MLXSW_REG(hpkt),
......
......@@ -94,7 +94,6 @@ static const struct mlxsw_fw_rev mlxsw_sp3_fw_rev = {
static const char mlxsw_sp1_driver_name[] = "mlxsw_spectrum";
static const char mlxsw_sp2_driver_name[] = "mlxsw_spectrum2";
static const char mlxsw_sp3_driver_name[] = "mlxsw_spectrum3";
static const char mlxsw_sp_driver_version[] = "1.0";
static const unsigned char mlxsw_sp1_mac_mask[ETH_ALEN] = {
0xff, 0xff, 0xff, 0xff, 0xfc, 0x00
......@@ -176,39 +175,6 @@ struct mlxsw_sp_mlxfw_dev {
struct mlxsw_sp *mlxsw_sp;
};
struct mlxsw_sp_ptp_ops {
struct mlxsw_sp_ptp_clock *
(*clock_init)(struct mlxsw_sp *mlxsw_sp, struct device *dev);
void (*clock_fini)(struct mlxsw_sp_ptp_clock *clock);
struct mlxsw_sp_ptp_state *(*init)(struct mlxsw_sp *mlxsw_sp);
void (*fini)(struct mlxsw_sp_ptp_state *ptp_state);
/* Notify a driver that a packet that might be PTP was received. Driver
* is responsible for freeing the passed-in SKB.
*/
void (*receive)(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
u8 local_port);
/* Notify a driver that a timestamped packet was transmitted. Driver
* is responsible for freeing the passed-in SKB.
*/
void (*transmitted)(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
u8 local_port);
int (*hwtstamp_get)(struct mlxsw_sp_port *mlxsw_sp_port,
struct hwtstamp_config *config);
int (*hwtstamp_set)(struct mlxsw_sp_port *mlxsw_sp_port,
struct hwtstamp_config *config);
void (*shaper_work)(struct work_struct *work);
int (*get_ts_info)(struct mlxsw_sp *mlxsw_sp,
struct ethtool_ts_info *info);
int (*get_stats_count)(void);
void (*get_stats_strings)(u8 **p);
void (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
u64 *data, int data_index);
};
struct mlxsw_sp_span_ops {
u32 (*buffsize_get)(int mtu, u32 speed);
};
......@@ -595,8 +561,8 @@ static int mlxsw_sp_base_mac_get(struct mlxsw_sp *mlxsw_sp)
return 0;
}
static int mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port,
bool is_up)
int mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port,
bool is_up)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
char paos_pl[MLXSW_REG_PAOS_LEN];
......@@ -1010,8 +976,8 @@ int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu,
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl);
}
static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
int mtu, bool pause_en)
int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
int mtu, bool pause_en)
{
u8 def_prio_tc[IEEE_8021QAZ_MAX_TCS] = {0};
bool dcb_en = !!mlxsw_sp_port->dcb.ets;
......@@ -1103,8 +1069,8 @@ static int mlxsw_sp_port_get_offload_stats(int attr_id, const struct net_device
return -EINVAL;
}
static int mlxsw_sp_port_get_stats_raw(struct net_device *dev, int grp,
int prio, char *ppcnt_pl)
int mlxsw_sp_port_get_stats_raw(struct net_device *dev, int grp,
int prio, char *ppcnt_pl)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
......@@ -1562,1500 +1528,6 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = {
.ndo_do_ioctl = mlxsw_sp_port_ioctl,
};
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));
}
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);
}
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;
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;
}
err = mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, pause_en);
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:
pause_en = mlxsw_sp_port_is_pause_en(mlxsw_sp_port);
mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, pause_en);
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)
#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))
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);
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_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();
}
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;
}
}
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_100BASE_T,
.mask_ethtool = ETHTOOL_LINK_MODE_100baseT_Full_BIT,
.speed = SPEED_100,
},
{
.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_T,
.mask_ethtool = ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
.speed = SPEED_10000,
},
{
.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_20GBASE_KR2,
.mask_ethtool = ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
.speed = SPEED_20000,
},
{
.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 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 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,
.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,
};
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_2_5gbase_x_2_5gmii[] = {
ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
};
#define MLXSW_SP2_MASK_ETHTOOL_2_5GBASE_X_2_5GMII_LEN \
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_2_5gbase_x_2_5gmii)
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_2_5GBASE_X_2_5GMII,
.mask_ethtool = mlxsw_sp2_mask_ethtool_2_5gbase_x_2_5gmii,
.m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_2_5GBASE_X_2_5GMII_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_2500,
},
{
.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 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 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,
.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,
};
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_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;
char ptys_pl[MLXSW_REG_PTYS_LEN];
u8 connector_type;
bool autoneg;
int err;
ops = mlxsw_sp->port_type_speed_ops;
autoneg = mlxsw_sp_port->link.autoneg;
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, &eth_proto_cap,
&eth_proto_admin, &eth_proto_oper);
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;
connector_type = mlxsw_reg_ptys_connector_type_get(ptys_pl);
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, &eth_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);
}
static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
.get_drvinfo = mlxsw_sp_port_get_drvinfo,
.get_link = ethtool_op_get_link,
.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,
};
static int
mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port)
{
......
......@@ -178,6 +178,39 @@ struct mlxsw_sp {
u32 lowest_shaper_bs;
};
struct mlxsw_sp_ptp_ops {
struct mlxsw_sp_ptp_clock *
(*clock_init)(struct mlxsw_sp *mlxsw_sp, struct device *dev);
void (*clock_fini)(struct mlxsw_sp_ptp_clock *clock);
struct mlxsw_sp_ptp_state *(*init)(struct mlxsw_sp *mlxsw_sp);
void (*fini)(struct mlxsw_sp_ptp_state *ptp_state);
/* Notify a driver that a packet that might be PTP was received. Driver
* is responsible for freeing the passed-in SKB.
*/
void (*receive)(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
u8 local_port);
/* Notify a driver that a timestamped packet was transmitted. Driver
* is responsible for freeing the passed-in SKB.
*/
void (*transmitted)(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
u8 local_port);
int (*hwtstamp_get)(struct mlxsw_sp_port *mlxsw_sp_port,
struct hwtstamp_config *config);
int (*hwtstamp_set)(struct mlxsw_sp_port *mlxsw_sp_port,
struct hwtstamp_config *config);
void (*shaper_work)(struct work_struct *work);
int (*get_ts_info)(struct mlxsw_sp *mlxsw_sp,
struct ethtool_ts_info *info);
int (*get_stats_count)(void);
void (*get_stats_strings)(u8 **p);
void (*get_stats)(struct mlxsw_sp_port *mlxsw_sp_port,
u64 *data, int data_index);
};
static inline struct mlxsw_sp_upper *
mlxsw_sp_lag_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
{
......@@ -393,6 +426,13 @@ enum mlxsw_sp_flood_type {
MLXSW_SP_FLOOD_TYPE_MC,
};
int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
int mtu, bool pause_en);
int mlxsw_sp_port_get_stats_raw(struct net_device *dev, int grp,
int prio, char *ppcnt_pl);
int mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port,
bool is_up);
/* spectrum_buffers.c */
int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp);
void mlxsw_sp_buffers_fini(struct mlxsw_sp *mlxsw_sp);
......@@ -1126,4 +1166,9 @@ static inline struct net *mlxsw_sp_net(struct mlxsw_sp *mlxsw_sp)
return mlxsw_core_net(mlxsw_sp->core);
}
/* spectrum_ethtool.c */
extern const struct ethtool_ops mlxsw_sp_port_ethtool_ops;
extern const struct mlxsw_sp_port_type_speed_ops mlxsw_sp1_port_type_speed_ops;
extern const struct mlxsw_sp_port_type_speed_ops mlxsw_sp2_port_type_speed_ops;
#endif
......@@ -110,8 +110,8 @@ static int mlxsw_sp_port_pg_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl);
}
static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
struct ieee_ets *ets)
static int mlxsw_sp_port_headroom_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,
struct ieee_ets *ets)
{
bool pause_en = mlxsw_sp_port_is_pause_en(mlxsw_sp_port);
struct ieee_ets *my_ets = mlxsw_sp_port->dcb.ets;
......@@ -180,7 +180,7 @@ static int __mlxsw_sp_dcbnl_ieee_setets(struct mlxsw_sp_port *mlxsw_sp_port,
}
/* Ingress configuration. */
err = mlxsw_sp_port_headroom_set(mlxsw_sp_port, ets);
err = mlxsw_sp_port_headroom_ets_set(mlxsw_sp_port, ets);
if (err)
goto err_port_headroom_set;
......
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2020 Mellanox Technologies. All rights reserved */
#include "reg.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);
}
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;
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;
}
err = mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, pause_en);
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:
pause_en = mlxsw_sp_port_is_pause_en(mlxsw_sp_port);
mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, pause_en);
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)
#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))
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);
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_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();
}
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_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;
char ptys_pl[MLXSW_REG_PTYS_LEN];
u8 connector_type;
bool autoneg;
int err;
ops = mlxsw_sp->port_type_speed_ops;
autoneg = mlxsw_sp_port->link.autoneg;
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, &eth_proto_cap,
&eth_proto_admin, &eth_proto_oper);
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;
connector_type = mlxsw_reg_ptys_connector_type_get(ptys_pl);
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, &eth_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_100BASE_T,
.mask_ethtool = ETHTOOL_LINK_MODE_100baseT_Full_BIT,
.speed = SPEED_100,
},
{
.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_T,
.mask_ethtool = ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
.speed = SPEED_10000,
},
{
.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_20GBASE_KR2,
.mask_ethtool = ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
.speed = SPEED_20000,
},
{
.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 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);
}
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,
.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,
};
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_2_5gbase_x_2_5gmii[] = {
ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
};
#define MLXSW_SP2_MASK_ETHTOOL_2_5GBASE_X_2_5GMII_LEN \
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_2_5gbase_x_2_5gmii)
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_2_5GBASE_X_2_5GMII,
.mask_ethtool = mlxsw_sp2_mask_ethtool_2_5gbase_x_2_5gmii,
.m_ethtool_len = MLXSW_SP2_MASK_ETHTOOL_2_5GBASE_X_2_5GMII_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_2500,
},
{
.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 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);
}
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,
.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,
};
......@@ -86,6 +86,22 @@ struct net_device;
u32 ethtool_op_get_link(struct net_device *dev);
int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *eti);
/**
* struct ethtool_link_ext_state_info - link extended state and substate.
*/
struct ethtool_link_ext_state_info {
enum ethtool_link_ext_state link_ext_state;
union {
enum ethtool_link_ext_substate_autoneg autoneg;
enum ethtool_link_ext_substate_link_training link_training;
enum ethtool_link_ext_substate_link_logical_mismatch link_logical_mismatch;
enum ethtool_link_ext_substate_bad_signal_integrity bad_signal_integrity;
enum ethtool_link_ext_substate_cable_issue cable_issue;
u8 __link_ext_substate;
};
};
/**
* ethtool_rxfh_indir_default - get default value for RX flow hash indirection
* @index: Index in RX flow hash indirection table
......@@ -245,6 +261,11 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
* @get_link: Report whether physical link is up. Will only be called if
* the netdev is up. Should usually be set to ethtool_op_get_link(),
* which uses netif_carrier_ok().
* @get_link_ext_state: Report link extended state. Should set link_ext_state and
* link_ext_substate (link_ext_substate of 0 means link_ext_substate is unknown,
* do not attach ext_substate attribute to netlink message). If link_ext_state
* and link_ext_substate are unknown, return -ENODATA. If not implemented,
* link_ext_state and link_ext_substate will not be sent to userspace.
* @get_eeprom: Read data from the device EEPROM.
* Should fill in the magic field. Don't need to check len for zero
* or wraparound. Fill in the data argument with the eeprom values
......@@ -384,6 +405,8 @@ struct ethtool_ops {
void (*set_msglevel)(struct net_device *, u32);
int (*nway_reset)(struct net_device *);
u32 (*get_link)(struct net_device *);
int (*get_link_ext_state)(struct net_device *,
struct ethtool_link_ext_state_info *);
int (*get_eeprom_len)(struct net_device *);
int (*get_eeprom)(struct net_device *,
struct ethtool_eeprom *, u8 *);
......
......@@ -579,6 +579,76 @@ struct ethtool_pauseparam {
__u32 tx_pause;
};
/**
* enum ethtool_link_ext_state - link extended state
*/
enum ethtool_link_ext_state {
ETHTOOL_LINK_EXT_STATE_AUTONEG,
ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE,
ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH,
ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY,
ETHTOOL_LINK_EXT_STATE_NO_CABLE,
ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE,
ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE,
ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE,
ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED,
ETHTOOL_LINK_EXT_STATE_OVERHEAT,
};
/**
* enum ethtool_link_ext_substate_autoneg - more information in addition to
* ETHTOOL_LINK_EXT_STATE_AUTONEG.
*/
enum ethtool_link_ext_substate_autoneg {
ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED = 1,
ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED,
ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED,
ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE,
ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE,
ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD,
};
/**
* enum ethtool_link_ext_substate_link_training - more information in addition to
* ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE.
*/
enum ethtool_link_ext_substate_link_training {
ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED = 1,
ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT,
ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY,
ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT,
};
/**
* enum ethtool_link_ext_substate_logical_mismatch - more information in addition
* to ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH.
*/
enum ethtool_link_ext_substate_link_logical_mismatch {
ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK = 1,
ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK,
ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS,
ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED,
ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED,
};
/**
* enum ethtool_link_ext_substate_bad_signal_integrity - more information in
* addition to ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY.
*/
enum ethtool_link_ext_substate_bad_signal_integrity {
ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS = 1,
ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE,
};
/**
* enum ethtool_link_ext_substate_cable_issue - more information in
* addition to ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE.
*/
enum ethtool_link_ext_substate_cable_issue {
ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE = 1,
ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE,
};
#define ETH_GSTRING_LEN 32
/**
......
......@@ -236,6 +236,8 @@ enum {
ETHTOOL_A_LINKSTATE_LINK, /* u8 */
ETHTOOL_A_LINKSTATE_SQI, /* u32 */
ETHTOOL_A_LINKSTATE_SQI_MAX, /* u32 */
ETHTOOL_A_LINKSTATE_EXT_STATE, /* u8 */
ETHTOOL_A_LINKSTATE_EXT_SUBSTATE, /* u8 */
/* add new constants above here */
__ETHTOOL_A_LINKSTATE_CNT,
......
......@@ -9,10 +9,12 @@ struct linkstate_req_info {
};
struct linkstate_reply_data {
struct ethnl_reply_data base;
int link;
int sqi;
int sqi_max;
struct ethnl_reply_data base;
int link;
int sqi;
int sqi_max;
bool link_ext_state_provided;
struct ethtool_link_ext_state_info ethtool_link_ext_state_info;
};
#define LINKSTATE_REPDATA(__reply_base) \
......@@ -25,6 +27,8 @@ linkstate_get_policy[ETHTOOL_A_LINKSTATE_MAX + 1] = {
[ETHTOOL_A_LINKSTATE_LINK] = { .type = NLA_REJECT },
[ETHTOOL_A_LINKSTATE_SQI] = { .type = NLA_REJECT },
[ETHTOOL_A_LINKSTATE_SQI_MAX] = { .type = NLA_REJECT },
[ETHTOOL_A_LINKSTATE_EXT_STATE] = { .type = NLA_REJECT },
[ETHTOOL_A_LINKSTATE_EXT_SUBSTATE] = { .type = NLA_REJECT },
};
static int linkstate_get_sqi(struct net_device *dev)
......@@ -61,6 +65,23 @@ static int linkstate_get_sqi_max(struct net_device *dev)
mutex_unlock(&phydev->lock);
return ret;
};
static int linkstate_get_link_ext_state(struct net_device *dev,
struct linkstate_reply_data *data)
{
int err;
if (!dev->ethtool_ops->get_link_ext_state)
return -EOPNOTSUPP;
err = dev->ethtool_ops->get_link_ext_state(dev, &data->ethtool_link_ext_state_info);
if (err)
return err;
data->link_ext_state_provided = true;
return 0;
}
static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
......@@ -86,6 +107,12 @@ static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
goto out;
data->sqi_max = ret;
if (dev->flags & IFF_UP) {
ret = linkstate_get_link_ext_state(dev, data);
if (ret < 0 && ret != -EOPNOTSUPP && ret != -ENODATA)
goto out;
}
ret = 0;
out:
ethnl_ops_complete(dev);
......@@ -107,6 +134,12 @@ static int linkstate_reply_size(const struct ethnl_req_info *req_base,
if (data->sqi_max != -EOPNOTSUPP)
len += nla_total_size(sizeof(u32));
if (data->link_ext_state_provided)
len += nla_total_size(sizeof(u8)); /* LINKSTATE_EXT_STATE */
if (data->ethtool_link_ext_state_info.__link_ext_substate)
len += nla_total_size(sizeof(u8)); /* LINKSTATE_EXT_SUBSTATE */
return len;
}
......@@ -128,6 +161,17 @@ static int linkstate_fill_reply(struct sk_buff *skb,
nla_put_u32(skb, ETHTOOL_A_LINKSTATE_SQI_MAX, data->sqi_max))
return -EMSGSIZE;
if (data->link_ext_state_provided) {
if (nla_put_u8(skb, ETHTOOL_A_LINKSTATE_EXT_STATE,
data->ethtool_link_ext_state_info.link_ext_state))
return -EMSGSIZE;
if (data->ethtool_link_ext_state_info.__link_ext_substate &&
nla_put_u8(skb, ETHTOOL_A_LINKSTATE_EXT_SUBSTATE,
data->ethtool_link_ext_state_info.__link_ext_substate))
return -EMSGSIZE;
}
return 0;
}
......
......@@ -50,23 +50,6 @@ cleanup()
h1_destroy
}
different_speeds_get()
{
local dev1=$1; shift
local dev2=$1; shift
local with_mode=$1; shift
local adver=$1; shift
local -a speeds_arr
speeds_arr=($(common_speeds_get $dev1 $dev2 $with_mode $adver))
if [[ ${#speeds_arr[@]} < 2 ]]; then
check_err 1 "cannot check different speeds. There are not enough speeds"
fi
echo ${speeds_arr[0]} ${speeds_arr[1]}
}
same_speeds_autoneg_off()
{
# Check that when each of the reported speeds is forced, the links come
......
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
ALL_TESTS="
autoneg
autoneg_force_mode
no_cable
"
NUM_NETIFS=2
source lib.sh
source ethtool_lib.sh
setup_prepare()
{
swp1=${NETIFS[p1]}
swp2=${NETIFS[p2]}
swp3=$NETIF_NO_CABLE
}
ethtool_extended_state_check()
{
local dev=$1; shift
local expected_ext_state=$1; shift
local expected_ext_substate=${1:-""}; shift
local ext_state=$(ethtool $dev | grep "Link detected" \
| cut -d "(" -f2 | cut -d ")" -f1)
local ext_substate=$(echo $ext_state | cut -sd "," -f2 \
| sed -e 's/^[[:space:]]*//')
ext_state=$(echo $ext_state | cut -d "," -f1)
[[ $ext_state == $expected_ext_state ]]
check_err $? "Expected \"$expected_ext_state\", got \"$ext_state\""
[[ $ext_substate == $expected_ext_substate ]]
check_err $? "Expected \"$expected_ext_substate\", got \"$ext_substate\""
}
autoneg()
{
RET=0
ip link set dev $swp1 up
sleep 4
ethtool_extended_state_check $swp1 "Autoneg" "No partner detected"
log_test "Autoneg, No partner detected"
ip link set dev $swp1 down
}
autoneg_force_mode()
{
RET=0
ip link set dev $swp1 up
ip link set dev $swp2 up
local -a speeds_arr=($(different_speeds_get $swp1 $swp2 0 0))
local speed1=${speeds_arr[0]}
local speed2=${speeds_arr[1]}
ethtool_set $swp1 speed $speed1 autoneg off
ethtool_set $swp2 speed $speed2 autoneg off
sleep 4
ethtool_extended_state_check $swp1 "Autoneg" \
"No partner detected during force mode"
ethtool_extended_state_check $swp2 "Autoneg" \
"No partner detected during force mode"
log_test "Autoneg, No partner detected during force mode"
ethtool -s $swp2 autoneg on
ethtool -s $swp1 autoneg on
ip link set dev $swp2 down
ip link set dev $swp1 down
}
no_cable()
{
RET=0
ip link set dev $swp3 up
sleep 1
ethtool_extended_state_check $swp3 "No cable"
log_test "No cable"
ip link set dev $swp3 down
}
setup_prepare
tests_run
exit $EXIT_STATUS
......@@ -67,3 +67,20 @@ common_speeds_get()
<(printf '%s\n' "${dev1_speeds[@]}" | sort -u) \
<(printf '%s\n' "${dev2_speeds[@]}" | sort -u)
}
different_speeds_get()
{
local dev1=$1; shift
local dev2=$1; shift
local with_mode=$1; shift
local adver=$1; shift
local -a speeds_arr
speeds_arr=($(common_speeds_get $dev1 $dev2 $with_mode $adver))
if [[ ${#speeds_arr[@]} < 2 ]]; then
check_err 1 "cannot check different speeds. There are not enough speeds"
fi
echo ${speeds_arr[0]} ${speeds_arr[1]}
}
......@@ -14,6 +14,9 @@ NETIFS[p6]=veth5
NETIFS[p7]=veth6
NETIFS[p8]=veth7
# Port that does not have a cable connected.
NETIF_NO_CABLE=eth8
##############################################################################
# Defines
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment