diff mbox series

[2/3] net/cxgbe: rework and simplify link handling code

Message ID 46dd53d2c19b099a21a8d3306fcc7834a64eca47.1608504560.git.rahul.lakkireddy@chelsio.com (mailing list archive)
State Accepted, archived
Delegated to: Ferruh Yigit
Headers show
Series net/cxgbe: rework link config and add FEC support | expand

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Rahul Lakkireddy Dec. 20, 2020, 10:47 p.m. UTC
From: Karra Satwik <kaara.satwik@chelsio.com>

Rework and simplify link handling code. Remove redundant
variables in link configuration structure and directly
extract information from the 32-bit link capabilities.

Signed-off-by: Karra Satwik <kaara.satwik@chelsio.com>
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
---
 drivers/net/cxgbe/base/adapter.h        |  10 -
 drivers/net/cxgbe/base/common.h         |  60 ++--
 drivers/net/cxgbe/base/t4_hw.c          | 350 +++++++++++++-----------
 drivers/net/cxgbe/base/t4fw_interface.h |  11 +-
 drivers/net/cxgbe/base/t4vf_hw.c        |   9 +-
 drivers/net/cxgbe/cxgbe_ethdev.c        |  68 +++--
 drivers/net/cxgbe/cxgbe_main.c          |  21 +-
 7 files changed, 275 insertions(+), 254 deletions(-)
diff mbox series

Patch

