From 57ccaedb74158be6d7d6edb255a6c153ec19e618 Mon Sep 17 00:00:00 2001 From: Ganesh Goudar Date: Thu, 24 May 2018 17:49:30 +0530 Subject: cxgb4/cxgb4vf: link management changes for new SFP newer SFPs like SFP28 and QSFP28 Transceiver Modules present several new possibilities which we haven't faced before. Fix the assumptions in the code reflecting the more limited capabilities of previous Transceiver Module systems Original work by Casey Leedom Signed-off-by: Ganesh Goudar Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c | 22 +++++----- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 34 ++++++++++++++-- drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c | 47 +++++++++++++++++++++- 3 files changed, 85 insertions(+), 18 deletions(-) (limited to 'drivers/net/ethernet/chelsio') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c index 59d04d73c672..f7eef93ffc87 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c @@ -800,24 +800,20 @@ static int set_link_ksettings(struct net_device *dev, if (base->duplex != DUPLEX_FULL) return -EINVAL; - if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) { - /* PHY offers a single speed. See if that's what's - * being requested. - */ - if (base->autoneg == AUTONEG_DISABLE && - (lc->pcaps & speed_to_fw_caps(base->speed))) - return 0; - return -EINVAL; - } - old_lc = *lc; - if (base->autoneg == AUTONEG_DISABLE) { + if (!(lc->pcaps & FW_PORT_CAP32_ANEG) || + base->autoneg == AUTONEG_DISABLE) { fw_caps = speed_to_fw_caps(base->speed); - if (!(lc->pcaps & fw_caps)) + /* Must only specify a single speed which must be supported + * as part of the Physical Port Capabilities. + */ + if ((fw_caps & (fw_caps - 1)) != 0 || + !(lc->pcaps & fw_caps)) return -EINVAL; + lc->speed_caps = fw_caps; - lc->acaps = 0; + lc->acaps = fw_caps; } else { fw_caps = lmm_to_fw_caps(link_ksettings->link_modes.advertising); diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 704f6960a6ea..962384cdd64b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -4066,6 +4066,7 @@ int t4_link_l1cfg_core(struct adapter *adapter, unsigned int mbox, fw_port_cap32_t fw_fc, cc_fec, fw_fec, rcap; struct fw_port_cmd cmd; unsigned int fw_mdi; + int ret; fw_mdi = (FW_PORT_CAP32_MDI_V(FW_PORT_CAP32_MDI_AUTO) & lc->pcaps); /* Convert driver coding of Pause Frame Flow Control settings into the @@ -4100,6 +4101,13 @@ int t4_link_l1cfg_core(struct adapter *adapter, unsigned int mbox, rcap = lc->acaps | fw_fc | fw_fec | fw_mdi; } + if (rcap & ~lc->pcaps) { + dev_err(adapter->pdev_dev, + "Requested Port Capabilities %#x exceed Physical Port Capabilities %#x\n", + rcap, lc->pcaps); + return -EINVAL; + } + /* And send that on to the Firmware ... */ memset(&cmd, 0, sizeof(cmd)); @@ -4110,13 +4118,21 @@ int t4_link_l1cfg_core(struct adapter *adapter, unsigned int mbox, cpu_to_be32(FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16 ? FW_PORT_ACTION_L1_CFG : FW_PORT_ACTION_L1_CFG32) | - FW_LEN16(cmd)); + FW_LEN16(cmd)); if (fw_caps == FW_CAPS16) cmd.u.l1cfg.rcap = cpu_to_be32(fwcaps32_to_caps16(rcap)); else cmd.u.l1cfg32.rcap32 = cpu_to_be32(rcap); - return t4_wr_mbox_meat_timeout(adapter, mbox, &cmd, sizeof(cmd), NULL, - sleep_ok, timeout); + + ret = t4_wr_mbox_meat_timeout(adapter, mbox, &cmd, sizeof(cmd), NULL, + sleep_ok, timeout); + if (ret) { + dev_err(adapter->pdev_dev, + "Requested Port Capabilities %#x rejected, error %d\n", + rcap, -ret); + return ret; + } + return ret; } /** @@ -8395,7 +8411,9 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) lc->lpacaps = lpacaps; lc->acaps = acaps & ADVERT_MASK; - if (lc->acaps & FW_PORT_CAP32_ANEG) { + if (!(lc->acaps & FW_PORT_CAP32_ANEG)) { + lc->autoneg = AUTONEG_DISABLE; + } else if (lc->acaps & FW_PORT_CAP32_ANEG) { lc->autoneg = AUTONEG_ENABLE; } else { /* When Autoneg is disabled, user needs to set @@ -8600,6 +8618,13 @@ static void init_link_config(struct link_config *lc, fw_port_cap32_t pcaps, lc->requested_fec = FEC_AUTO; lc->fec = fwcap_to_cc_fec(lc->def_acaps); + /* If the Port is capable of Auto-Negtotiation, initialize it as + * "enabled" and copy over all of the Physical Port Capabilities + * to the Advertised Port Capabilities. Otherwise mark it as + * Auto-Negotiate disabled and select the highest supported speed + * for the link. Note parallel structure in t4_link_l1cfg_core() + * and t4_handle_get_port_info(). + */ if (lc->pcaps & FW_PORT_CAP32_ANEG) { lc->acaps = lc->pcaps & ADVERT_MASK; lc->autoneg = AUTONEG_ENABLE; @@ -8607,6 +8632,7 @@ static void init_link_config(struct link_config *lc, fw_port_cap32_t pcaps, } else { lc->acaps = 0; lc->autoneg = AUTONEG_DISABLE; + lc->speed_caps = fwcap_to_fwspeed(acaps); } } diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c index 3017f7873ff9..8786431a8825 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c @@ -405,6 +405,36 @@ static unsigned int fwcap_to_speed(fw_port_cap32_t caps) return 0; } +/** + * fwcap_to_fwspeed - return highest speed in Port Capabilities + * @acaps: advertised Port Capabilities + * + * Get the highest speed for the port from the advertised Port + * Capabilities. It will be either the highest speed from the list of + * speeds or whatever user has set using ethtool. + */ +static fw_port_cap32_t fwcap_to_fwspeed(fw_port_cap32_t acaps) +{ + #define TEST_SPEED_RETURN(__caps_speed) \ + do { \ + if (acaps & FW_PORT_CAP32_SPEED_##__caps_speed) \ + return FW_PORT_CAP32_SPEED_##__caps_speed; \ + } while (0) + + TEST_SPEED_RETURN(400G); + TEST_SPEED_RETURN(200G); + TEST_SPEED_RETURN(100G); + TEST_SPEED_RETURN(50G); + TEST_SPEED_RETURN(40G); + TEST_SPEED_RETURN(25G); + TEST_SPEED_RETURN(10G); + TEST_SPEED_RETURN(1G); + TEST_SPEED_RETURN(100M); + + #undef TEST_SPEED_RETURN + return 0; +} + /* * init_link_config - initialize a link's SW state * @lc: structure holding the link state @@ -431,6 +461,13 @@ static void init_link_config(struct link_config *lc, lc->requested_fec = FEC_AUTO; lc->fec = lc->auto_fec; + /* If the Port is capable of Auto-Negtotiation, initialize it as + * "enabled" and copy over all of the Physical Port Capabilities + * to the Advertised Port Capabilities. Otherwise mark it as + * Auto-Negotiate disabled and select the highest supported speed + * for the link. Note parallel structure in t4_link_l1cfg_core() + * and t4_handle_get_port_info(). + */ if (lc->pcaps & FW_PORT_CAP32_ANEG) { lc->acaps = acaps & ADVERT_MASK; lc->autoneg = AUTONEG_ENABLE; @@ -438,6 +475,7 @@ static void init_link_config(struct link_config *lc, } else { lc->acaps = 0; lc->autoneg = AUTONEG_DISABLE; + lc->speed_caps = fwcap_to_fwspeed(acaps); } } @@ -1955,7 +1993,14 @@ static void t4vf_handle_get_port_info(struct port_info *pi, lc->lpacaps = lpacaps; lc->acaps = acaps & ADVERT_MASK; - if (lc->acaps & FW_PORT_CAP32_ANEG) { + /* If we're not physically capable of Auto-Negotiation, note + * this as Auto-Negotiation disabled. Otherwise, we track + * what Auto-Negotiation settings we have. Note parallel + * structure in init_link_config(). + */ + if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) { + lc->autoneg = AUTONEG_DISABLE; + } else if (lc->acaps & FW_PORT_CAP32_ANEG) { lc->autoneg = AUTONEG_ENABLE; } else { /* When Autoneg is disabled, user needs to set -- cgit v1.2.3