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 <ecree@solarflare.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
4404c0898d
commit
400d64cf2d
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user