diff --git a/drivers/net/cxgbe/base/adapter.h b/drivers/net/cxgbe/base/adapter.h
index 6ff009a5f..a5a3313c2 100644
--- a/drivers/net/cxgbe/base/adapter.h
+++ b/drivers/net/cxgbe/base/adapter.h
@@ -39,9 +39,6 @@  struct port_info {
 	short int xact_addr_filt;       /* index of exact MAC address filter */
 
 	u16    viid;                    /* associated virtual interface id */
-	s8     mdio_addr;               /* address of the PHY */
-	u8     port_type;               /* firmware port type */
-	u8     mod_type;                /* firmware module type */
 	u8     port_id;                 /* physical port ID */
 	u8     pidx;			/* port index for this PF */
 	u8     tx_chan;                 /* associated channel */
@@ -63,12 +60,6 @@  struct port_info {
 	u8 vivld;
 };
 
-/* Enable or disable autonegotiation.  If this is set to enable,
- * the forced link modes above are completely ignored.
- */
-#define AUTONEG_DISABLE         0x00
-#define AUTONEG_ENABLE          0x01
-
 enum {                                 /* adapter flags */
 	FULL_INIT_DONE     = (1 << 0),
 	USING_MSI          = (1 << 1),
@@ -801,7 +792,6 @@  void t4_free_mem(void *addr);
 #define t4_os_free(_ptr)       t4_free_mem((_ptr))
 
 void t4_os_portmod_changed(const struct adapter *adap, int port_id);
-void t4_os_link_changed(struct adapter *adap, int port_id, int link_stat);
 
 void reclaim_completed_tx(struct sge_txq *q);
 void t4_free_sge_resources(struct adapter *adap);
diff --git a/drivers/net/cxgbe/base/common.h b/drivers/net/cxgbe/base/common.h
index 86f3dc80b..a244df7a0 100644
--- a/drivers/net/cxgbe/base/common.h
+++ b/drivers/net/cxgbe/base/common.h
@@ -38,18 +38,6 @@  enum dev_master { MASTER_CANT, MASTER_MAY, MASTER_MUST };
 
 enum dev_state { DEV_STATE_UNINIT, DEV_STATE_INIT, DEV_STATE_ERR };
 
-enum cc_pause {
-	PAUSE_RX      = 1 << 0,
-	PAUSE_TX      = 1 << 1,
-	PAUSE_AUTONEG = 1 << 2
-};
-
-enum cc_fec {
-	FEC_AUTO     = 1 << 0,    /* IEEE 802.3 "automatic" */
-	FEC_RS       = 1 << 1,    /* Reed-Solomon */
-	FEC_BASER_RS = 1 << 2,    /* BaseR/Reed-Solomon */
-};
-
 enum { MEM_EDC0, MEM_EDC1, MEM_MC, MEM_MC0 = MEM_MC, MEM_MC1 };
 
 struct port_stats {
@@ -281,28 +269,19 @@  struct adapter_params {
 
 /* Firmware Port Capabilities types.
  */
-typedef u32 fw_port_cap32_t;    /* 32-bit Port Capabilities integral value */
-
 struct link_config {
-	fw_port_cap32_t pcaps;          /* link capabilities */
-	fw_port_cap32_t acaps;          /* advertised capabilities */
-
-	u32 requested_speed;            /* speed (Mb/s) user has requested */
-	u32 speed;                      /* actual link speed (Mb/s) */
-
-	enum cc_pause requested_fc;     /* flow control user has requested */
-	enum cc_pause fc;               /* actual link flow control */
+	u32 pcaps;         /* Physically supported link caps */
+	u32 acaps;         /* Advertised link caps */
 
-	enum cc_fec auto_fec;           /* Forward Error Correction
-					 * "automatic" (IEEE 802.3)
-					 */
-	enum cc_fec requested_fec;      /* Forward Error Correction requested */
-	enum cc_fec fec;                /* Forward Error Correction actual */
+	u32 link_caps;     /* Current link caps */
+	u32 admin_caps;    /* Admin configured link caps  */
 
-	unsigned char autoneg;          /* autonegotiating? */
+	u8 mdio_addr;      /* Address of the PHY */
+	u8 port_type;      /* Firmware port type */
+	u8 mod_type;       /* Firmware module type */
 
-	unsigned char link_ok;          /* link up? */
-	unsigned char link_down_rc;     /* link down reason */
+	u8 link_ok;        /* Link up? */
+	u8 link_down_rc;   /* Link down reason */
 };
 
 #include "adapter.h"
@@ -338,8 +317,21 @@  void t4_tp_wr_bits_indirect(struct adapter *adap, unsigned int addr,
 			    unsigned int mask, unsigned int val);
 void t4_intr_enable(struct adapter *adapter);
 void t4_intr_disable(struct adapter *adapter);
-int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port,
-		  struct link_config *lc);
+int t4_link_l1cfg_core(struct port_info *pi, u32 caps, u8 sleep_ok);
+static inline int t4_link_l1cfg(struct port_info *pi, u32 caps)
+{
+	return t4_link_l1cfg_core(pi, caps, true);
+}
+
+static inline int t4_link_l1cfg_ns(struct port_info *pi, u32 caps)
+{
+	return t4_link_l1cfg_core(pi, caps, false);
+}
+
+int t4_set_link_speed(struct port_info *pi, u32 speed, u32 *new_caps);
+int t4_set_link_pause(struct port_info *pi, u8 autoneg, u8 pause_tx,
+		      u8 pause_rx, u32 *new_caps);
+unsigned int t4_fwcap_to_speed(u32 caps);
 void t4_load_mtus(struct adapter *adap, const unsigned short *mtus,
 		  const unsigned short *alpha, const unsigned short *beta);
 int t4_fw_hello(struct adapter *adap, unsigned int mbox, unsigned int evt_mbox,
@@ -496,8 +488,8 @@  void t4_get_port_stats_offset(struct adapter *adap, int idx,
 			      struct port_stats *stats,
 			      struct port_stats *offset);
 void t4_clr_port_stats(struct adapter *adap, int idx);
-void init_link_config(struct link_config *lc, fw_port_cap32_t pcaps,
-		      fw_port_cap32_t acaps);
+void t4_init_link_config(struct port_info *pi, u32 pcaps, u32 acaps,
+			 u8 mdio_addr, u8 port_type, u8 mod_type);
 void t4_reset_link_config(struct adapter *adap, int idx);
 int t4_get_version_info(struct adapter *adapter);
 void t4_dump_version_info(struct adapter *adapter);
diff --git a/drivers/net/cxgbe/base/t4_hw.c b/drivers/net/cxgbe/base/t4_hw.c
index 44227740c..3c0990cb3 100644
--- a/drivers/net/cxgbe/base/t4_hw.c
+++ b/drivers/net/cxgbe/base/t4_hw.c
@@ -2809,67 +2809,11 @@  void t4_dump_version_info(struct adapter *adapter)
 			 G_FW_HDR_FW_VER_BUILD(adapter->params.er_vers));
 }
 
-#define ADVERT_MASK (V_FW_PORT_CAP32_SPEED(M_FW_PORT_CAP32_SPEED) | \
-		     FW_PORT_CAP32_ANEG)
-
-/* Translate Firmware Pause specification to Common Code */
-static inline enum cc_pause fwcap_to_cc_pause(fw_port_cap32_t fw_pause)
-{
-	enum cc_pause cc_pause = 0;
-
-	if (fw_pause & FW_PORT_CAP32_FC_RX)
-		cc_pause |= PAUSE_RX;
-	if (fw_pause & FW_PORT_CAP32_FC_TX)
-		cc_pause |= PAUSE_TX;
-
-	return cc_pause;
-}
-
-/* Translate Common Code Pause Frame specification into Firmware */
-static inline fw_port_cap32_t cc_to_fwcap_pause(enum cc_pause cc_pause)
-{
-	fw_port_cap32_t fw_pause = 0;
-
-	if (cc_pause & PAUSE_RX)
-		fw_pause |= FW_PORT_CAP32_FC_RX;
-	if (cc_pause & PAUSE_TX)
-		fw_pause |= FW_PORT_CAP32_FC_TX;
-
-	return fw_pause;
-}
-
-/* Translate Firmware Forward Error Correction specification to Common Code */
-static inline enum cc_fec fwcap_to_cc_fec(fw_port_cap32_t fw_fec)
-{
-	enum cc_fec cc_fec = 0;
-
-	if (fw_fec & FW_PORT_CAP32_FEC_RS)
-		cc_fec |= FEC_RS;
-	if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS)
-		cc_fec |= FEC_BASER_RS;
-
-	return cc_fec;
-}
-
-/* Translate Common Code Forward Error Correction specification to Firmware */
-static inline fw_port_cap32_t cc_to_fwcap_fec(enum cc_fec cc_fec)
-{
-	fw_port_cap32_t fw_fec = 0;
-
-	if (cc_fec & FEC_RS)
-		fw_fec |= FW_PORT_CAP32_FEC_RS;
-	if (cc_fec & FEC_BASER_RS)
-		fw_fec |= FW_PORT_CAP32_FEC_BASER_RS;
-
-	return fw_fec;
-}
-
 /**
- * t4_link_l1cfg - apply link configuration to MAC/PHY
- * @adapter: the adapter
- * @mbox: the Firmware Mailbox to use
- * @port: the Port ID
- * @lc: the Port's Link Configuration
+ * t4_link_l1cfg_core - apply link configuration to MAC/PHY
+ * @pi: the port info
+ * @caps: link capabilities to configure
+ * @sleep_ok: if true we may sleep while awaiting command completion
  *
  * Set up a port's MAC and PHY according to a desired link configuration.
  * - If the PHY can auto-negotiate first decide what to advertise, then
@@ -2878,57 +2822,38 @@  static inline fw_port_cap32_t cc_to_fwcap_fec(enum cc_fec cc_fec)
  * - If auto-negotiation is off set the MAC to the proper speed/duplex/FC,
  *   otherwise do it later based on the outcome of auto-negotiation.
  */
