Commit 400d64cf authored by Edward Cree's avatar Edward Cree Committed by Jakub Kicinski

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: default avatarEdward Cree <ecree@solarflare.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 4404c089
...@@ -308,7 +308,7 @@ void efx_mcdi_phy_decode_link(struct efx_nic *efx, ...@@ -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 * Both RS and BASER (whether AUTO or not) means use FEC if cable and link
* partner support it, preferring RS to BASER. * 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; u32 ret = 0;
...@@ -316,17 +316,21 @@ u32 ethtool_fec_caps_to_mcdi(u32 ethtool_cap) ...@@ -316,17 +316,21 @@ u32 ethtool_fec_caps_to_mcdi(u32 ethtool_cap)
return 0; return 0;
if (ethtool_cap & ETHTOOL_FEC_AUTO) if (ethtool_cap & ETHTOOL_FEC_AUTO)
ret |= (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) | ret |= ((1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) |
(1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) | (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) |
(1 << MC_CMD_PHY_CAP_RS_FEC_LBN); (1 << MC_CMD_PHY_CAP_RS_FEC_LBN)) & supported_cap;
if (ethtool_cap & ETHTOOL_FEC_RS) 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) | ret |= (1 << MC_CMD_PHY_CAP_RS_FEC_LBN) |
(1 << MC_CMD_PHY_CAP_RS_FEC_REQUESTED_LBN); (1 << MC_CMD_PHY_CAP_RS_FEC_REQUESTED_LBN);
if (ethtool_cap & ETHTOOL_FEC_BASER) if (ethtool_cap & ETHTOOL_FEC_BASER) {
ret |= (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) | if (supported_cap & (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN))
(1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) | ret |= (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) |
(1 << MC_CMD_PHY_CAP_BASER_FEC_REQUESTED_LBN) | (1 << MC_CMD_PHY_CAP_BASER_FEC_REQUESTED_LBN);
(1 << MC_CMD_PHY_CAP_25G_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; return ret;
} }
...@@ -577,7 +581,7 @@ int efx_mcdi_phy_set_link_ksettings(struct efx_nic *efx, const struct ethtool_li ...@@ -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), rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
efx->loopback_mode, 0); efx->loopback_mode, 0);
...@@ -645,12 +649,30 @@ int efx_mcdi_phy_get_fecparam(struct efx_nic *efx, struct ethtool_fecparam *fec) ...@@ -645,12 +649,30 @@ int efx_mcdi_phy_get_fecparam(struct efx_nic *efx, struct ethtool_fecparam *fec)
return 0; 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) 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; struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
u32 caps; u32 caps;
int rc; 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 /* Work out what efx_mcdi_phy_set_link_ksettings() would produce from
* saved advertising bits * saved advertising bits
*/ */
...@@ -660,7 +682,7 @@ int efx_mcdi_phy_set_fecparam(struct efx_nic *efx, const struct ethtool_fecparam ...@@ -660,7 +682,7 @@ int efx_mcdi_phy_set_fecparam(struct efx_nic *efx, const struct ethtool_fecparam
else else
caps = phy_cfg->forced_cap; 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), rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
efx->loopback_mode, 0); efx->loopback_mode, 0);
if (rc) if (rc)
...@@ -699,7 +721,7 @@ int efx_mcdi_port_reconfigure(struct efx_nic *efx) ...@@ -699,7 +721,7 @@ int efx_mcdi_port_reconfigure(struct efx_nic *efx)
ethtool_linkset_to_mcdi_cap(efx->link_advertising) : ethtool_linkset_to_mcdi_cap(efx->link_advertising) :
phy_cfg->forced_cap); 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), return efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
efx->loopback_mode, 0); efx->loopback_mode, 0);
......
...@@ -41,7 +41,7 @@ u8 mcdi_to_ethtool_media(u32 media); ...@@ -41,7 +41,7 @@ u8 mcdi_to_ethtool_media(u32 media);
void efx_mcdi_phy_decode_link(struct efx_nic *efx, void efx_mcdi_phy_decode_link(struct efx_nic *efx,
struct efx_link_state *link_state, struct efx_link_state *link_state,
u32 speed, u32 flags, u32 fcntl); 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); u32 mcdi_fec_caps_to_ethtool(u32 caps, bool is_25g);
void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa); void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa);
bool efx_mcdi_phy_poll(struct efx_nic *efx); bool efx_mcdi_phy_poll(struct efx_nic *efx);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment