* 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;
 
                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;
 }
 
                }
        }
 
-       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);
        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
         */
        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)
                    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);