-int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port,
-		  struct link_config *lc)
+int t4_link_l1cfg_core(struct port_info *pi, u32 caps, u8 sleep_ok)
 {
-	unsigned int fw_mdi = V_FW_PORT_CAP32_MDI(FW_PORT_CAP32_MDI_AUTO);
-	fw_port_cap32_t fw_fc, cc_fec, fw_fec, rcap;
+	struct link_config *lc = &pi->link_cfg;
+	struct adapter *adap = pi->adapter;
 	struct fw_port_cmd cmd;
+	int ret;
 
-	lc->link_ok = 0;
-
-	fw_fc = cc_to_fwcap_pause(lc->requested_fc);
-
-	/* Convert Common Code Forward Error Control settings into the
-	 * Firmware's API.  If the current Requested FEC has "Automatic"
-	 * (IEEE 802.3) specified, then we use whatever the Firmware
-	 * sent us as part of it's IEEE 802.3-based interpratation of
-	 * the Transceiver Module EPROM FEC parameters.  Otherwise we
-	 * use whatever is in the current Requested FEC settings.
-	 */
-	if (lc->requested_fec & FEC_AUTO)
-		cc_fec = lc->auto_fec;
-	else
-		cc_fec = lc->requested_fec;
-	fw_fec = cc_to_fwcap_fec(cc_fec);
-
-	/* Figure out what our Requested Port Capabilities are going to be.
-	 */
-	if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) {
-		rcap = (lc->pcaps & ADVERT_MASK) | fw_fc | fw_fec;
-		lc->fc = lc->requested_fc & ~PAUSE_AUTONEG;
-		lc->fec = cc_fec;
-	} else if (lc->autoneg == AUTONEG_DISABLE) {
-		rcap = lc->requested_speed | fw_fc | fw_fec | fw_mdi;
-		lc->fc = lc->requested_fc & ~PAUSE_AUTONEG;
-		lc->fec = cc_fec;
-	} else {
-		rcap = lc->acaps | fw_fc | fw_fec | fw_mdi;
-	}
-
-	/* And send that on to the Firmware ...
-	 */
 	memset(&cmd, 0, sizeof(cmd));
 	cmd.op_to_portid = cpu_to_be32(V_FW_CMD_OP(FW_PORT_CMD) |
 				       F_FW_CMD_REQUEST | F_FW_CMD_EXEC |
-				       V_FW_PORT_CMD_PORTID(port));
+				       V_FW_PORT_CMD_PORTID(pi->port_id));
 	cmd.action_to_len16 =
 		cpu_to_be32(V_FW_PORT_CMD_ACTION(FW_PORT_ACTION_L1_CFG32) |
 			    FW_LEN16(cmd));
 
-	cmd.u.l1cfg32.rcap32 = cpu_to_be32(rcap);
+	cmd.u.l1cfg32.rcap32 = cpu_to_be32(caps);
 
