From 86f5fc1c10e6787afa5c9ddb2a3c10afd3dd7471 Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Mon, 7 Sep 2020 17:15:08 +0100 Subject: [PATCH] sfc: handle limited FEC support If the reported PHY capabilities do not include a given FEC mode, don't attempt to select that FEC mode anyway. If the user tries to set a mode through ethtool that is not supported, return an error. The _REQUESTED bits don't appear in the supported caps, but are implied by the corresponding FEC bits. Signed-off-by: Edward Cree Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/sfc/mcdi_port_common.c | 48 +++++++++++++++------ drivers/net/ethernet/sfc/mcdi_port_common.h | 2 +- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/sfc/mcdi_port_common.c b/drivers/net/ethernet/sfc/mcdi_port_common.c index 71c76a6a6b0e3..c0e1c88a652c4 100644 --- a/drivers/net/ethernet/sfc/mcdi_port_common.c +++ b/drivers/net/ethernet/sfc/mcdi_port_common.c @@ -308,7 +308,7 @@ void efx_mcdi_phy_decode_link(struct efx_nic *efx, * Both RS and BASER (whether AUTO or not) means use FEC if cable and link * partner support it, preferring RS to BASER. */ -u32 ethtool_fec_caps_to_mcdi(u32 ethtool_cap) +u32 ethtool_fec_caps_to_mcdi(u32 supported_cap, u32 ethtool_cap) { u32 ret = 0; @@ -316,17 +316,21 @@ u32 ethtool_fec_caps_to_mcdi(u32 ethtool_cap) return 0; if (ethtool_cap & ETHTOOL_FEC_AUTO) - ret |= (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) | - (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) | - (1 << MC_CMD_PHY_CAP_RS_FEC_LBN); - if (ethtool_cap & ETHTOOL_FEC_RS) + ret |= ((1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) | + (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) | + (1 << MC_CMD_PHY_CAP_RS_FEC_LBN)) & supported_cap; + if (ethtool_cap & ETHTOOL_FEC_RS && + supported_cap & (1 << MC_CMD_PHY_CAP_RS_FEC_LBN)) ret |= (1 << MC_CMD_PHY_CAP_RS_FEC_LBN) | (1 << MC_CMD_PHY_CAP_RS_FEC_REQUESTED_LBN); - if (ethtool_cap & ETHTOOL_FEC_BASER) - ret |= (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) | - (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) | - (1 << MC_CMD_PHY_CAP_BASER_FEC_REQUESTED_LBN) | - (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_REQUESTED_LBN); + if (ethtool_cap & ETHTOOL_FEC_BASER) { + if (supported_cap & (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN)) + ret |= (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) | + (1 << MC_CMD_PHY_CAP_BASER_FEC_REQUESTED_LBN); + if (supported_cap & (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN)) + ret |= (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) | + (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_REQUESTED_LBN); + } return ret; } @@ -577,7 +581,7 @@ int efx_mcdi_phy_set_link_ksettings(struct efx_nic *efx, const struct ethtool_li } } - caps |= ethtool_fec_caps_to_mcdi(efx->fec_config); + caps |= ethtool_fec_caps_to_mcdi(phy_cfg->supported_cap, efx->fec_config); rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx), efx->loopback_mode, 0); @@ -645,12 +649,30 @@ int efx_mcdi_phy_get_fecparam(struct efx_nic *efx, struct ethtool_fecparam *fec) return 0; } +/* Basic validation to ensure that the caps we are going to attempt to set are + * in fact supported by the adapter. Note that 'no FEC' is always supported. + */ +static int ethtool_fec_supported(u32 supported_cap, u32 ethtool_cap) +{ + if (ethtool_cap & ETHTOOL_FEC_OFF) + return 0; + + if (ethtool_cap && + !ethtool_fec_caps_to_mcdi(supported_cap, ethtool_cap)) + return -EINVAL; + return 0; +} + int efx_mcdi_phy_set_fecparam(struct efx_nic *efx, const struct ethtool_fecparam *fec) { struct efx_mcdi_phy_data *phy_cfg = efx->phy_data; u32 caps; int rc; + rc = ethtool_fec_supported(phy_cfg->supported_cap, fec->fec); + if (rc) + return rc; + /* Work out what efx_mcdi_phy_set_link_ksettings() would produce from * saved advertising bits */ @@ -660,7 +682,7 @@ int efx_mcdi_phy_set_fecparam(struct efx_nic *efx, const struct ethtool_fecparam else caps = phy_cfg->forced_cap; - caps |= ethtool_fec_caps_to_mcdi(fec->fec); + caps |= ethtool_fec_caps_to_mcdi(phy_cfg->supported_cap, fec->fec); rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx), efx->loopback_mode, 0); if (rc) @@ -699,7 +721,7 @@ int efx_mcdi_port_reconfigure(struct efx_nic *efx) ethtool_linkset_to_mcdi_cap(efx->link_advertising) : phy_cfg->forced_cap); - caps |= ethtool_fec_caps_to_mcdi(efx->fec_config); + caps |= ethtool_fec_caps_to_mcdi(phy_cfg->supported_cap, efx->fec_config); return efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx), efx->loopback_mode, 0); diff --git a/drivers/net/ethernet/sfc/mcdi_port_common.h b/drivers/net/ethernet/sfc/mcdi_port_common.h index 9d214079f699d..f37e18adbc377 100644 --- a/drivers/net/ethernet/sfc/mcdi_port_common.h +++ b/drivers/net/ethernet/sfc/mcdi_port_common.h @@ -41,7 +41,7 @@ u8 mcdi_to_ethtool_media(u32 media); void efx_mcdi_phy_decode_link(struct efx_nic *efx, struct efx_link_state *link_state, u32 speed, u32 flags, u32 fcntl); -u32 ethtool_fec_caps_to_mcdi(u32 ethtool_cap); +u32 ethtool_fec_caps_to_mcdi(u32 supported_cap, u32 ethtool_cap); u32 mcdi_fec_caps_to_ethtool(u32 caps, bool is_25g); void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa); bool efx_mcdi_phy_poll(struct efx_nic *efx); -- 2.39.5