2020-06-29 23:46:13 +03:00
|
|
|
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
|
|
|
|
|
/* Copyright (c) 2020 Mellanox Technologies. All rights reserved */
|
|
|
|
|
|
|
|
|
|
#include "reg.h"
|
2020-09-27 10:50:15 +03:00
|
|
|
#include "core.h"
|
2020-06-29 23:46:13 +03:00
|
|
|
#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));
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-29 23:46:18 +03:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-29 23:46:13 +03:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
mlxsw: spectrum: Unify delay handling between PFC and pause
When a priority is marked as lossless using DCB PFC, or when pause frames
are enabled on a port, mlxsw adds to port buffers an extra space to cover
the traffic that will arrive between the time that a pause or PFC frame is
emitted, and the time traffic actually stops. This is called the delay. The
concept is the same in PFC and pause, however the way the extra buffer
space is calculated differs.
In this patch, unify this handling. Delay is to be measured in bytes of
extra space, and will not include MTU. PFC handler sets the delay directly
from the parameter it gets through the DCB interface.
To convert pause handler, move MLXSW_SP_PAUSE_DELAY to ethtool module,
convert to bytes, and reduce it by maximum MTU, and divide by two. Then it
has the same meaning as the delay_bytes set by the PFC handler.
Keep the delay_bytes value in struct mlxsw_sp_hdroom introduced in the
previous patch. Change PFC and pause handlers to store the new delay value
there and have __mlxsw_sp_port_headroom_set() take it from there.
Instead of mlxsw_sp_pfc_delay_get() and mlxsw_sp_pg_buf_delay_get(),
introduce mlxsw_sp_hdroom_buf_delay_get() to calculate the delay provision.
Drop the unnecessary MLXSW_SP_CELL_FACTOR, and instead add an explanatory
comment describing the formula used.
Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-16 09:35:15 +03:00
|
|
|
/* Maximum delay buffer needed in case of PAUSE frames. Similar to PFC delay, but is
|
|
|
|
|
* measured in bytes. Assumes 100m cable and does not take into account MTU.
|
|
|
|
|
*/
|
|
|
|
|
#define MLXSW_SP_PAUSE_DELAY_BYTES 19476
|
|
|
|
|
|
2020-06-29 23:46:13 +03:00
|
|
|
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;
|
mlxsw: spectrum: Unify delay handling between PFC and pause
When a priority is marked as lossless using DCB PFC, or when pause frames
are enabled on a port, mlxsw adds to port buffers an extra space to cover
the traffic that will arrive between the time that a pause or PFC frame is
emitted, and the time traffic actually stops. This is called the delay. The
concept is the same in PFC and pause, however the way the extra buffer
space is calculated differs.
In this patch, unify this handling. Delay is to be measured in bytes of
extra space, and will not include MTU. PFC handler sets the delay directly
from the parameter it gets through the DCB interface.
To convert pause handler, move MLXSW_SP_PAUSE_DELAY to ethtool module,
convert to bytes, and reduce it by maximum MTU, and divide by two. Then it
has the same meaning as the delay_bytes set by the PFC handler.
Keep the delay_bytes value in struct mlxsw_sp_hdroom introduced in the
previous patch. Change PFC and pause handlers to store the new delay value
there and have __mlxsw_sp_port_headroom_set() take it from there.
Instead of mlxsw_sp_pfc_delay_get() and mlxsw_sp_pg_buf_delay_get(),
introduce mlxsw_sp_hdroom_buf_delay_get() to calculate the delay provision.
Drop the unnecessary MLXSW_SP_CELL_FACTOR, and instead add an explanatory
comment describing the formula used.
Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-16 09:35:15 +03:00
|
|
|
struct mlxsw_sp_hdroom orig_hdroom;
|
|
|
|
|
struct mlxsw_sp_hdroom hdroom;
|
2020-09-16 09:35:18 +03:00
|
|
|
int prio;
|
2020-06-29 23:46:13 +03:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
mlxsw: spectrum: Unify delay handling between PFC and pause
When a priority is marked as lossless using DCB PFC, or when pause frames
are enabled on a port, mlxsw adds to port buffers an extra space to cover
the traffic that will arrive between the time that a pause or PFC frame is
emitted, and the time traffic actually stops. This is called the delay. The
concept is the same in PFC and pause, however the way the extra buffer
space is calculated differs.
In this patch, unify this handling. Delay is to be measured in bytes of
extra space, and will not include MTU. PFC handler sets the delay directly
from the parameter it gets through the DCB interface.
To convert pause handler, move MLXSW_SP_PAUSE_DELAY to ethtool module,
convert to bytes, and reduce it by maximum MTU, and divide by two. Then it
has the same meaning as the delay_bytes set by the PFC handler.
Keep the delay_bytes value in struct mlxsw_sp_hdroom introduced in the
previous patch. Change PFC and pause handlers to store the new delay value
there and have __mlxsw_sp_port_headroom_set() take it from there.
Instead of mlxsw_sp_pfc_delay_get() and mlxsw_sp_pg_buf_delay_get(),
introduce mlxsw_sp_hdroom_buf_delay_get() to calculate the delay provision.
Drop the unnecessary MLXSW_SP_CELL_FACTOR, and instead add an explanatory
comment describing the formula used.
Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-16 09:35:15 +03:00
|
|
|
orig_hdroom = *mlxsw_sp_port->hdroom;
|
|
|
|
|
|
|
|
|
|
hdroom = orig_hdroom;
|
|
|
|
|
if (pause_en)
|
|
|
|
|
hdroom.delay_bytes = MLXSW_SP_PAUSE_DELAY_BYTES;
|
|
|
|
|
else
|
|
|
|
|
hdroom.delay_bytes = 0;
|
|
|
|
|
|
2020-09-16 09:35:18 +03:00
|
|
|
for (prio = 0; prio < IEEE_8021QAZ_MAX_TCS; prio++)
|
|
|
|
|
hdroom.prios.prio[prio].lossy = !pause_en;
|
|
|
|
|
|
|
|
|
|
mlxsw_sp_hdroom_bufs_reset_lossiness(&hdroom);
|
mlxsw: spectrum: Split headroom autoresize out of buffer configuration
Split mlxsw_sp_port_headroom_set() to three functions.
mlxsw_sp_hdroom_bufs_reset_sizes() changes the sizes of the individual PG
buffers, and mlxsw_sp_hdroom_configure_buffers() will actually apply the
configuration. A third function, mlxsw_sp_hdroom_bufs_fit(), verifies that
the requested buffer configuration matches total headroom size
requirements.
Add wrappers, mlxsw_sp_hdroom_configure() and __..., that will eventually
perform full headroom configuration, but for now, only have them verify the
configured headroom size, and invoke mlxsw_sp_hdroom_configure_buffers().
Have them take the `force` argument to prepare for a later patch, even
though it is currently unused.
Note that the loop in mlxsw_sp_hdroom_configure_buffers() only goes through
DCBX_MAX_BUFFERS. Since there is no logic to configure the control buffer,
it needs to keep the values queried from the FW. Eventually this function
should configure all the PGs.
Note that conversion of __mlxsw_sp_dcbnl_ieee_setets() is not trivial. That
function performs the headroom configuration in three steps: first it
resizes the buffers and adds any new ones. Then it redirects priorities to
the new buffers. And finally it sets the size of the now-unused buffers to
zero. This way no packet drops are introduced.
So after invoking mlxsw_sp_hdroom_bufs_reset_sizes(), tweak the
configuration to keep the old sizes of PG buffers for those buffers whose
size was set to zero.
Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-16 09:35:20 +03:00
|
|
|
mlxsw_sp_hdroom_bufs_reset_sizes(mlxsw_sp_port, &hdroom);
|
2020-09-16 09:35:18 +03:00
|
|
|
|
mlxsw: spectrum: Split headroom autoresize out of buffer configuration
Split mlxsw_sp_port_headroom_set() to three functions.
mlxsw_sp_hdroom_bufs_reset_sizes() changes the sizes of the individual PG
buffers, and mlxsw_sp_hdroom_configure_buffers() will actually apply the
configuration. A third function, mlxsw_sp_hdroom_bufs_fit(), verifies that
the requested buffer configuration matches total headroom size
requirements.
Add wrappers, mlxsw_sp_hdroom_configure() and __..., that will eventually
perform full headroom configuration, but for now, only have them verify the
configured headroom size, and invoke mlxsw_sp_hdroom_configure_buffers().
Have them take the `force` argument to prepare for a later patch, even
though it is currently unused.
Note that the loop in mlxsw_sp_hdroom_configure_buffers() only goes through
DCBX_MAX_BUFFERS. Since there is no logic to configure the control buffer,
it needs to keep the values queried from the FW. Eventually this function
should configure all the PGs.
Note that conversion of __mlxsw_sp_dcbnl_ieee_setets() is not trivial. That
function performs the headroom configuration in three steps: first it
resizes the buffers and adds any new ones. Then it redirects priorities to
the new buffers. And finally it sets the size of the now-unused buffers to
zero. This way no packet drops are introduced.
So after invoking mlxsw_sp_hdroom_bufs_reset_sizes(), tweak the
configuration to keep the old sizes of PG buffers for those buffers whose
size was set to zero.
Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-16 09:35:20 +03:00
|
|
|
err = mlxsw_sp_hdroom_configure(mlxsw_sp_port, &hdroom);
|
2020-06-29 23:46:13 +03:00
|
|
|
if (err) {
|
|
|
|
|
netdev_err(dev, "Failed to configure port's headroom\n");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = mlxsw_sp_port_pause_set(mlxsw_sp_port, pause);
|
|
|
|
|
if (err) {
|
|
|
|
|
netdev_err(dev, "Failed to set PAUSE parameters\n");
|
|
|
|
|
goto err_port_pause_configure;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mlxsw_sp_port->link.rx_pause = pause->rx_pause;
|
|
|
|
|
mlxsw_sp_port->link.tx_pause = pause->tx_pause;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
err_port_pause_configure:
|
mlxsw: spectrum: Split headroom autoresize out of buffer configuration
Split mlxsw_sp_port_headroom_set() to three functions.
mlxsw_sp_hdroom_bufs_reset_sizes() changes the sizes of the individual PG
buffers, and mlxsw_sp_hdroom_configure_buffers() will actually apply the
configuration. A third function, mlxsw_sp_hdroom_bufs_fit(), verifies that
the requested buffer configuration matches total headroom size
requirements.
Add wrappers, mlxsw_sp_hdroom_configure() and __..., that will eventually
perform full headroom configuration, but for now, only have them verify the
configured headroom size, and invoke mlxsw_sp_hdroom_configure_buffers().
Have them take the `force` argument to prepare for a later patch, even
though it is currently unused.
Note that the loop in mlxsw_sp_hdroom_configure_buffers() only goes through
DCBX_MAX_BUFFERS. Since there is no logic to configure the control buffer,
it needs to keep the values queried from the FW. Eventually this function
should configure all the PGs.
Note that conversion of __mlxsw_sp_dcbnl_ieee_setets() is not trivial. That
function performs the headroom configuration in three steps: first it
resizes the buffers and adds any new ones. Then it redirects priorities to
the new buffers. And finally it sets the size of the now-unused buffers to
zero. This way no packet drops are introduced.
So after invoking mlxsw_sp_hdroom_bufs_reset_sizes(), tweak the
configuration to keep the old sizes of PG buffers for those buffers whose
size was set to zero.
Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-16 09:35:20 +03:00
|
|
|
mlxsw_sp_hdroom_configure(mlxsw_sp_port, &orig_hdroom);
|
2020-06-29 23:46:13 +03:00
|
|
|
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)
|
|
|
|
|
|
2020-09-27 10:50:15 +03:00
|
|
|
struct mlxsw_sp_port_stats {
|
|
|
|
|
char str[ETH_GSTRING_LEN];
|
|
|
|
|
u64 (*getter)(struct mlxsw_sp_port *mlxsw_sp_port);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static u64
|
|
|
|
|
mlxsw_sp_port_get_transceiver_overheat_stats(struct mlxsw_sp_port *mlxsw_sp_port)
|
|
|
|
|
{
|
|
|
|
|
struct mlxsw_sp_port_mapping port_mapping = mlxsw_sp_port->mapping;
|
|
|
|
|
struct mlxsw_core *mlxsw_core = mlxsw_sp_port->mlxsw_sp->core;
|
|
|
|
|
u64 stats;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = mlxsw_env_module_overheat_counter_get(mlxsw_core,
|
|
|
|
|
port_mapping.module,
|
|
|
|
|
&stats);
|
|
|
|
|
if (err)
|
|
|
|
|
return mlxsw_sp_port->module_overheat_initial_val;
|
|
|
|
|
|
|
|
|
|
return stats - mlxsw_sp_port->module_overheat_initial_val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct mlxsw_sp_port_stats mlxsw_sp_port_transceiver_stats[] = {
|
|
|
|
|
{
|
|
|
|
|
.str = "transceiver_overheat",
|
|
|
|
|
.getter = mlxsw_sp_port_get_transceiver_overheat_stats,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP_PORT_HW_TRANSCEIVER_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_transceiver_stats)
|
|
|
|
|
|
2020-06-29 23:46:13 +03:00
|
|
|
#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 * \
|
2020-09-27 10:50:15 +03:00
|
|
|
TC_MAX_QUEUE) + \
|
|
|
|
|
MLXSW_SP_PORT_HW_TRANSCEIVER_STATS_LEN)
|
2020-06-29 23:46:13 +03:00
|
|
|
|
|
|
|
|
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);
|
2020-09-27 10:50:15 +03:00
|
|
|
|
|
|
|
|
for (i = 0; i < MLXSW_SP_PORT_HW_TRANSCEIVER_STATS_LEN; i++) {
|
|
|
|
|
memcpy(p, mlxsw_sp_port_transceiver_stats[i].str,
|
|
|
|
|
ETH_GSTRING_LEN);
|
|
|
|
|
p += ETH_GSTRING_LEN;
|
|
|
|
|
}
|
2020-06-29 23:46:13 +03:00
|
|
|
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]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-27 10:50:15 +03:00
|
|
|
static void __mlxsw_sp_port_get_env_stats(struct net_device *dev, u64 *data, int data_index,
|
|
|
|
|
struct mlxsw_sp_port_stats *port_stats,
|
|
|
|
|
int len)
|
|
|
|
|
{
|
|
|
|
|
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
|
data[data_index + i] = port_stats[i].getter(mlxsw_sp_port);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-29 23:46:13 +03:00
|
|
|
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();
|
2020-09-27 10:50:15 +03:00
|
|
|
|
|
|
|
|
/* Transceiver counters */
|
|
|
|
|
__mlxsw_sp_port_get_env_stats(dev, data, data_index, mlxsw_sp_port_transceiver_stats,
|
|
|
|
|
MLXSW_SP_PORT_HW_TRANSCEIVER_STATS_LEN);
|
|
|
|
|
data_index += MLXSW_SP_PORT_HW_TRANSCEIVER_STATS_LEN;
|
2020-06-29 23:46:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
2021-02-02 20:06:09 +02:00
|
|
|
struct ethtool_link_ksettings *cmd)
|
2020-06-29 23:46:13 +03:00
|
|
|
{
|
|
|
|
|
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);
|
2021-02-02 20:06:09 +02:00
|
|
|
ops->from_ptys_link(mlxsw_sp, eth_proto_cap,
|
2020-06-29 23:46:13 +03:00
|
|
|
cmd->link_modes.supported);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
mlxsw_sp_port_get_link_advertise(struct mlxsw_sp *mlxsw_sp,
|
2021-02-02 20:06:09 +02:00
|
|
|
u32 eth_proto_admin, bool autoneg,
|
2020-06-29 23:46:13 +03:00
|
|
|
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);
|
2021-02-02 20:06:09 +02:00
|
|
|
ops->from_ptys_link(mlxsw_sp, eth_proto_admin,
|
2020-06-29 23:46:13 +03:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-13 18:46:05 +03:00
|
|
|
static int mlxsw_sp_port_ptys_query(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
|
u32 *p_eth_proto_cap, u32 *p_eth_proto_admin,
|
|
|
|
|
u32 *p_eth_proto_oper, u8 *p_connector_type)
|
|
|
|
|
{
|
|
|
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
|
|
const struct mlxsw_sp_port_type_speed_ops *ops;
|
|
|
|
|
char ptys_pl[MLXSW_REG_PTYS_LEN];
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
ops = mlxsw_sp->port_type_speed_ops;
|
|
|
|
|
|
|
|
|
|
ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port, 0, false);
|
|
|
|
|
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, p_eth_proto_cap, p_eth_proto_admin,
|
|
|
|
|
p_eth_proto_oper);
|
|
|
|
|
if (p_connector_type)
|
|
|
|
|
*p_connector_type = mlxsw_reg_ptys_connector_type_get(ptys_pl);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-29 23:46:13 +03:00
|
|
|
static int mlxsw_sp_port_get_link_ksettings(struct net_device *dev,
|
|
|
|
|
struct ethtool_link_ksettings *cmd)
|
|
|
|
|
{
|
|
|
|
|
u32 eth_proto_cap, eth_proto_admin, eth_proto_oper;
|
|
|
|
|
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
|
|
|
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
|
|
const struct mlxsw_sp_port_type_speed_ops *ops;
|
|
|
|
|
u8 connector_type;
|
|
|
|
|
bool autoneg;
|
|
|
|
|
int err;
|
|
|
|
|
|
2020-09-13 18:46:05 +03:00
|
|
|
err = mlxsw_sp_port_ptys_query(mlxsw_sp_port, ð_proto_cap, ð_proto_admin,
|
|
|
|
|
ð_proto_oper, &connector_type);
|
2020-06-29 23:46:13 +03:00
|
|
|
if (err)
|
|
|
|
|
return err;
|
2020-09-13 18:46:05 +03:00
|
|
|
|
|
|
|
|
ops = mlxsw_sp->port_type_speed_ops;
|
|
|
|
|
autoneg = mlxsw_sp_port->link.autoneg;
|
2020-06-29 23:46:13 +03:00
|
|
|
|
2021-02-02 20:06:09 +02:00
|
|
|
mlxsw_sp_port_get_link_supported(mlxsw_sp, eth_proto_cap, cmd);
|
2020-06-29 23:46:13 +03:00
|
|
|
|
2021-02-02 20:06:09 +02:00
|
|
|
mlxsw_sp_port_get_link_advertise(mlxsw_sp, eth_proto_admin, autoneg, cmd);
|
2020-06-29 23:46:13 +03:00
|
|
|
|
|
|
|
|
cmd->base.autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
|
|
|
|
|
cmd->base.port = mlxsw_sp_port_connector_port(connector_type);
|
2021-02-02 20:06:11 +02:00
|
|
|
ops->from_ptys_link_mode(mlxsw_sp, netif_carrier_ok(dev),
|
|
|
|
|
eth_proto_oper, cmd);
|
2020-06-29 23:46:13 +03:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
mlxsw_sp_port_set_link_ksettings(struct net_device *dev,
|
|
|
|
|
const struct ethtool_link_ksettings *cmd)
|
|
|
|
|
{
|
|
|
|
|
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
|
|
|
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
|
|
const struct mlxsw_sp_port_type_speed_ops *ops;
|
|
|
|
|
char ptys_pl[MLXSW_REG_PTYS_LEN];
|
|
|
|
|
u32 eth_proto_cap, eth_proto_new;
|
|
|
|
|
bool autoneg;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
ops = mlxsw_sp->port_type_speed_ops;
|
|
|
|
|
|
|
|
|
|
ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port,
|
|
|
|
|
0, false);
|
|
|
|
|
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, ð_proto_cap, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
autoneg = cmd->base.autoneg == AUTONEG_ENABLE;
|
|
|
|
|
eth_proto_new = autoneg ?
|
2021-02-02 20:06:09 +02:00
|
|
|
ops->to_ptys_advert_link(mlxsw_sp, cmd) :
|
2021-02-02 20:06:10 +02:00
|
|
|
ops->to_ptys_speed_lanes(mlxsw_sp, mlxsw_sp_port->mapping.width,
|
|
|
|
|
cmd);
|
2020-06-29 23:46:13 +03:00
|
|
|
|
|
|
|
|
eth_proto_new = eth_proto_new & eth_proto_cap;
|
|
|
|
|
if (!eth_proto_new) {
|
2021-02-02 20:06:10 +02:00
|
|
|
netdev_err(dev, "No supported speed or lanes requested\n");
|
2020-06-29 23:46:13 +03:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-16 12:27:43 -07:00
|
|
|
static void
|
|
|
|
|
mlxsw_sp_get_eth_phy_stats(struct net_device *dev,
|
|
|
|
|
struct ethtool_eth_phy_stats *phy_stats)
|
|
|
|
|
{
|
|
|
|
|
char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
|
|
|
|
|
|
|
|
|
|
if (mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT,
|
|
|
|
|
0, ppcnt_pl))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
phy_stats->SymbolErrorDuringCarrier =
|
|
|
|
|
mlxsw_reg_ppcnt_a_symbol_error_during_carrier_get(ppcnt_pl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
mlxsw_sp_get_eth_mac_stats(struct net_device *dev,
|
|
|
|
|
struct ethtool_eth_mac_stats *mac_stats)
|
|
|
|
|
{
|
|
|
|
|
char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
|
|
|
|
|
|
|
|
|
|
if (mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT,
|
|
|
|
|
0, ppcnt_pl))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
mac_stats->FramesTransmittedOK =
|
|
|
|
|
mlxsw_reg_ppcnt_a_frames_transmitted_ok_get(ppcnt_pl);
|
|
|
|
|
mac_stats->FramesReceivedOK =
|
|
|
|
|
mlxsw_reg_ppcnt_a_frames_received_ok_get(ppcnt_pl);
|
|
|
|
|
mac_stats->FrameCheckSequenceErrors =
|
|
|
|
|
mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get(ppcnt_pl);
|
|
|
|
|
mac_stats->AlignmentErrors =
|
|
|
|
|
mlxsw_reg_ppcnt_a_alignment_errors_get(ppcnt_pl);
|
|
|
|
|
mac_stats->OctetsTransmittedOK =
|
|
|
|
|
mlxsw_reg_ppcnt_a_octets_transmitted_ok_get(ppcnt_pl);
|
|
|
|
|
mac_stats->OctetsReceivedOK =
|
|
|
|
|
mlxsw_reg_ppcnt_a_octets_received_ok_get(ppcnt_pl);
|
|
|
|
|
mac_stats->MulticastFramesXmittedOK =
|
|
|
|
|
mlxsw_reg_ppcnt_a_multicast_frames_xmitted_ok_get(ppcnt_pl);
|
|
|
|
|
mac_stats->BroadcastFramesXmittedOK =
|
|
|
|
|
mlxsw_reg_ppcnt_a_broadcast_frames_xmitted_ok_get(ppcnt_pl);
|
|
|
|
|
mac_stats->MulticastFramesReceivedOK =
|
|
|
|
|
mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get(ppcnt_pl);
|
|
|
|
|
mac_stats->BroadcastFramesReceivedOK =
|
|
|
|
|
mlxsw_reg_ppcnt_a_broadcast_frames_received_ok_get(ppcnt_pl);
|
|
|
|
|
mac_stats->InRangeLengthErrors =
|
|
|
|
|
mlxsw_reg_ppcnt_a_in_range_length_errors_get(ppcnt_pl);
|
|
|
|
|
mac_stats->OutOfRangeLengthField =
|
|
|
|
|
mlxsw_reg_ppcnt_a_out_of_range_length_field_get(ppcnt_pl);
|
|
|
|
|
mac_stats->FrameTooLongErrors =
|
|
|
|
|
mlxsw_reg_ppcnt_a_frame_too_long_errors_get(ppcnt_pl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
mlxsw_sp_get_eth_ctrl_stats(struct net_device *dev,
|
|
|
|
|
struct ethtool_eth_ctrl_stats *ctrl_stats)
|
|
|
|
|
{
|
|
|
|
|
char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
|
|
|
|
|
|
|
|
|
|
if (mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_IEEE_8023_CNT,
|
|
|
|
|
0, ppcnt_pl))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ctrl_stats->MACControlFramesTransmitted =
|
|
|
|
|
mlxsw_reg_ppcnt_a_mac_control_frames_transmitted_get(ppcnt_pl);
|
|
|
|
|
ctrl_stats->MACControlFramesReceived =
|
|
|
|
|
mlxsw_reg_ppcnt_a_mac_control_frames_received_get(ppcnt_pl);
|
|
|
|
|
ctrl_stats->UnsupportedOpcodesReceived =
|
|
|
|
|
mlxsw_reg_ppcnt_a_unsupported_opcodes_received_get(ppcnt_pl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct ethtool_rmon_hist_range mlxsw_rmon_ranges[] = {
|
|
|
|
|
{ 0, 64 },
|
|
|
|
|
{ 65, 127 },
|
|
|
|
|
{ 128, 255 },
|
|
|
|
|
{ 256, 511 },
|
|
|
|
|
{ 512, 1023 },
|
|
|
|
|
{ 1024, 1518 },
|
|
|
|
|
{ 1519, 2047 },
|
|
|
|
|
{ 2048, 4095 },
|
|
|
|
|
{ 4096, 8191 },
|
|
|
|
|
{ 8192, 10239 },
|
|
|
|
|
{}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
mlxsw_sp_get_rmon_stats(struct net_device *dev,
|
|
|
|
|
struct ethtool_rmon_stats *rmon,
|
|
|
|
|
const struct ethtool_rmon_hist_range **ranges)
|
|
|
|
|
{
|
|
|
|
|
char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
|
|
|
|
|
|
|
|
|
|
if (mlxsw_sp_port_get_stats_raw(dev, MLXSW_REG_PPCNT_RFC_2819_CNT,
|
|
|
|
|
0, ppcnt_pl))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
rmon->undersize_pkts =
|
|
|
|
|
mlxsw_reg_ppcnt_ether_stats_undersize_pkts_get(ppcnt_pl);
|
|
|
|
|
rmon->oversize_pkts =
|
|
|
|
|
mlxsw_reg_ppcnt_ether_stats_oversize_pkts_get(ppcnt_pl);
|
|
|
|
|
rmon->fragments =
|
|
|
|
|
mlxsw_reg_ppcnt_ether_stats_fragments_get(ppcnt_pl);
|
|
|
|
|
|
|
|
|
|
rmon->hist[0] = mlxsw_reg_ppcnt_ether_stats_pkts64octets_get(ppcnt_pl);
|
|
|
|
|
rmon->hist[1] =
|
|
|
|
|
mlxsw_reg_ppcnt_ether_stats_pkts65to127octets_get(ppcnt_pl);
|
|
|
|
|
rmon->hist[2] =
|
|
|
|
|
mlxsw_reg_ppcnt_ether_stats_pkts128to255octets_get(ppcnt_pl);
|
|
|
|
|
rmon->hist[3] =
|
|
|
|
|
mlxsw_reg_ppcnt_ether_stats_pkts256to511octets_get(ppcnt_pl);
|
|
|
|
|
rmon->hist[4] =
|
|
|
|
|
mlxsw_reg_ppcnt_ether_stats_pkts512to1023octets_get(ppcnt_pl);
|
|
|
|
|
rmon->hist[5] =
|
|
|
|
|
mlxsw_reg_ppcnt_ether_stats_pkts1024to1518octets_get(ppcnt_pl);
|
|
|
|
|
rmon->hist[6] =
|
|
|
|
|
mlxsw_reg_ppcnt_ether_stats_pkts1519to2047octets_get(ppcnt_pl);
|
|
|
|
|
rmon->hist[7] =
|
|
|
|
|
mlxsw_reg_ppcnt_ether_stats_pkts2048to4095octets_get(ppcnt_pl);
|
|
|
|
|
rmon->hist[8] =
|
|
|
|
|
mlxsw_reg_ppcnt_ether_stats_pkts4096to8191octets_get(ppcnt_pl);
|
|
|
|
|
rmon->hist[9] =
|
|
|
|
|
mlxsw_reg_ppcnt_ether_stats_pkts8192to10239octets_get(ppcnt_pl);
|
|
|
|
|
|
|
|
|
|
*ranges = mlxsw_rmon_ranges;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-29 23:46:13 +03:00
|
|
|
const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
|
2021-02-02 20:06:10 +02:00
|
|
|
.cap_link_lanes_supported = true,
|
|
|
|
|
.get_drvinfo = mlxsw_sp_port_get_drvinfo,
|
|
|
|
|
.get_link = ethtool_op_get_link,
|
|
|
|
|
.get_link_ext_state = mlxsw_sp_port_get_link_ext_state,
|
|
|
|
|
.get_pauseparam = mlxsw_sp_port_get_pauseparam,
|
|
|
|
|
.set_pauseparam = mlxsw_sp_port_set_pauseparam,
|
|
|
|
|
.get_strings = mlxsw_sp_port_get_strings,
|
|
|
|
|
.set_phys_id = mlxsw_sp_port_set_phys_id,
|
|
|
|
|
.get_ethtool_stats = mlxsw_sp_port_get_stats,
|
|
|
|
|
.get_sset_count = mlxsw_sp_port_get_sset_count,
|
|
|
|
|
.get_link_ksettings = mlxsw_sp_port_get_link_ksettings,
|
|
|
|
|
.set_link_ksettings = mlxsw_sp_port_set_link_ksettings,
|
|
|
|
|
.get_module_info = mlxsw_sp_get_module_info,
|
|
|
|
|
.get_module_eeprom = mlxsw_sp_get_module_eeprom,
|
|
|
|
|
.get_ts_info = mlxsw_sp_get_ts_info,
|
2021-04-16 12:27:43 -07:00
|
|
|
.get_eth_phy_stats = mlxsw_sp_get_eth_phy_stats,
|
|
|
|
|
.get_eth_mac_stats = mlxsw_sp_get_eth_mac_stats,
|
|
|
|
|
.get_eth_ctrl_stats = mlxsw_sp_get_eth_ctrl_stats,
|
|
|
|
|
.get_rmon_stats = mlxsw_sp_get_rmon_stats,
|
2020-06-29 23:46:13 +03:00
|
|
|
};
|
2020-06-29 23:46:14 +03:00
|
|
|
|
|
|
|
|
struct mlxsw_sp1_port_link_mode {
|
|
|
|
|
enum ethtool_link_mode_bit_indices mask_ethtool;
|
|
|
|
|
u32 mask;
|
|
|
|
|
u32 speed;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct mlxsw_sp1_port_link_mode mlxsw_sp1_port_link_mode[] = {
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_SGMII |
|
|
|
|
|
MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
|
|
|
|
|
.speed = SPEED_1000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CX4 |
|
|
|
|
|
MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
|
|
|
|
|
.speed = SPEED_10000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR |
|
|
|
|
|
MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR |
|
|
|
|
|
MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR |
|
|
|
|
|
MLXSW_REG_PTYS_ETH_SPEED_10GBASE_ER_LR,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
|
|
|
|
|
.speed = SPEED_10000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
|
|
|
|
|
.speed = SPEED_40000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
|
|
|
|
|
.speed = SPEED_40000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
|
|
|
|
|
.speed = SPEED_40000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_LR4_ER4,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
|
|
|
|
|
.speed = SPEED_40000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_CR,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
|
|
|
|
|
.speed = SPEED_25000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_KR,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
|
|
|
|
|
.speed = SPEED_25000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_SR,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
|
|
|
|
|
.speed = SPEED_25000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_CR2,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
|
|
|
|
|
.speed = SPEED_50000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR2,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
|
|
|
|
|
.speed = SPEED_50000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_SR2,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
|
|
|
|
|
.speed = SPEED_50000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
|
|
|
|
|
.speed = SPEED_100000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
|
|
|
|
|
.speed = SPEED_100000,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
|
|
|
|
|
.speed = SPEED_100000,
|
|
|
|
|
},
|
2021-02-25 18:57:20 +02:00
|
|
|
{
|
|
|
|
|
.mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_LR4_ER4,
|
|
|
|
|
.mask_ethtool = ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
|
|
|
|
|
.speed = SPEED_100000,
|
|
|
|
|
},
|
2020-06-29 23:46:14 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#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,
|
2021-02-02 20:06:09 +02:00
|
|
|
unsigned long *mode)
|
2020-06-29 23:46:14 +03:00
|
|
|
{
|
|
|
|
|
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
|
2021-02-02 20:06:11 +02:00
|
|
|
mlxsw_sp1_from_ptys_link_mode(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
|
|
|
|
|
u32 ptys_eth_proto,
|
|
|
|
|
struct ethtool_link_ksettings *cmd)
|
2020-06-29 23:46:14 +03:00
|
|
|
{
|
2021-04-07 13:06:51 +03:00
|
|
|
struct mlxsw_sp1_port_link_mode link;
|
2021-02-02 20:06:11 +02:00
|
|
|
int i;
|
|
|
|
|
|
2021-04-07 13:06:51 +03:00
|
|
|
cmd->base.speed = SPEED_UNKNOWN;
|
|
|
|
|
cmd->base.duplex = DUPLEX_UNKNOWN;
|
|
|
|
|
cmd->lanes = 0;
|
2020-06-29 23:46:14 +03:00
|
|
|
|
|
|
|
|
if (!carrier_ok)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-02-02 20:06:11 +02:00
|
|
|
for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
|
2021-04-07 13:06:51 +03:00
|
|
|
if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask) {
|
|
|
|
|
link = mlxsw_sp1_port_link_mode[i];
|
|
|
|
|
ethtool_params_from_link_mode(cmd,
|
|
|
|
|
link.mask_ethtool);
|
|
|
|
|
}
|
2021-02-02 20:06:11 +02:00
|
|
|
}
|
2020-06-29 23:46:14 +03:00
|
|
|
}
|
|
|
|
|
|
mlxsw: spectrum_ethtool: Introduce ptys_max_speed callback
The SBIB register configures the size of an internal buffer that the
Spectrum ASICs use when mirroring traffic on egress. This size should be
taken into account when validating that the port headroom buffers are not
larger than the chip can handle. Up until now this was not done, which is
incidentally not a problem, because the priority group buffers that mlxsw
auto-configures are small enough that the boundary condition could not be
violated.
When dcbnl_setbuffer is implemented, the user gets control over sizes of PG
buffers, and they might overshoot the headroom capacity. However the size
of the SBIB buffer depends on port speed, which cannot be vetoed. There is
obviously no way to retroactively push back on requests for overlarge PG
buffers, or reject an overlarge MTU, or cancel losslessness of a certain
PG.
Therefore, instead of taking into account the current speed when
calculating SBIB buffer size, take into account the maximum speed that a
port with given Ethernet protocol capabilities can have.
To that end, add a new ethtool callback, ptys_max_speed, which determines
this maximum speed.
Signed-off-by: Petr Machata <petrm@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-13 18:46:06 +03:00
|
|
|
static int mlxsw_sp1_ptys_max_speed(struct mlxsw_sp_port *mlxsw_sp_port, u32 *p_max_speed)
|
|
|
|
|
{
|
|
|
|
|
u32 eth_proto_cap;
|
|
|
|
|
u32 max_speed = 0;
|
|
|
|
|
int err;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
err = mlxsw_sp_port_ptys_query(mlxsw_sp_port, ð_proto_cap, NULL, NULL, NULL);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
|
|
|
|
|
if ((eth_proto_cap & mlxsw_sp1_port_link_mode[i].mask) &&
|
|
|
|
|
mlxsw_sp1_port_link_mode[i].speed > max_speed)
|
|
|
|
|
max_speed = mlxsw_sp1_port_link_mode[i].speed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*p_max_speed = max_speed;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-29 23:46:14 +03:00
|
|
|
static u32
|
2021-02-02 20:06:09 +02:00
|
|
|
mlxsw_sp1_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp,
|
2020-06-29 23:46:14 +03:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-02 20:06:10 +02:00
|
|
|
static u32 mlxsw_sp1_to_ptys_speed_lanes(struct mlxsw_sp *mlxsw_sp, u8 width,
|
|
|
|
|
const struct ethtool_link_ksettings *cmd)
|
2020-06-29 23:46:14 +03:00
|
|
|
{
|
|
|
|
|
u32 ptys_proto = 0;
|
|
|
|
|
int i;
|
|
|
|
|
|
2021-02-02 20:06:10 +02:00
|
|
|
if (cmd->lanes > width)
|
|
|
|
|
return ptys_proto;
|
|
|
|
|
|
2020-06-29 23:46:14 +03:00
|
|
|
for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
|
2021-02-02 20:06:10 +02:00
|
|
|
if (cmd->base.speed == mlxsw_sp1_port_link_mode[i].speed)
|
2020-06-29 23:46:14 +03:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-24 16:37:31 +03:00
|
|
|
static u32 mlxsw_sp1_ptys_proto_cap_masked_get(u32 eth_proto_cap)
|
|
|
|
|
{
|
|
|
|
|
u32 ptys_proto_cap_masked = 0;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
|
|
|
|
|
if (mlxsw_sp1_port_link_mode[i].mask & eth_proto_cap)
|
|
|
|
|
ptys_proto_cap_masked |=
|
|
|
|
|
mlxsw_sp1_port_link_mode[i].mask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ptys_proto_cap_masked;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-29 23:46:14 +03:00
|
|
|
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,
|
2021-02-02 20:06:11 +02:00
|
|
|
.from_ptys_link_mode = mlxsw_sp1_from_ptys_link_mode,
|
mlxsw: spectrum_ethtool: Introduce ptys_max_speed callback
The SBIB register configures the size of an internal buffer that the
Spectrum ASICs use when mirroring traffic on egress. This size should be
taken into account when validating that the port headroom buffers are not
larger than the chip can handle. Up until now this was not done, which is
incidentally not a problem, because the priority group buffers that mlxsw
auto-configures are small enough that the boundary condition could not be
violated.
When dcbnl_setbuffer is implemented, the user gets control over sizes of PG
buffers, and they might overshoot the headroom capacity. However the size
of the SBIB buffer depends on port speed, which cannot be vetoed. There is
obviously no way to retroactively push back on requests for overlarge PG
buffers, or reject an overlarge MTU, or cancel losslessness of a certain
PG.
Therefore, instead of taking into account the current speed when
calculating SBIB buffer size, take into account the maximum speed that a
port with given Ethernet protocol capabilities can have.
To that end, add a new ethtool callback, ptys_max_speed, which determines
this maximum speed.
Signed-off-by: Petr Machata <petrm@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-13 18:46:06 +03:00
|
|
|
.ptys_max_speed = mlxsw_sp1_ptys_max_speed,
|
2020-06-29 23:46:14 +03:00
|
|
|
.to_ptys_advert_link = mlxsw_sp1_to_ptys_advert_link,
|
2021-02-02 20:06:10 +02:00
|
|
|
.to_ptys_speed_lanes = mlxsw_sp1_to_ptys_speed_lanes,
|
2020-06-29 23:46:14 +03:00
|
|
|
.reg_ptys_eth_pack = mlxsw_sp1_reg_ptys_eth_pack,
|
|
|
|
|
.reg_ptys_eth_unpack = mlxsw_sp1_reg_ptys_eth_unpack,
|
2020-10-24 16:37:31 +03:00
|
|
|
.ptys_proto_cap_masked_get = mlxsw_sp1_ptys_proto_cap_masked_get,
|
2020-06-29 23:46:14 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const enum ethtool_link_mode_bit_indices
|
|
|
|
|
mlxsw_sp2_mask_ethtool_sgmii_100m[] = {
|
|
|
|
|
ETHTOOL_LINK_MODE_100baseT_Full_BIT,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP2_MASK_ETHTOOL_SGMII_100M_LEN \
|
|
|
|
|
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_sgmii_100m)
|
|
|
|
|
|
|
|
|
|
static const enum ethtool_link_mode_bit_indices
|
|
|
|
|
mlxsw_sp2_mask_ethtool_1000base_x_sgmii[] = {
|
|
|
|
|
ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP2_MASK_ETHTOOL_1000BASE_X_SGMII_LEN \
|
|
|
|
|
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_1000base_x_sgmii)
|
|
|
|
|
|
|
|
|
|
static const enum ethtool_link_mode_bit_indices
|
|
|
|
|
mlxsw_sp2_mask_ethtool_5gbase_r[] = {
|
|
|
|
|
ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP2_MASK_ETHTOOL_5GBASE_R_LEN \
|
|
|
|
|
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_5gbase_r)
|
|
|
|
|
|
|
|
|
|
static const enum ethtool_link_mode_bit_indices
|
|
|
|
|
mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g[] = {
|
|
|
|
|
ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP2_MASK_ETHTOOL_XFI_XAUI_1_10G_LEN \
|
|
|
|
|
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_xfi_xaui_1_10g)
|
|
|
|
|
|
|
|
|
|
static const enum ethtool_link_mode_bit_indices
|
|
|
|
|
mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g[] = {
|
|
|
|
|
ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP2_MASK_ETHTOOL_XLAUI_4_XLPPI_4_40G_LEN \
|
|
|
|
|
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_xlaui_4_xlppi_4_40g)
|
|
|
|
|
|
|
|
|
|
static const enum ethtool_link_mode_bit_indices
|
|
|
|
|
mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr[] = {
|
|
|
|
|
ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP2_MASK_ETHTOOL_25GAUI_1_25GBASE_CR_KR_LEN \
|
|
|
|
|
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_25gaui_1_25gbase_cr_kr)
|
|
|
|
|
|
|
|
|
|
static const enum ethtool_link_mode_bit_indices
|
|
|
|
|
mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2[] = {
|
|
|
|
|
ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP2_MASK_ETHTOOL_50GAUI_2_LAUI_2_50GBASE_CR2_KR2_LEN \
|
|
|
|
|
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_50gaui_2_laui_2_50gbase_cr2_kr2)
|
|
|
|
|
|
|
|
|
|
static const enum ethtool_link_mode_bit_indices
|
|
|
|
|
mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr[] = {
|
|
|
|
|
ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_50000baseDR_Full_BIT,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP2_MASK_ETHTOOL_50GAUI_1_LAUI_1_50GBASE_CR_KR_LEN \
|
|
|
|
|
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_50gaui_1_laui_1_50gbase_cr_kr)
|
|
|
|
|
|
|
|
|
|
static const enum ethtool_link_mode_bit_indices
|
|
|
|
|
mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4[] = {
|
|
|
|
|
ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP2_MASK_ETHTOOL_CAUI_4_100GBASE_CR4_KR4_LEN \
|
|
|
|
|
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_caui_4_100gbase_cr4_kr4)
|
|
|
|
|
|
|
|
|
|
static const enum ethtool_link_mode_bit_indices
|
|
|
|
|
mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2[] = {
|
|
|
|
|
ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP2_MASK_ETHTOOL_100GAUI_2_100GBASE_CR2_KR2_LEN \
|
|
|
|
|
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_100gaui_2_100gbase_cr2_kr2)
|
|
|
|
|
|
|
|
|
|
static const enum ethtool_link_mode_bit_indices
|
|
|
|
|
mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4[] = {
|
|
|
|
|
ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP2_MASK_ETHTOOL_200GAUI_4_200GBASE_CR4_KR4_LEN \
|
|
|
|
|
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_200gaui_4_200gbase_cr4_kr4)
|
|
|
|
|
|
|
|
|
|
static const enum ethtool_link_mode_bit_indices
|
|
|
|
|
mlxsw_sp2_mask_ethtool_400gaui_8[] = {
|
|
|
|
|
ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT,
|
|
|
|
|
ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP2_MASK_ETHTOOL_400GAUI_8_LEN \
|
|
|
|
|
ARRAY_SIZE(mlxsw_sp2_mask_ethtool_400gaui_8)
|
|
|
|
|
|
|
|
|
|
#define MLXSW_SP_PORT_MASK_WIDTH_1X BIT(0)
|
|
|
|
|
#define MLXSW_SP_PORT_MASK_WIDTH_2X BIT(1)
|
|
|
|
|
#define MLXSW_SP_PORT_MASK_WIDTH_4X BIT(2)
|
|
|
|
|
#define MLXSW_SP_PORT_MASK_WIDTH_8X BIT(3)
|
|
|
|
|
|
|
|
|
|
static u8 mlxsw_sp_port_mask_width_get(u8 width)
|
|
|
|
|
{
|
|
|
|
|
switch (width) {
|
|
|
|
|
case 1:
|
|
|
|
|
return MLXSW_SP_PORT_MASK_WIDTH_1X;
|
|
|
|
|
case 2:
|
|
|
|
|
return MLXSW_SP_PORT_MASK_WIDTH_2X;
|
|
|
|
|
case 4:
|
|
|
|
|
return MLXSW_SP_PORT_MASK_WIDTH_4X;
|
|
|
|
|
case 8:
|
|
|
|
|
return MLXSW_SP_PORT_MASK_WIDTH_8X;
|
|
|
|
|
default:
|
|
|
|
|
WARN_ON_ONCE(1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct mlxsw_sp2_port_link_mode {
|
|
|
|
|
const enum ethtool_link_mode_bit_indices *mask_ethtool;
|
|
|
|
|
int m_ethtool_len;
|
|
|
|
|
u32 mask;
|
|
|
|
|
u32 speed;
|
2021-02-02 20:06:10 +02:00
|
|
|
u32 width;
|
|
|
|
|
u8 mask_sup_width;
|
2020-06-29 23:46:14 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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,
|
2021-02-02 20:06:10 +02:00
|
|
|
.mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_1X |
|
2020-06-29 23:46:14 +03:00
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_2X |
|
|
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_4X |
|
|
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_8X,
|
|
|
|
|
.speed = SPEED_100,
|
2021-02-02 20:06:10 +02:00
|
|
|
.width = 1,
|
2020-06-29 23:46:14 +03:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.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,
|
2021-02-02 20:06:10 +02:00
|
|
|
.mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_1X |
|
2020-06-29 23:46:14 +03:00
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_2X |
|
|
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_4X |
|
|
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_8X,
|
|
|
|
|
.speed = SPEED_1000,
|
2021-02-02 20:06:10 +02:00
|
|
|
.width = 1,
|
2020-06-29 23:46:14 +03:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.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,
|
2021-02-02 20:06:10 +02:00
|
|
|
.mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_1X |
|
2020-06-29 23:46:14 +03:00
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_2X |
|
|
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_4X |
|
|
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_8X,
|
|
|
|
|
.speed = SPEED_5000,
|
2021-02-02 20:06:10 +02:00
|
|
|
.width = 1,
|
2020-06-29 23:46:14 +03:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.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,
|
2021-02-02 20:06:10 +02:00
|
|
|
.mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_1X |
|
2020-06-29 23:46:14 +03:00
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_2X |
|
|
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_4X |
|
|
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_8X,
|
|
|
|
|
.speed = SPEED_10000,
|
2021-02-02 20:06:10 +02:00
|
|
|
.width = 1,
|
2020-06-29 23:46:14 +03:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.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,
|
2021-02-02 20:06:10 +02:00
|
|
|
.mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_4X |
|
2020-06-29 23:46:14 +03:00
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_8X,
|
|
|
|
|
.speed = SPEED_40000,
|
2021-02-02 20:06:10 +02:00
|
|
|
.width = 4,
|
2020-06-29 23:46:14 +03:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.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,
|
2021-02-02 20:06:10 +02:00
|
|
|
.mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_1X |
|
2020-06-29 23:46:14 +03:00
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_2X |
|
|
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_4X |
|
|
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_8X,
|
|
|
|
|
.speed = SPEED_25000,
|
2021-02-02 20:06:10 +02:00
|
|
|
.width = 1,
|
2020-06-29 23:46:14 +03:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.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,
|
2021-02-02 20:06:10 +02:00
|
|
|
.mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_2X |
|
2020-06-29 23:46:14 +03:00
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_4X |
|
|
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_8X,
|
|
|
|
|
.speed = SPEED_50000,
|
2021-02-02 20:06:10 +02:00
|
|
|
.width = 2,
|
2020-06-29 23:46:14 +03:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.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,
|
2021-02-02 20:06:10 +02:00
|
|
|
.mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_1X,
|
2020-06-29 23:46:14 +03:00
|
|
|
.speed = SPEED_50000,
|
2021-02-02 20:06:10 +02:00
|
|
|
.width = 1,
|
2020-06-29 23:46:14 +03:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.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,
|
2021-02-02 20:06:10 +02:00
|
|
|
.mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_4X |
|
2020-06-29 23:46:14 +03:00
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_8X,
|
|
|
|
|
.speed = SPEED_100000,
|
2021-02-02 20:06:10 +02:00
|
|
|
.width = 4,
|
2020-06-29 23:46:14 +03:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.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,
|
2021-02-02 20:06:10 +02:00
|
|
|
.mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_2X,
|
2020-06-29 23:46:14 +03:00
|
|
|
.speed = SPEED_100000,
|
2021-02-02 20:06:10 +02:00
|
|
|
.width = 2,
|
2020-06-29 23:46:14 +03:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.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,
|
2021-02-02 20:06:10 +02:00
|
|
|
.mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_4X |
|
2020-06-29 23:46:14 +03:00
|
|
|
MLXSW_SP_PORT_MASK_WIDTH_8X,
|
|
|
|
|
.speed = SPEED_200000,
|
2021-02-02 20:06:10 +02:00
|
|
|
.width = 4,
|
2020-06-29 23:46:14 +03:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.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,
|
2021-02-02 20:06:10 +02:00
|
|
|
.mask_sup_width = MLXSW_SP_PORT_MASK_WIDTH_8X,
|
2020-06-29 23:46:14 +03:00
|
|
|
.speed = SPEED_400000,
|
2021-02-02 20:06:10 +02:00
|
|
|
.width = 8,
|
2020-06-29 23:46:14 +03:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#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,
|
2021-02-02 20:06:09 +02:00
|
|
|
unsigned long *mode)
|
2020-06-29 23:46:14 +03:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
|
2021-02-02 20:06:09 +02:00
|
|
|
if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask)
|
2020-06-29 23:46:14 +03:00
|
|
|
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
|
2021-02-02 20:06:11 +02:00
|
|
|
mlxsw_sp2_from_ptys_link_mode(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
|
|
|
|
|
u32 ptys_eth_proto,
|
|
|
|
|
struct ethtool_link_ksettings *cmd)
|
2020-06-29 23:46:14 +03:00
|
|
|
{
|
2021-02-02 20:06:11 +02:00
|
|
|
struct mlxsw_sp2_port_link_mode link;
|
|
|
|
|
int i;
|
|
|
|
|
|
2021-04-07 13:06:51 +03:00
|
|
|
cmd->base.speed = SPEED_UNKNOWN;
|
|
|
|
|
cmd->base.duplex = DUPLEX_UNKNOWN;
|
|
|
|
|
cmd->lanes = 0;
|
2020-06-29 23:46:14 +03:00
|
|
|
|
|
|
|
|
if (!carrier_ok)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-02-02 20:06:11 +02:00
|
|
|
for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
|
|
|
|
|
if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) {
|
|
|
|
|
link = mlxsw_sp2_port_link_mode[i];
|
2021-04-07 13:06:51 +03:00
|
|
|
ethtool_params_from_link_mode(cmd,
|
|
|
|
|
link.mask_ethtool[1]);
|
2021-02-02 20:06:11 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-06-29 23:46:14 +03:00
|
|
|
}
|
|
|
|
|
|
mlxsw: spectrum_ethtool: Introduce ptys_max_speed callback
The SBIB register configures the size of an internal buffer that the
Spectrum ASICs use when mirroring traffic on egress. This size should be
taken into account when validating that the port headroom buffers are not
larger than the chip can handle. Up until now this was not done, which is
incidentally not a problem, because the priority group buffers that mlxsw
auto-configures are small enough that the boundary condition could not be
violated.
When dcbnl_setbuffer is implemented, the user gets control over sizes of PG
buffers, and they might overshoot the headroom capacity. However the size
of the SBIB buffer depends on port speed, which cannot be vetoed. There is
obviously no way to retroactively push back on requests for overlarge PG
buffers, or reject an overlarge MTU, or cancel losslessness of a certain
PG.
Therefore, instead of taking into account the current speed when
calculating SBIB buffer size, take into account the maximum speed that a
port with given Ethernet protocol capabilities can have.
To that end, add a new ethtool callback, ptys_max_speed, which determines
this maximum speed.
Signed-off-by: Petr Machata <petrm@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-13 18:46:06 +03:00
|
|
|
static int mlxsw_sp2_ptys_max_speed(struct mlxsw_sp_port *mlxsw_sp_port, u32 *p_max_speed)
|
|
|
|
|
{
|
|
|
|
|
u32 eth_proto_cap;
|
|
|
|
|
u32 max_speed = 0;
|
|
|
|
|
int err;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
err = mlxsw_sp_port_ptys_query(mlxsw_sp_port, ð_proto_cap, NULL, NULL, NULL);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
|
|
|
|
|
if ((eth_proto_cap & mlxsw_sp2_port_link_mode[i].mask) &&
|
|
|
|
|
mlxsw_sp2_port_link_mode[i].speed > max_speed)
|
|
|
|
|
max_speed = mlxsw_sp2_port_link_mode[i].speed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*p_max_speed = max_speed;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-29 23:46:14 +03:00
|
|
|
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
|
2021-02-02 20:06:09 +02:00
|
|
|
mlxsw_sp2_to_ptys_advert_link(struct mlxsw_sp *mlxsw_sp,
|
2020-06-29 23:46:14 +03:00
|
|
|
const struct ethtool_link_ksettings *cmd)
|
|
|
|
|
{
|
|
|
|
|
u32 ptys_proto = 0;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
|
2021-02-02 20:06:09 +02:00
|
|
|
if (mlxsw_sp2_test_bit_ethtool(&mlxsw_sp2_port_link_mode[i],
|
2020-06-29 23:46:14 +03:00
|
|
|
cmd->link_modes.advertising))
|
|
|
|
|
ptys_proto |= mlxsw_sp2_port_link_mode[i].mask;
|
|
|
|
|
}
|
|
|
|
|
return ptys_proto;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-02 20:06:10 +02:00
|
|
|
static u32 mlxsw_sp2_to_ptys_speed_lanes(struct mlxsw_sp *mlxsw_sp, u8 width,
|
|
|
|
|
const struct ethtool_link_ksettings *cmd)
|
2020-06-29 23:46:14 +03:00
|
|
|
{
|
|
|
|
|
u8 mask_width = mlxsw_sp_port_mask_width_get(width);
|
2021-02-02 20:06:10 +02:00
|
|
|
struct mlxsw_sp2_port_link_mode link_mode;
|
2020-06-29 23:46:14 +03:00
|
|
|
u32 ptys_proto = 0;
|
|
|
|
|
int i;
|
|
|
|
|
|
2021-02-02 20:06:10 +02:00
|
|
|
if (cmd->lanes > width)
|
|
|
|
|
return ptys_proto;
|
|
|
|
|
|
2020-06-29 23:46:14 +03:00
|
|
|
for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
|
2021-02-02 20:06:10 +02:00
|
|
|
if (cmd->base.speed == mlxsw_sp2_port_link_mode[i].speed) {
|
|
|
|
|
link_mode = mlxsw_sp2_port_link_mode[i];
|
|
|
|
|
|
|
|
|
|
if (!cmd->lanes) {
|
|
|
|
|
/* If number of lanes was not set by user space,
|
|
|
|
|
* choose the link mode that supports the width
|
|
|
|
|
* of the port.
|
|
|
|
|
*/
|
|
|
|
|
if (mask_width & link_mode.mask_sup_width)
|
|
|
|
|
ptys_proto |= link_mode.mask;
|
|
|
|
|
} else if (cmd->lanes == link_mode.width) {
|
|
|
|
|
/* Else if the number of lanes was set, choose
|
|
|
|
|
* the link mode that its actual width equals to
|
|
|
|
|
* it.
|
|
|
|
|
*/
|
|
|
|
|
ptys_proto |= link_mode.mask;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-29 23:46:14 +03:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-24 16:37:31 +03:00
|
|
|
static u32 mlxsw_sp2_ptys_proto_cap_masked_get(u32 eth_proto_cap)
|
|
|
|
|
{
|
|
|
|
|
u32 ptys_proto_cap_masked = 0;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
|
|
|
|
|
if (mlxsw_sp2_port_link_mode[i].mask & eth_proto_cap)
|
|
|
|
|
ptys_proto_cap_masked |=
|
|
|
|
|
mlxsw_sp2_port_link_mode[i].mask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ptys_proto_cap_masked;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-29 23:46:14 +03:00
|
|
|
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,
|
2021-02-02 20:06:11 +02:00
|
|
|
.from_ptys_link_mode = mlxsw_sp2_from_ptys_link_mode,
|
mlxsw: spectrum_ethtool: Introduce ptys_max_speed callback
The SBIB register configures the size of an internal buffer that the
Spectrum ASICs use when mirroring traffic on egress. This size should be
taken into account when validating that the port headroom buffers are not
larger than the chip can handle. Up until now this was not done, which is
incidentally not a problem, because the priority group buffers that mlxsw
auto-configures are small enough that the boundary condition could not be
violated.
When dcbnl_setbuffer is implemented, the user gets control over sizes of PG
buffers, and they might overshoot the headroom capacity. However the size
of the SBIB buffer depends on port speed, which cannot be vetoed. There is
obviously no way to retroactively push back on requests for overlarge PG
buffers, or reject an overlarge MTU, or cancel losslessness of a certain
PG.
Therefore, instead of taking into account the current speed when
calculating SBIB buffer size, take into account the maximum speed that a
port with given Ethernet protocol capabilities can have.
To that end, add a new ethtool callback, ptys_max_speed, which determines
this maximum speed.
Signed-off-by: Petr Machata <petrm@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-09-13 18:46:06 +03:00
|
|
|
.ptys_max_speed = mlxsw_sp2_ptys_max_speed,
|
2020-06-29 23:46:14 +03:00
|
|
|
.to_ptys_advert_link = mlxsw_sp2_to_ptys_advert_link,
|
2021-02-02 20:06:10 +02:00
|
|
|
.to_ptys_speed_lanes = mlxsw_sp2_to_ptys_speed_lanes,
|
2020-06-29 23:46:14 +03:00
|
|
|
.reg_ptys_eth_pack = mlxsw_sp2_reg_ptys_eth_pack,
|
|
|
|
|
.reg_ptys_eth_unpack = mlxsw_sp2_reg_ptys_eth_unpack,
|
2020-10-24 16:37:31 +03:00
|
|
|
.ptys_proto_cap_masked_get = mlxsw_sp2_ptys_proto_cap_masked_get,
|
2020-06-29 23:46:14 +03:00
|
|
|
};
|