-	return t4_wr_mbox(adap, mbox, &cmd, sizeof(cmd), NULL);
+	if (sleep_ok)
+		ret = t4_wr_mbox(adap, adap->mbox, &cmd, sizeof(cmd), NULL);
+	else
+		ret = t4_wr_mbox_ns(adap, adap->mbox, &cmd, sizeof(cmd), NULL);
+
+	if (ret == FW_SUCCESS) {
+		lc->link_ok = 0;
+		lc->link_caps = caps;
+	} else {
+		dev_err(adap,
+			"Requested Port Capabilities %#x rejected, error %d\n",
+			caps, ret);
+	}
+
+	return ret;
 }
 
 /**
@@ -4426,8 +4351,32 @@  static const char *t4_link_down_rc_str(unsigned char link_down_rc)
 	return reason[link_down_rc];
 }
 
+static u32 t4_speed_to_fwcap(u32 speed)
+{
+	switch (speed) {
+	case 100000:
+		return FW_PORT_CAP32_SPEED_100G;
+	case 50000:
+		return FW_PORT_CAP32_SPEED_50G;
+	case 40000:
+		return FW_PORT_CAP32_SPEED_40G;
+	case 25000:
+		return FW_PORT_CAP32_SPEED_25G;
+	case 10000:
+		return FW_PORT_CAP32_SPEED_10G;
+	case 1000:
+		return FW_PORT_CAP32_SPEED_1G;
+	case 100:
+		return FW_PORT_CAP32_SPEED_100M;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 /* Return the highest speed set in the port capabilities, in Mb/s. */
-static unsigned int fwcap_to_speed(fw_port_cap32_t caps)
+unsigned int t4_fwcap_to_speed(u32 caps)
 {
 #define TEST_SPEED_RETURN(__caps_speed, __speed) \
 	do { \
@@ -4448,6 +4397,84 @@  static unsigned int fwcap_to_speed(fw_port_cap32_t caps)
 	return 0;
 }
 
+static void t4_set_link_autoneg_speed(struct port_info *pi, u32 *new_caps)
+{
+	struct link_config *lc = &pi->link_cfg;
+	u32 caps = *new_caps;
+
+	caps &= ~V_FW_PORT_CAP32_SPEED(M_FW_PORT_CAP32_SPEED);
+	caps |= G_FW_PORT_CAP32_SPEED(lc->acaps);
+
+	*new_caps = caps;
+}
+
+int t4_set_link_speed(struct port_info *pi, u32 speed, u32 *new_caps)
+{
+	u32 fw_speed_cap = t4_speed_to_fwcap(speed);
+	struct link_config *lc = &pi->link_cfg;
+	u32 caps = *new_caps;
+
+	if (!(lc->pcaps & fw_speed_cap))
+		return -EOPNOTSUPP;
+
+	caps &= ~V_FW_PORT_CAP32_SPEED(M_FW_PORT_CAP32_SPEED);
+	caps |= fw_speed_cap;
+
+	*new_caps = caps;
+
+	return 0;
+}
+
+int t4_set_link_pause(struct port_info *pi, u8 autoneg, u8 pause_tx,
+		      u8 pause_rx, u32 *new_caps)
+{
+	struct link_config *lc = &pi->link_cfg;
+	u32 caps = *new_caps;
+	u32 max_speed;
+
+	max_speed = t4_fwcap_to_speed(lc->link_caps);
+
+	if (autoneg) {
+		if (!(lc->pcaps & FW_PORT_CAP32_ANEG))
+			return -EINVAL;
+
+		caps |= FW_PORT_CAP32_ANEG;
+		t4_set_link_autoneg_speed(pi, &caps);
+	} else {
+		if (!max_speed)
+			max_speed = t4_fwcap_to_speed(lc->acaps);
+
+		caps &= ~FW_PORT_CAP32_ANEG;
+		t4_set_link_speed(pi, max_speed, &caps);
+	}
+
+	if (lc->pcaps & FW_PORT_CAP32_MDIAUTO)
+		caps |= V_FW_PORT_CAP32_MDI(FW_PORT_CAP32_MDI_AUTO);
+
+	caps &= ~V_FW_PORT_CAP32_FC(M_FW_PORT_CAP32_FC);
+	caps &= ~V_FW_PORT_CAP32_802_3(M_FW_PORT_CAP32_802_3);
+	if (pause_tx && pause_rx) {
+		caps |= FW_PORT_CAP32_FC_TX | FW_PORT_CAP32_FC_RX;
+		if (lc->pcaps & FW_PORT_CAP32_802_3_PAUSE)
+			caps |= FW_PORT_CAP32_802_3_PAUSE;
+	} else if (pause_tx) {
+		caps |= FW_PORT_CAP32_FC_TX;
+		if (lc->pcaps & FW_PORT_CAP32_802_3_ASM_DIR)
+			caps |= FW_PORT_CAP32_802_3_ASM_DIR;
+	} else if (pause_rx) {
+		caps |= FW_PORT_CAP32_FC_RX;
+		if (lc->pcaps & FW_PORT_CAP32_802_3_PAUSE)
+			caps |= FW_PORT_CAP32_802_3_PAUSE;
+
+		if (lc->pcaps & FW_PORT_CAP32_802_3_ASM_DIR)
+			caps |= FW_PORT_CAP32_802_3_ASM_DIR;
+	}
+
+	*new_caps = caps;
+
+	return 0;
+}
+
 /**
  * t4_handle_get_port_info - process a FW reply message
  * @pi: the port info
@@ -4459,10 +4486,10 @@  static void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl)
 {
 	const struct fw_port_cmd *cmd = (const void *)rpl;
 	u8 link_ok, link_down_rc, mod_type, port_type;
-	u32 action, pcaps, acaps, linkattr, lstatus;
+	u32 action, pcaps, acaps, link_caps, lstatus;
 	struct link_config *lc = &pi->link_cfg;
 	struct adapter *adapter = pi->adapter;
-	unsigned int speed, fc, fec;
+	u8 mod_changed = 0;
 
 	/* Extract the various fields from the Port Information message.
 	 */
@@ -4481,21 +4508,16 @@  static void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl)
 
 	pcaps = be32_to_cpu(cmd->u.info32.pcaps32);
 	acaps = be32_to_cpu(cmd->u.info32.acaps32);
-	linkattr = be32_to_cpu(cmd->u.info32.linkattr32);
-
-	fec = fwcap_to_cc_fec(acaps);
-
-	fc = fwcap_to_cc_pause(linkattr);
-	speed = fwcap_to_speed(linkattr);
+	link_caps = be32_to_cpu(cmd->u.info32.linkattr32);
 
-	if (mod_type != pi->mod_type) {
-		lc->auto_fec = fec;
-		pi->port_type = port_type;
-		pi->mod_type = mod_type;
+	if (mod_type != lc->mod_type) {
+		t4_init_link_config(pi, pcaps, acaps, lc->mdio_addr,
+				    port_type, mod_type);
 		t4_os_portmod_changed(adapter, pi->pidx);
+		mod_changed = 1;
 	}
-	if (link_ok != lc->link_ok || speed != lc->speed ||
-	    fc != lc->fc || fec != lc->fec) { /* something changed */
+	if (link_ok != lc->link_ok || acaps != lc->acaps ||
+	    link_caps != lc->link_caps) { /* something changed */
 		if (!link_ok && lc->link_ok) {
 			lc->link_down_rc = link_down_rc;
 			dev_warn(adap, "Port %d link down, reason: %s\n",
@@ -4503,23 +4525,19 @@  static void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl)
 				 t4_link_down_rc_str(link_down_rc));
 		}
 		lc->link_ok = link_ok;
-		lc->speed = speed;
-		lc->fc = fc;
-		lc->fec = fec;
-		lc->pcaps = pcaps;
-		lc->acaps = acaps & ADVERT_MASK;
-
-		if (lc->acaps & FW_PORT_CAP32_ANEG) {
-			lc->autoneg = AUTONEG_ENABLE;
-		} else {
-			/* When Autoneg is disabled, user needs to set
-			 * single speed.
-			 * Similar to cxgb4_ethtool.c: set_link_ksettings
-			 */
-			lc->acaps = 0;
-			lc->requested_speed = fwcap_to_speed(acaps);
-			lc->autoneg = AUTONEG_DISABLE;
-		}
+		lc->acaps = acaps;
+		lc->link_caps = link_caps;
+	}
+
+	if (mod_changed) {
+		u32 mod_caps = lc->admin_caps;
+		int ret;
+
+		ret = t4_link_l1cfg_ns(pi, mod_caps);
+		if (ret != FW_SUCCESS)
+			dev_warn(adapter,
+				 "Attempt to update new Transceiver Module settings %#x failed with error: %d\n",
+				 mod_caps, ret);
 	}
 }
 
@@ -4595,45 +4613,49 @@  void t4_reset_link_config(struct adapter *adap, int idx)
 	struct link_config *lc = &pi->link_cfg;
 
 	lc->link_ok = 0;
-	lc->requested_speed = 0;
-	lc->requested_fc = 0;
-	lc->speed = 0;
-	lc->fc = 0;
+	lc->link_down_rc = 0;
+	lc->link_caps = 0;
 }
 
 /**
- * init_link_config - initialize a link's SW state
- * @lc: structure holding the link state
+ * t4_init_link_config - initialize a link's SW state
+ * @pi: the port info
  * @pcaps: link Port Capabilities
  * @acaps: link current Advertised Port Capabilities
+ * @mdio_addr : address of the PHY
+ * @port_type : firmware port type
+ * @mod_type  : firmware module type
  *
  * Initializes the SW state maintained for each link, including the link's
  * capabilities and default speed/flow-control/autonegotiation settings.
  */
-void init_link_config(struct link_config *lc, fw_port_cap32_t pcaps,
-		      fw_port_cap32_t acaps)
+void t4_init_link_config(struct port_info *pi, u32 pcaps, u32 acaps,
+			 u8 mdio_addr, u8 port_type, u8 mod_type)
 {
+	struct link_config *lc = &pi->link_cfg;
+
 	lc->pcaps = pcaps;
-	lc->requested_speed = 0;
-	lc->speed = 0;
-	lc->requested_fc = 0;
-	lc->fc = 0;
+	lc->acaps = acaps;
+	lc->admin_caps = acaps;
+	lc->link_caps = 0;
 
-	/**
-	 * For Forward Error Control, we default to whatever the Firmware
-	 * tells us the Link is currently advertising.
-	 */
-	lc->auto_fec = fwcap_to_cc_fec(acaps);
-	lc->requested_fec = FEC_AUTO;
-	lc->fec = lc->auto_fec;
-
-	if (lc->pcaps & FW_PORT_CAP32_ANEG) {
-		lc->acaps = lc->pcaps & ADVERT_MASK;
-		lc->autoneg = AUTONEG_ENABLE;
-		lc->requested_fc |= PAUSE_AUTONEG;
-	} else {
-		lc->acaps = 0;
-		lc->autoneg = AUTONEG_DISABLE;
+	lc->mdio_addr = mdio_addr;
+	lc->port_type = port_type;
+	lc->mod_type = mod_type;
+
+	lc->link_ok = 0;
+	lc->link_down_rc = 0;
+
+	/* Turn Tx and Rx pause off by default */
+	lc->admin_caps &= ~V_FW_PORT_CAP32_FC(M_FW_PORT_CAP32_FC);
+	lc->admin_caps &= ~V_FW_PORT_CAP32_802_3(M_FW_PORT_CAP32_802_3);
+	if (lc->pcaps & FW_PORT_CAP32_FORCE_PAUSE)
+		lc->admin_caps &= ~FW_PORT_CAP32_FORCE_PAUSE;
+
+	/* Reset MDI to AUTO */
+	if (lc->pcaps & FW_PORT_CAP32_MDIAUTO) {
+		lc->admin_caps &= ~V_FW_PORT_CAP32_MDI(M_FW_PORT_CAP32_MDI);
+		lc->admin_caps |= V_FW_PORT_CAP32_MDI(FW_PORT_CAP32_MDI_AUTO);
 	}
 }
 
@@ -5271,12 +5293,11 @@  int t4_init_rss_mode(struct adapter *adap, int mbox)
 
 int t4_port_init(struct adapter *adap, int mbox, int pf, int vf)
 {
-	fw_port_cap32_t pcaps, acaps;
+	u32 param, val, pcaps, acaps;
 	enum fw_port_type port_type;
 	struct fw_port_cmd cmd;
 	u8 vivld = 0, vin = 0;
 	int ret, i, j = 0;
-	u32 param, val;
 	int mdio_addr;
 	u8 addr[6];
 
@@ -5342,11 +5363,8 @@  int t4_port_init(struct adapter *adap, int mbox, int pf, int vf)
 			pi->vin =  G_FW_VIID_VIN(pi->viid);
 		}
 
-		pi->port_type = port_type;
-		pi->mdio_addr = mdio_addr;
-		pi->mod_type = FW_PORT_MOD_TYPE_NA;
-
-		init_link_config(&pi->link_cfg, pcaps, acaps);
+		t4_init_link_config(pi, pcaps, acaps, mdio_addr, port_type,
+				    FW_PORT_MOD_TYPE_NA);
 		j++;
 	}
 	return 0;
diff --git a/drivers/net/cxgbe/base/t4fw_interface.h b/drivers/net/cxgbe/base/t4fw_interface.h
index 153141247..cfd03cf34 100644
--- a/drivers/net/cxgbe/base/t4fw_interface.h
+++ b/drivers/net/cxgbe/base/t4fw_interface.h
@@ -1615,6 +1615,7 @@  struct fw_vi_stats_cmd {
 #define FW_PORT_CAP32_MDIAUTO           0x00400000UL
 #define FW_PORT_CAP32_FEC_RS            0x00800000UL
 #define FW_PORT_CAP32_FEC_BASER_RS      0x01000000UL
+#define FW_PORT_CAP32_FORCE_PAUSE       0x10000000UL
 
 #define S_FW_PORT_CAP32_SPEED           0
 #define M_FW_PORT_CAP32_SPEED           0xfff
@@ -1622,8 +1623,16 @@  struct fw_vi_stats_cmd {
 #define G_FW_PORT_CAP32_SPEED(x) \
 	(((x) >> S_FW_PORT_CAP32_SPEED) & M_FW_PORT_CAP32_SPEED)
 
+#define S_FW_PORT_CAP32_FC             16
+#define M_FW_PORT_CAP32_FC             0x3
+#define V_FW_PORT_CAP32_FC(x)          ((x) << S_FW_PORT_CAP32_FC)
+
+#define S_FW_PORT_CAP32_802_3          18
+#define M_FW_PORT_CAP32_802_3          0x3
+#define V_FW_PORT_CAP32_802_3(x)       ((x) << S_FW_PORT_CAP32_802_3)
+
 enum fw_port_mdi32 {
-	FW_PORT_CAP32_MDI_AUTO,
+	FW_PORT_CAP32_MDI_AUTO = 1,
 };
 
 #define S_FW_PORT_CAP32_MDI 21
diff --git a/drivers/net/cxgbe/base/t4vf_hw.c b/drivers/net/cxgbe/base/t4vf_hw.c
index 6454029aa..cd011bc1b 100644
--- a/drivers/net/cxgbe/base/t4vf_hw.c
+++ b/drivers/net/cxgbe/base/t4vf_hw.c
@@ -768,9 +768,8 @@  int t4vf_port_init(struct adapter *adapter)
 {
 	struct fw_port_cmd port_cmd, port_rpl, rpl;
 	struct fw_vi_cmd vi_cmd, vi_rpl;
-	fw_port_cap32_t pcaps, acaps;
+	u32 param, val, pcaps, acaps;
 	enum fw_port_type port_type;
-	u32 param, val;
 	int mdio_addr;
 	int ret, i;
 
@@ -844,10 +843,8 @@  int t4vf_port_init(struct adapter *adapter)
 		pcaps = be32_to_cpu(port_rpl.u.info32.pcaps32);
 		acaps = be32_to_cpu(port_rpl.u.info32.acaps32);
 
-		p->port_type = port_type;
-		p->mdio_addr = mdio_addr;
-		p->mod_type = FW_PORT_MOD_TYPE_NA;
-		init_link_config(&p->link_cfg, pcaps, acaps);
+		t4_init_link_config(p, pcaps, acaps, mdio_addr, port_type,
+				    FW_PORT_MOD_TYPE_NA);
 	}
 	return 0;
 }
diff --git a/drivers/net/cxgbe/cxgbe_ethdev.c b/drivers/net/cxgbe/cxgbe_ethdev.c
index 98d0362fa..c58e63918 100644
--- a/drivers/net/cxgbe/cxgbe_ethdev.c
+++ b/drivers/net/cxgbe/cxgbe_ethdev.c
@@ -193,11 +193,12 @@  int cxgbe_dev_link_update(struct rte_eth_dev *eth_dev,
 			  int wait_to_complete)
 {
 	struct port_info *pi = eth_dev->data->dev_private;
+	unsigned int i, work_done, budget = 32;
+	struct link_config *lc = &pi->link_cfg;
 	struct adapter *adapter = pi->adapter;
-	struct sge *s = &adapter->sge;
 	struct rte_eth_link new_link = { 0 };
-	unsigned int i, work_done, budget = 32;
 	u8 old_link = pi->link_cfg.link_ok;
+	struct sge *s = &adapter->sge;
 
 	for (i = 0; i < CXGBE_LINK_STATUS_POLL_CNT; i++) {
 		if (!s->fw_evtq.desc)
@@ -218,9 +219,9 @@  int cxgbe_dev_link_update(struct rte_eth_dev *eth_dev,
 
 	new_link.link_status = cxgbe_force_linkup(adapter) ?
 			       ETH_LINK_UP : pi->link_cfg.link_ok;
-	new_link.link_autoneg = pi->link_cfg.autoneg;
+	new_link.link_autoneg = (lc->link_caps & FW_PORT_CAP32_ANEG) ? 1 : 0;
 	new_link.link_duplex = ETH_LINK_FULL_DUPLEX;
-	new_link.link_speed = pi->link_cfg.speed;
+	new_link.link_speed = t4_fwcap_to_speed(lc->link_caps);
 
 	return rte_eth_linkstatus_set(eth_dev, &new_link);
 }
@@ -787,11 +788,17 @@  static int cxgbe_flow_ctrl_get(struct rte_eth_dev *eth_dev,
 {
 	struct port_info *pi = eth_dev->data->dev_private;
 	struct link_config *lc = &pi->link_cfg;
-	int rx_pause, tx_pause;
+	u8 rx_pause = 0, tx_pause = 0;
+	u32 caps = lc->link_caps;
 
-	fc_conf->autoneg = lc->fc & PAUSE_AUTONEG;
-	rx_pause = lc->fc & PAUSE_RX;
-	tx_pause = lc->fc & PAUSE_TX;
+	if (caps & FW_PORT_CAP32_ANEG)
+		fc_conf->autoneg = 1;
+
+	if (caps & FW_PORT_CAP32_FC_TX)
+		tx_pause = 1;
+
+	if (caps & FW_PORT_CAP32_FC_RX)
+		rx_pause = 1;
 
 	if (rx_pause && tx_pause)
 		fc_conf->mode = RTE_FC_FULL;
@@ -808,30 +815,39 @@  static int cxgbe_flow_ctrl_set(struct rte_eth_dev *eth_dev,
 			       struct rte_eth_fc_conf *fc_conf)
 {
 	struct port_info *pi = eth_dev->data->dev_private;
-	struct adapter *adapter = pi->adapter;
 	struct link_config *lc = &pi->link_cfg;
+	u32 new_caps = lc->admin_caps;
+	u8 tx_pause = 0, rx_pause = 0;
+	int ret;
 
-	if (lc->pcaps & FW_PORT_CAP32_ANEG) {
-		if (fc_conf->autoneg)
-			lc->requested_fc |= PAUSE_AUTONEG;
-		else
-			lc->requested_fc &= ~PAUSE_AUTONEG;
+	if (fc_conf->mode == RTE_FC_FULL) {
+		tx_pause = 1;
+		rx_pause = 1;
+	} else if (fc_conf->mode == RTE_FC_TX_PAUSE) {
+		tx_pause = 1;
+	} else if (fc_conf->mode == RTE_FC_RX_PAUSE) {
+		rx_pause = 1;
 	}
 
-	if (((fc_conf->mode & RTE_FC_FULL) == RTE_FC_FULL) ||
-	    (fc_conf->mode & RTE_FC_RX_PAUSE))
-		lc->requested_fc |= PAUSE_RX;
-	else
-		lc->requested_fc &= ~PAUSE_RX;
+	ret = t4_set_link_pause(pi, fc_conf->autoneg, tx_pause,
+				rx_pause, &new_caps);
+	if (ret != 0)
+		return ret;
 
-	if (((fc_conf->mode & RTE_FC_FULL) == RTE_FC_FULL) ||
-	    (fc_conf->mode & RTE_FC_TX_PAUSE))
-		lc->requested_fc |= PAUSE_TX;
-	else
-		lc->requested_fc &= ~PAUSE_TX;
+	if (!fc_conf->autoneg) {
+		if (lc->pcaps & FW_PORT_CAP32_FORCE_PAUSE)
+			new_caps |= FW_PORT_CAP32_FORCE_PAUSE;
+	} else {
+		new_caps &= ~FW_PORT_CAP32_FORCE_PAUSE;
+	}
+
+	if (new_caps != lc->admin_caps) {
+		ret = t4_link_l1cfg(pi, new_caps);
+		if (ret == 0)
+			lc->admin_caps = new_caps;
+	}
 
-	return t4_link_l1cfg(adapter, adapter->mbox, pi->tx_chan,
-			     &pi->link_cfg);
+	return ret;
 }
 
 const uint32_t *
diff --git a/drivers/net/cxgbe/cxgbe_main.c b/drivers/net/cxgbe/cxgbe_main.c
index 53b08a64a..3b56eccfb 100644
--- a/drivers/net/cxgbe/cxgbe_main.c
+++ b/drivers/net/cxgbe/cxgbe_main.c
@@ -732,7 +732,7 @@  void cxgbe_print_port_info(struct adapter *adap)
 			--bufp;
 		sprintf(bufp, "BASE-%s",
 			t4_get_port_type_description(
-					(enum fw_port_type)pi->port_type));
+				(enum fw_port_type)pi->link_cfg.port_type));
 
 		dev_info(adap,
 			 " " PCI_PRI_FMT " Chelsio rev %d %s %s\n",
@@ -1575,23 +1575,23 @@  void t4_os_portmod_changed(const struct adapter *adap, int port_id)
 
 	const struct port_info *pi = adap2pinfo(adap, port_id);
 
-	if (pi->mod_type == FW_PORT_MOD_TYPE_NONE)
+	if (pi->link_cfg.mod_type == FW_PORT_MOD_TYPE_NONE)
 		dev_info(adap, "Port%d: port module unplugged\n", pi->port_id);
-	else if (pi->mod_type < ARRAY_SIZE(mod_str))
+	else if (pi->link_cfg.mod_type < ARRAY_SIZE(mod_str))
 		dev_info(adap, "Port%d: %s port module inserted\n", pi->port_id,
-			 mod_str[pi->mod_type]);
-	else if (pi->mod_type == FW_PORT_MOD_TYPE_NOTSUPPORTED)
+			 mod_str[pi->link_cfg.mod_type]);
+	else if (pi->link_cfg.mod_type == FW_PORT_MOD_TYPE_NOTSUPPORTED)
 		dev_info(adap, "Port%d: unsupported port module inserted\n",
 			 pi->port_id);
-	else if (pi->mod_type == FW_PORT_MOD_TYPE_UNKNOWN)
+	else if (pi->link_cfg.mod_type == FW_PORT_MOD_TYPE_UNKNOWN)
 		dev_info(adap, "Port%d: unknown port module inserted\n",
 			 pi->port_id);
-	else if (pi->mod_type == FW_PORT_MOD_TYPE_ERROR)
+	else if (pi->link_cfg.mod_type == FW_PORT_MOD_TYPE_ERROR)
 		dev_info(adap, "Port%d: transceiver module error\n",
 			 pi->port_id);
 	else
 		dev_info(adap, "Port%d: unknown module type %d inserted\n",
-			 pi->port_id, pi->mod_type);
+			 pi->port_id, pi->link_cfg.mod_type);
 }
 
 bool cxgbe_force_linkup(struct adapter *adap)
@@ -1636,8 +1636,7 @@  int cxgbe_link_start(struct port_info *pi)
 		}
 	}
 	if (ret == 0 && is_pf4(adapter))
-		ret = t4_link_l1cfg(adapter, adapter->mbox, pi->tx_chan,
-				    &pi->link_cfg);
+		ret = t4_link_l1cfg(pi, pi->link_cfg.admin_caps);
 	if (ret == 0) {
 		/*
 		 * Enabling a Virtual Interface can result in an interrupt
@@ -1905,7 +1904,7 @@  void cxgbe_get_speed_caps(struct port_info *pi, u32 *speed_caps)
 {
 	*speed_caps = 0;
 
-	fw_caps_to_speed_caps(pi->port_type, pi->link_cfg.pcaps,
+	fw_caps_to_speed_caps(pi->link_cfg.port_type, pi->link_cfg.pcaps,
 			      speed_caps);
 
 	if (!(pi->link_cfg.pcaps & FW_PORT_CAP32_ANEG))