These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / net / ethernet / intel / ixgbe / ixgbe_x550.c
index cf5cf81..ebe0ac9 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  *  Intel 10 Gigabit PCI Express Linux driver
- *  Copyright(c) 1999 - 2014 Intel Corporation.
+ *  Copyright(c) 1999 - 2015 Intel Corporation.
  *
  *  This program is free software; you can redistribute it and/or modify it
  *  under the terms and conditions of the GNU General Public License,
 #include "ixgbe_common.h"
 #include "ixgbe_phy.h"
 
+static s32 ixgbe_get_invariants_X550_x(struct ixgbe_hw *hw)
+{
+       struct ixgbe_mac_info *mac = &hw->mac;
+       struct ixgbe_phy_info *phy = &hw->phy;
+
+       /* Start with X540 invariants, since so simular */
+       ixgbe_get_invariants_X540(hw);
+
+       if (mac->ops.get_media_type(hw) != ixgbe_media_type_copper)
+               phy->ops.set_phy_power = NULL;
+
+       return 0;
+}
+
+/** ixgbe_setup_mux_ctl - Setup ESDP register for I2C mux control
+ *  @hw: pointer to hardware structure
+ **/
+static void ixgbe_setup_mux_ctl(struct ixgbe_hw *hw)
+{
+       u32 esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
+
+       if (hw->bus.lan_id) {
+               esdp &= ~(IXGBE_ESDP_SDP1_NATIVE | IXGBE_ESDP_SDP1);
+               esdp |= IXGBE_ESDP_SDP1_DIR;
+       }
+       esdp &= ~(IXGBE_ESDP_SDP0_NATIVE | IXGBE_ESDP_SDP0_DIR);
+       IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
+       IXGBE_WRITE_FLUSH(hw);
+}
+
+/**
+ * ixgbe_read_cs4227 - Read CS4227 register
+ * @hw: pointer to hardware structure
+ * @reg: register number to write
+ * @value: pointer to receive value read
+ *
+ * Returns status code
+ */
+static s32 ixgbe_read_cs4227(struct ixgbe_hw *hw, u16 reg, u16 *value)
+{
+       return hw->phy.ops.read_i2c_combined_unlocked(hw, IXGBE_CS4227, reg,
+                                                     value);
+}
+
+/**
+ * ixgbe_write_cs4227 - Write CS4227 register
+ * @hw: pointer to hardware structure
+ * @reg: register number to write
+ * @value: value to write to register
+ *
+ * Returns status code
+ */
+static s32 ixgbe_write_cs4227(struct ixgbe_hw *hw, u16 reg, u16 value)
+{
+       return hw->phy.ops.write_i2c_combined_unlocked(hw, IXGBE_CS4227, reg,
+                                                      value);
+}
+
+/**
+ * ixgbe_check_cs4227_reg - Perform diag on a CS4227 register
+ * @hw: pointer to hardware structure
+ * @reg: the register to check
+ *
+ * Performs a diagnostic on a register in the CS4227 chip. Returns an error
+ * if it is not operating correctly.
+ * This function assumes that the caller has acquired the proper semaphore.
+ */
+static s32 ixgbe_check_cs4227_reg(struct ixgbe_hw *hw, u16 reg)
+{
+       s32 status;
+       u32 retry;
+       u16 reg_val;
+
+       reg_val = (IXGBE_CS4227_EDC_MODE_DIAG << 1) | 1;
+       status = ixgbe_write_cs4227(hw, reg, reg_val);
+       if (status)
+               return status;
+       for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) {
+               msleep(IXGBE_CS4227_CHECK_DELAY);
+               reg_val = 0xFFFF;
+               ixgbe_read_cs4227(hw, reg, &reg_val);
+               if (!reg_val)
+                       break;
+       }
+       if (reg_val) {
+               hw_err(hw, "CS4227 reg 0x%04X failed diagnostic\n", reg);
+               return status;
+       }
+
+       return 0;
+}
+
+/**
+ * ixgbe_get_cs4227_status - Return CS4227 status
+ * @hw: pointer to hardware structure
+ *
+ * Performs a diagnostic on the CS4227 chip. Returns an error if it is
+ * not operating correctly.
+ * This function assumes that the caller has acquired the proper semaphore.
+ */
+static s32 ixgbe_get_cs4227_status(struct ixgbe_hw *hw)
+{
+       s32 status;
+       u16 value = 0;
+
+       /* Exit if the diagnostic has already been performed. */
+       status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value);
+       if (status)
+               return status;
+       if (value == IXGBE_CS4227_RESET_COMPLETE)
+               return 0;
+
+       /* Check port 0. */
+       status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB);
+       if (status)
+               return status;
+
+       status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB);
+       if (status)
+               return status;
+
+       /* Check port 1. */
+       status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB +
+                                       (1 << 12));
+       if (status)
+               return status;
+
+       return ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB +
+                                     (1 << 12));
+}
+
+/**
+ * ixgbe_read_pe - Read register from port expander
+ * @hw: pointer to hardware structure
+ * @reg: register number to read
+ * @value: pointer to receive read value
+ *
+ * Returns status code
+ */
+static s32 ixgbe_read_pe(struct ixgbe_hw *hw, u8 reg, u8 *value)
+{
+       s32 status;
+
+       status = ixgbe_read_i2c_byte_generic_unlocked(hw, reg, IXGBE_PE, value);
+       if (status)
+               hw_err(hw, "port expander access failed with %d\n", status);
+       return status;
+}
+
+/**
+ * ixgbe_write_pe - Write register to port expander
+ * @hw: pointer to hardware structure
+ * @reg: register number to write
+ * @value: value to write
+ *
+ * Returns status code
+ */
+static s32 ixgbe_write_pe(struct ixgbe_hw *hw, u8 reg, u8 value)
+{
+       s32 status;
+
+       status = ixgbe_write_i2c_byte_generic_unlocked(hw, reg, IXGBE_PE,
+                                                      value);
+       if (status)
+               hw_err(hw, "port expander access failed with %d\n", status);
+       return status;
+}
+
+/**
+ * ixgbe_reset_cs4227 - Reset CS4227 using port expander
+ * @hw: pointer to hardware structure
+ *
+ * This function assumes that the caller has acquired the proper semaphore.
+ * Returns error code
+ */
+static s32 ixgbe_reset_cs4227(struct ixgbe_hw *hw)
+{
+       s32 status;
+       u32 retry;
+       u16 value;
+       u8 reg;
+
+       /* Trigger hard reset. */
+       status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, &reg);
+       if (status)
+               return status;
+       reg |= IXGBE_PE_BIT1;
+       status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg);
+       if (status)
+               return status;
+
+       status = ixgbe_read_pe(hw, IXGBE_PE_CONFIG, &reg);
+       if (status)
+               return status;
+       reg &= ~IXGBE_PE_BIT1;
+       status = ixgbe_write_pe(hw, IXGBE_PE_CONFIG, reg);
+       if (status)
+               return status;
+
+       status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, &reg);
+       if (status)
+               return status;
+       reg &= ~IXGBE_PE_BIT1;
+       status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg);
+       if (status)
+               return status;
+
+       usleep_range(IXGBE_CS4227_RESET_HOLD, IXGBE_CS4227_RESET_HOLD + 100);
+
+       status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, &reg);
+       if (status)
+               return status;
+       reg |= IXGBE_PE_BIT1;
+       status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg);
+       if (status)
+               return status;
+
+       /* Wait for the reset to complete. */
+       msleep(IXGBE_CS4227_RESET_DELAY);
+       for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) {
+               status = ixgbe_read_cs4227(hw, IXGBE_CS4227_EFUSE_STATUS,
+                                          &value);
+               if (!status && value == IXGBE_CS4227_EEPROM_LOAD_OK)
+                       break;
+               msleep(IXGBE_CS4227_CHECK_DELAY);
+       }
+       if (retry == IXGBE_CS4227_RETRIES) {
+               hw_err(hw, "CS4227 reset did not complete\n");
+               return IXGBE_ERR_PHY;
+       }
+
+       status = ixgbe_read_cs4227(hw, IXGBE_CS4227_EEPROM_STATUS, &value);
+       if (status || !(value & IXGBE_CS4227_EEPROM_LOAD_OK)) {
+               hw_err(hw, "CS4227 EEPROM did not load successfully\n");
+               return IXGBE_ERR_PHY;
+       }
+
+       return 0;
+}
+
+/**
+ * ixgbe_check_cs4227 - Check CS4227 and reset as needed
+ * @hw: pointer to hardware structure
+ */
+static void ixgbe_check_cs4227(struct ixgbe_hw *hw)
+{
+       u32 swfw_mask = hw->phy.phy_semaphore_mask;
+       s32 status;
+       u16 value;
+       u8 retry;
+
+       for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) {
+               status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask);
+               if (status) {
+                       hw_err(hw, "semaphore failed with %d\n", status);
+                       msleep(IXGBE_CS4227_CHECK_DELAY);
+                       continue;
+               }
+
+               /* Get status of reset flow. */
+               status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value);
+               if (!status && value == IXGBE_CS4227_RESET_COMPLETE)
+                       goto out;
+
+               if (status || value != IXGBE_CS4227_RESET_PENDING)
+                       break;
+
+               /* Reset is pending. Wait and check again. */
+               hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+               msleep(IXGBE_CS4227_CHECK_DELAY);
+       }
+       /* If still pending, assume other instance failed. */
+       if (retry == IXGBE_CS4227_RETRIES) {
+               status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask);
+               if (status) {
+                       hw_err(hw, "semaphore failed with %d\n", status);
+                       return;
+               }
+       }
+
+       /* Reset the CS4227. */
+       status = ixgbe_reset_cs4227(hw);
+       if (status) {
+               hw_err(hw, "CS4227 reset failed: %d", status);
+               goto out;
+       }
+
+       /* Reset takes so long, temporarily release semaphore in case the
+        * other driver instance is waiting for the reset indication.
+        */
+       ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH,
+                          IXGBE_CS4227_RESET_PENDING);
+       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+       usleep_range(10000, 12000);
+       status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask);
+       if (status) {
+               hw_err(hw, "semaphore failed with %d", status);
+               return;
+       }
+
+       /* Is the CS4227 working correctly? */
+       status = ixgbe_get_cs4227_status(hw);
+       if (status) {
+               hw_err(hw, "CS4227 status failed: %d", status);
+               goto out;
+       }
+
+       /* Record completion for next time. */
+       status = ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH,
+                                   IXGBE_CS4227_RESET_COMPLETE);
+
+out:
+       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+       msleep(hw->eeprom.semaphore_delay);
+}
+
 /** ixgbe_identify_phy_x550em - Get PHY type based on device id
  *  @hw: pointer to hardware structure
  *
  */
 static s32 ixgbe_identify_phy_x550em(struct ixgbe_hw *hw)
 {
-       u32 esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
-
        switch (hw->device_id) {
        case IXGBE_DEV_ID_X550EM_X_SFP:
                /* set up for CS4227 usage */
                hw->phy.phy_semaphore_mask = IXGBE_GSSR_SHARED_I2C_SM;
-               if (hw->bus.lan_id) {
-                       esdp &= ~(IXGBE_ESDP_SDP1_NATIVE | IXGBE_ESDP_SDP1);
-                       esdp |= IXGBE_ESDP_SDP1_DIR;
-               }
-               esdp &= ~(IXGBE_ESDP_SDP0_NATIVE | IXGBE_ESDP_SDP0_DIR);
-               IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
-
+               ixgbe_setup_mux_ctl(hw);
+               ixgbe_check_cs4227(hw);
                return ixgbe_identify_module_generic(hw);
        case IXGBE_DEV_ID_X550EM_X_KX4:
                hw->phy.type = ixgbe_phy_x550em_kx4;
@@ -90,7 +399,7 @@ static s32 ixgbe_init_eeprom_params_X550(struct ixgbe_hw *hw)
                eeprom->semaphore_delay = 10;
                eeprom->type = ixgbe_flash;
 
-               eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+               eec = IXGBE_READ_REG(hw, IXGBE_EEC(hw));
                eeprom_size = (u16)((eec & IXGBE_EEC_SIZE) >>
                                    IXGBE_EEC_SIZE_SHIFT);
                eeprom->word_size = 1 << (eeprom_size +
@@ -103,6 +412,39 @@ static s32 ixgbe_init_eeprom_params_X550(struct ixgbe_hw *hw)
        return 0;
 }
 
+/**
+ * ixgbe_iosf_wait - Wait for IOSF command completion
+ * @hw: pointer to hardware structure
+ * @ctrl: pointer to location to receive final IOSF control value
+ *
+ * Return: failing status on timeout
+ *
+ * Note: ctrl can be NULL if the IOSF control register value is not needed
+ */
+static s32 ixgbe_iosf_wait(struct ixgbe_hw *hw, u32 *ctrl)
+{
+       u32 i, command;
+
+       /* Check every 10 usec to see if the address cycle completed.
+        * The SB IOSF BUSY bit will clear when the operation is
+        * complete.
+        */
+       for (i = 0; i < IXGBE_MDIO_COMMAND_TIMEOUT; i++) {
+               command = IXGBE_READ_REG(hw, IXGBE_SB_IOSF_INDIRECT_CTRL);
+               if (!(command & IXGBE_SB_IOSF_CTRL_BUSY))
+                       break;
+               usleep_range(10, 20);
+       }
+       if (ctrl)
+               *ctrl = command;
+       if (i == IXGBE_MDIO_COMMAND_TIMEOUT) {
+               hw_dbg(hw, "IOSF wait timed out\n");
+               return IXGBE_ERR_PHY;
+       }
+
+       return 0;
+}
+
 /** ixgbe_read_iosf_sb_reg_x550 - Writes a value to specified register of the
  *  IOSF device
  *  @hw: pointer to hardware structure
@@ -113,7 +455,17 @@ static s32 ixgbe_init_eeprom_params_X550(struct ixgbe_hw *hw)
 static s32 ixgbe_read_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr,
                                       u32 device_type, u32 *data)
 {
-       u32 i, command, error;
+       u32 gssr = IXGBE_GSSR_PHY1_SM | IXGBE_GSSR_PHY0_SM;
+       u32 command, error;
+       s32 ret;
+
+       ret = hw->mac.ops.acquire_swfw_sync(hw, gssr);
+       if (ret)
+               return ret;
+
+       ret = ixgbe_iosf_wait(hw, NULL);
+       if (ret)
+               goto out;
 
        command = ((reg_addr << IXGBE_SB_IOSF_CTRL_ADDR_SHIFT) |
                   (device_type << IXGBE_SB_IOSF_CTRL_TARGET_SELECT_SHIFT));
@@ -121,17 +473,7 @@ static s32 ixgbe_read_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr,
        /* Write IOSF control register */
        IXGBE_WRITE_REG(hw, IXGBE_SB_IOSF_INDIRECT_CTRL, command);
 
-       /* Check every 10 usec to see if the address cycle completed.
-        * The SB IOSF BUSY bit will clear when the operation is
-        * complete
-        */
-       for (i = 0; i < IXGBE_MDIO_COMMAND_TIMEOUT; i++) {
-               usleep_range(10, 20);
-
-               command = IXGBE_READ_REG(hw, IXGBE_SB_IOSF_INDIRECT_CTRL);
-               if ((command & IXGBE_SB_IOSF_CTRL_BUSY) == 0)
-                       break;
-       }
+       ret = ixgbe_iosf_wait(hw, &command);
 
        if ((command & IXGBE_SB_IOSF_CTRL_RESP_STAT_MASK) != 0) {
                error = (command & IXGBE_SB_IOSF_CTRL_CMPL_ERR_MASK) >>
@@ -140,14 +482,12 @@ static s32 ixgbe_read_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr,
                return IXGBE_ERR_PHY;
        }
 
-       if (i == IXGBE_MDIO_COMMAND_TIMEOUT) {
-               hw_dbg(hw, "Read timed out\n");
-               return IXGBE_ERR_PHY;
-       }
-
-       *data = IXGBE_READ_REG(hw, IXGBE_SB_IOSF_INDIRECT_DATA);
+       if (!ret)
+               *data = IXGBE_READ_REG(hw, IXGBE_SB_IOSF_INDIRECT_DATA);
 
-       return 0;
+out:
+       hw->mac.ops.release_swfw_sync(hw, gssr);
+       return ret;
 }
 
 /** ixgbe_read_ee_hostif_data_X550 - Read EEPROM word using a host interface
@@ -557,6 +897,24 @@ static s32 ixgbe_update_flash_X550(struct ixgbe_hw *hw)
        return status;
 }
 
+/**
+ * ixgbe_get_bus_info_X550em - Set PCI bus info
+ * @hw: pointer to hardware structure
+ *
+ * Sets bus link width and speed to unknown because X550em is
+ * not a PCI device.
+ **/
+static s32 ixgbe_get_bus_info_X550em(struct ixgbe_hw *hw)
+{
+       hw->bus.type  = ixgbe_bus_type_internal;
+       hw->bus.width = ixgbe_bus_width_unknown;
+       hw->bus.speed = ixgbe_bus_speed_unknown;
+
+       hw->mac.ops.set_lan_id(hw);
+
+       return 0;
+}
+
 /** ixgbe_disable_rx_x550 - Disable RX unit
  *
  *  Enables the Rx DMA unit for x550
@@ -673,144 +1031,39 @@ static s32 ixgbe_write_ee_hostif_buffer_X550(struct ixgbe_hw *hw,
        return status;
 }
 
-/** ixgbe_init_mac_link_ops_X550em - init mac link function pointers
+/** ixgbe_write_iosf_sb_reg_x550 - Writes a value to specified register of the
+ *  IOSF device
+ *
  *  @hw: pointer to hardware structure
+ *  @reg_addr: 32 bit PHY register to write
+ *  @device_type: 3 bit device type
+ *  @data: Data to write to the register
  **/
-static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw)
+static s32 ixgbe_write_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr,
+                                       u32 device_type, u32 data)
 {
-       struct ixgbe_mac_info *mac = &hw->mac;
+       u32 gssr = IXGBE_GSSR_PHY1_SM | IXGBE_GSSR_PHY0_SM;
+       u32 command, error;
+       s32 ret;
 
-       /* CS4227 does not support autoneg, so disable the laser control
-        * functions for SFP+ fiber
-        */
-       if (hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP) {
-               mac->ops.disable_tx_laser = NULL;
-               mac->ops.enable_tx_laser = NULL;
-               mac->ops.flap_tx_laser = NULL;
-       }
-}
+       ret = hw->mac.ops.acquire_swfw_sync(hw, gssr);
+       if (ret)
+               return ret;
 
-/** ixgbe_setup_sfp_modules_X550em - Setup SFP module
- * @hw: pointer to hardware structure
- */
-static s32 ixgbe_setup_sfp_modules_X550em(struct ixgbe_hw *hw)
-{
-       bool setup_linear;
-       u16 reg_slice, edc_mode;
-       s32 ret_val;
+       ret = ixgbe_iosf_wait(hw, NULL);
+       if (ret)
+               goto out;
 
-       switch (hw->phy.sfp_type) {
-       case ixgbe_sfp_type_unknown:
-               return 0;
-       case ixgbe_sfp_type_not_present:
-               return IXGBE_ERR_SFP_NOT_PRESENT;
-       case ixgbe_sfp_type_da_cu_core0:
-       case ixgbe_sfp_type_da_cu_core1:
-               setup_linear = true;
-               break;
-       case ixgbe_sfp_type_srlr_core0:
-       case ixgbe_sfp_type_srlr_core1:
-       case ixgbe_sfp_type_da_act_lmt_core0:
-       case ixgbe_sfp_type_da_act_lmt_core1:
-       case ixgbe_sfp_type_1g_sx_core0:
-       case ixgbe_sfp_type_1g_sx_core1:
-               setup_linear = false;
-               break;
-       default:
-               return IXGBE_ERR_SFP_NOT_SUPPORTED;
-       }
+       command = ((reg_addr << IXGBE_SB_IOSF_CTRL_ADDR_SHIFT) |
+                  (device_type << IXGBE_SB_IOSF_CTRL_TARGET_SELECT_SHIFT));
 
-       ixgbe_init_mac_link_ops_X550em(hw);
-       hw->phy.ops.reset = NULL;
-
-       /* The CS4227 slice address is the base address + the port-pair reg
-        * offset. I.e. Slice 0 = 0x12B0 and slice 1 = 0x22B0.
-        */
-       reg_slice = IXGBE_CS4227_SPARE24_LSB + (hw->bus.lan_id << 12);
-
-       if (setup_linear)
-               edc_mode = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 0x1;
-       else
-               edc_mode = (IXGBE_CS4227_EDC_MODE_SR << 1) | 0x1;
-
-       /* Configure CS4227 for connection type. */
-       ret_val = hw->phy.ops.write_i2c_combined(hw, IXGBE_CS4227, reg_slice,
-                                                edc_mode);
-
-       if (ret_val)
-               ret_val = hw->phy.ops.write_i2c_combined(hw, 0x80, reg_slice,
-                                                        edc_mode);
-
-       return ret_val;
-}
-
-/** ixgbe_get_link_capabilities_x550em - Determines link capabilities
- * @hw: pointer to hardware structure
- * @speed: pointer to link speed
- * @autoneg: true when autoneg or autotry is enabled
- **/
-static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw,
-                                             ixgbe_link_speed *speed,
-                                             bool *autoneg)
-{
-       /* SFP */
-       if (hw->phy.media_type == ixgbe_media_type_fiber) {
-               /* CS4227 SFP must not enable auto-negotiation */
-               *autoneg = false;
-
-               if (hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core0 ||
-                   hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1) {
-                       *speed = IXGBE_LINK_SPEED_1GB_FULL;
-                       return 0;
-               }
-
-               /* Link capabilities are based on SFP */
-               if (hw->phy.multispeed_fiber)
-                       *speed = IXGBE_LINK_SPEED_10GB_FULL |
-                                IXGBE_LINK_SPEED_1GB_FULL;
-               else
-                       *speed = IXGBE_LINK_SPEED_10GB_FULL;
-       } else {
-               *speed = IXGBE_LINK_SPEED_10GB_FULL |
-                        IXGBE_LINK_SPEED_1GB_FULL;
-               *autoneg = true;
-       }
-       return 0;
-}
-
-/** ixgbe_write_iosf_sb_reg_x550 - Writes a value to specified register of the
- *  IOSF device
- *
- *  @hw: pointer to hardware structure
- *  @reg_addr: 32 bit PHY register to write
- *  @device_type: 3 bit device type
- *  @data: Data to write to the register
- **/
-static s32 ixgbe_write_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr,
-                                       u32 device_type, u32 data)
-{
-       u32 i, command, error;
-
-       command = ((reg_addr << IXGBE_SB_IOSF_CTRL_ADDR_SHIFT) |
-                  (device_type << IXGBE_SB_IOSF_CTRL_TARGET_SELECT_SHIFT));
-
-       /* Write IOSF control register */
-       IXGBE_WRITE_REG(hw, IXGBE_SB_IOSF_INDIRECT_CTRL, command);
+       /* Write IOSF control register */
+       IXGBE_WRITE_REG(hw, IXGBE_SB_IOSF_INDIRECT_CTRL, command);
 
        /* Write IOSF data register */
        IXGBE_WRITE_REG(hw, IXGBE_SB_IOSF_INDIRECT_DATA, data);
 
-       /* Check every 10 usec to see if the address cycle completed.
-        * The SB IOSF BUSY bit will clear when the operation is
-        * complete
-        */
-       for (i = 0; i < IXGBE_MDIO_COMMAND_TIMEOUT; i++) {
-               usleep_range(10, 20);
-
-               command = IXGBE_READ_REG(hw, IXGBE_SB_IOSF_INDIRECT_CTRL);
-               if ((command & IXGBE_SB_IOSF_CTRL_BUSY) == 0)
-                       break;
-       }
+       ret = ixgbe_iosf_wait(hw, &command);
 
        if ((command & IXGBE_SB_IOSF_CTRL_RESP_STAT_MASK) != 0) {
                error = (command & IXGBE_SB_IOSF_CTRL_CMPL_ERR_MASK) >>
@@ -819,12 +1072,9 @@ static s32 ixgbe_write_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr,
                return IXGBE_ERR_PHY;
        }
 
-       if (i == IXGBE_MDIO_COMMAND_TIMEOUT) {
-               hw_dbg(hw, "Write timed out\n");
-               return IXGBE_ERR_PHY;
-       }
-
-       return 0;
+out:
+       hw->mac.ops.release_swfw_sync(hw, gssr);
+       return ret;
 }
 
 /** ixgbe_setup_ixfi_x550em - Configure the KR PHY for iXFI mode.
@@ -863,7 +1113,7 @@ static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed)
        }
 
        status = ixgbe_write_iosf_sb_reg_x550(hw,
-                               IXGBE_KRM_RX_TRN_LINKUP_CTRL(hw->bus.lan_id),
+                               IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
                                IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
        if (status)
                return status;
@@ -913,34 +1163,504 @@ static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed)
        if (status)
                return status;
 
-       /* Enable override for coefficients. */
-       status = ixgbe_read_iosf_sb_reg_x550(hw,
-                               IXGBE_KRM_TX_COEFF_CTRL_1(hw->bus.lan_id),
-                               IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
+       /* Enable override for coefficients. */
+       status = ixgbe_read_iosf_sb_reg_x550(hw,
+                               IXGBE_KRM_TX_COEFF_CTRL_1(hw->bus.lan_id),
+                               IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
+       if (status)
+               return status;
+
+       reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_OVRRD_EN;
+       reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_CZERO_EN;
+       reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_CPLUS1_OVRRD_EN;
+       reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_CMINUS1_OVRRD_EN;
+       status = ixgbe_write_iosf_sb_reg_x550(hw,
+                               IXGBE_KRM_TX_COEFF_CTRL_1(hw->bus.lan_id),
+                               IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
+       if (status)
+               return status;
+
+       /* Toggle port SW reset by AN reset. */
+       status = ixgbe_read_iosf_sb_reg_x550(hw,
+                               IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
+                               IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
+       if (status)
+               return status;
+
+       reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART;
+       status = ixgbe_write_iosf_sb_reg_x550(hw,
+                               IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
+                               IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
+
+       return status;
+}
+
+/**
+ *  ixgbe_supported_sfp_modules_X550em - Check if SFP module type is supported
+ *  @hw: pointer to hardware structure
+ *  @linear: true if SFP module is linear
+ */
+static s32 ixgbe_supported_sfp_modules_X550em(struct ixgbe_hw *hw, bool *linear)
+{
+       switch (hw->phy.sfp_type) {
+       case ixgbe_sfp_type_not_present:
+               return IXGBE_ERR_SFP_NOT_PRESENT;
+       case ixgbe_sfp_type_da_cu_core0:
+       case ixgbe_sfp_type_da_cu_core1:
+               *linear = true;
+               break;
+       case ixgbe_sfp_type_srlr_core0:
+       case ixgbe_sfp_type_srlr_core1:
+       case ixgbe_sfp_type_da_act_lmt_core0:
+       case ixgbe_sfp_type_da_act_lmt_core1:
+       case ixgbe_sfp_type_1g_sx_core0:
+       case ixgbe_sfp_type_1g_sx_core1:
+       case ixgbe_sfp_type_1g_lx_core0:
+       case ixgbe_sfp_type_1g_lx_core1:
+               *linear = false;
+               break;
+       case ixgbe_sfp_type_unknown:
+       case ixgbe_sfp_type_1g_cu_core0:
+       case ixgbe_sfp_type_1g_cu_core1:
+       default:
+               return IXGBE_ERR_SFP_NOT_SUPPORTED;
+       }
+
+       return 0;
+}
+
+/**
+ *  ixgbe_setup_mac_link_sfp_x550em - Configure the KR PHY for SFP.
+ *  @hw: pointer to hardware structure
+ *
+ *  Configures the extern PHY and the integrated KR PHY for SFP support.
+ */
+static s32
+ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw,
+                               ixgbe_link_speed speed,
+                               __always_unused bool autoneg_wait_to_complete)
+{
+       s32 status;
+       u16 slice, value;
+       bool setup_linear = false;
+
+       /* Check if SFP module is supported and linear */
+       status = ixgbe_supported_sfp_modules_X550em(hw, &setup_linear);
+
+       /* If no SFP module present, then return success. Return success since
+        * there is no reason to configure CS4227 and SFP not present error is
+        * not accepted in the setup MAC link flow.
+        */
+       if (status == IXGBE_ERR_SFP_NOT_PRESENT)
+               return 0;
+
+       if (status)
+               return status;
+
+       /* Configure CS4227 LINE side to 10G SR. */
+       slice = IXGBE_CS4227_LINE_SPARE22_MSB + (hw->bus.lan_id << 12);
+       value = IXGBE_CS4227_SPEED_10G;
+       status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
+                                                 value);
+
+       /* Configure CS4227 for HOST connection rate then type. */
+       slice = IXGBE_CS4227_HOST_SPARE22_MSB + (hw->bus.lan_id << 12);
+       value = speed & IXGBE_LINK_SPEED_10GB_FULL ?
+               IXGBE_CS4227_SPEED_10G : IXGBE_CS4227_SPEED_1G;
+       status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
+                                                 value);
+
+       slice = IXGBE_CS4227_HOST_SPARE24_LSB + (hw->bus.lan_id << 12);
+       if (setup_linear)
+               value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1;
+       else
+               value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1;
+       status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
+                                                 value);
+
+       /* If internal link mode is XFI, then setup XFI internal link. */
+       if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE))
+               status = ixgbe_setup_ixfi_x550em(hw, &speed);
+
+       return status;
+}
+
+/**
+ * ixgbe_setup_mac_link_t_X550em - Sets the auto advertised link speed
+ * @hw: pointer to hardware structure
+ * @speed: new link speed
+ * @autoneg_wait_to_complete: true when waiting for completion is needed
+ *
+ * Setup internal/external PHY link speed based on link speed, then set
+ * external PHY auto advertised link speed.
+ *
+ * Returns error status for any failure
+ **/
+static s32 ixgbe_setup_mac_link_t_X550em(struct ixgbe_hw *hw,
+                                        ixgbe_link_speed speed,
+                                        bool autoneg_wait)
+{
+       s32 status;
+       ixgbe_link_speed force_speed;
+
+       /* Setup internal/external PHY link speed to iXFI (10G), unless
+        * only 1G is auto advertised then setup KX link.
+        */
+       if (speed & IXGBE_LINK_SPEED_10GB_FULL)
+               force_speed = IXGBE_LINK_SPEED_10GB_FULL;
+       else
+               force_speed = IXGBE_LINK_SPEED_1GB_FULL;
+
+       /* If internal link mode is XFI, then setup XFI internal link. */
+       if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) {
+               status = ixgbe_setup_ixfi_x550em(hw, &force_speed);
+
+               if (status)
+                       return status;
+       }
+
+       return hw->phy.ops.setup_link_speed(hw, speed, autoneg_wait);
+}
+
+/** ixgbe_check_link_t_X550em - Determine link and speed status
+  * @hw: pointer to hardware structure
+  * @speed: pointer to link speed
+  * @link_up: true when link is up
+  * @link_up_wait_to_complete: bool used to wait for link up or not
+  *
+  * Check that both the MAC and X557 external PHY have link.
+  **/
+static s32 ixgbe_check_link_t_X550em(struct ixgbe_hw *hw,
+                                    ixgbe_link_speed *speed,
+                                    bool *link_up,
+                                    bool link_up_wait_to_complete)
+{
+       u32 status;
+       u16 autoneg_status;
+
+       if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper)
+               return IXGBE_ERR_CONFIG;
+
+       status = ixgbe_check_mac_link_generic(hw, speed, link_up,
+                                             link_up_wait_to_complete);
+
+       /* If check link fails or MAC link is not up, then return */
+       if (status || !(*link_up))
+               return status;
+
+        /* MAC link is up, so check external PHY link.
+         * Read this twice back to back to indicate current status.
+         */
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS,
+                                     IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+                                     &autoneg_status);
+       if (status)
+               return status;
+
+       /* If external PHY link is not up, then indicate link not up */
+       if (!(autoneg_status & IXGBE_MDIO_AUTO_NEG_LINK_STATUS))
+               *link_up = false;
+
+       return 0;
+}
+
+/** ixgbe_init_mac_link_ops_X550em - init mac link function pointers
+ *  @hw: pointer to hardware structure
+ **/
+static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw)
+{
+       struct ixgbe_mac_info *mac = &hw->mac;
+
+       switch (mac->ops.get_media_type(hw)) {
+       case ixgbe_media_type_fiber:
+               /* CS4227 does not support autoneg, so disable the laser control
+                * functions for SFP+ fiber
+                */
+               mac->ops.disable_tx_laser = NULL;
+               mac->ops.enable_tx_laser = NULL;
+               mac->ops.flap_tx_laser = NULL;
+               mac->ops.setup_link = ixgbe_setup_mac_link_multispeed_fiber;
+               mac->ops.setup_mac_link = ixgbe_setup_mac_link_sfp_x550em;
+               mac->ops.set_rate_select_speed =
+                                       ixgbe_set_soft_rate_select_speed;
+               break;
+       case ixgbe_media_type_copper:
+               mac->ops.setup_link = ixgbe_setup_mac_link_t_X550em;
+               mac->ops.check_link = ixgbe_check_link_t_X550em;
+               break;
+       default:
+               break;
+       }
+}
+
+/** ixgbe_setup_sfp_modules_X550em - Setup SFP module
+ * @hw: pointer to hardware structure
+ */
+static s32 ixgbe_setup_sfp_modules_X550em(struct ixgbe_hw *hw)
+{
+       s32 status;
+       bool linear;
+
+       /* Check if SFP module is supported */
+       status = ixgbe_supported_sfp_modules_X550em(hw, &linear);
+       if (status)
+               return status;
+
+       ixgbe_init_mac_link_ops_X550em(hw);
+       hw->phy.ops.reset = NULL;
+
+       return 0;
+}
+
+/** ixgbe_get_link_capabilities_x550em - Determines link capabilities
+ * @hw: pointer to hardware structure
+ * @speed: pointer to link speed
+ * @autoneg: true when autoneg or autotry is enabled
+ **/
+static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw,
+                                             ixgbe_link_speed *speed,
+                                             bool *autoneg)
+{
+       /* SFP */
+       if (hw->phy.media_type == ixgbe_media_type_fiber) {
+               /* CS4227 SFP must not enable auto-negotiation */
+               *autoneg = false;
+
+               if (hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core0 ||
+                   hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1) {
+                       *speed = IXGBE_LINK_SPEED_1GB_FULL;
+                       return 0;
+               }
+
+               /* Link capabilities are based on SFP */
+               if (hw->phy.multispeed_fiber)
+                       *speed = IXGBE_LINK_SPEED_10GB_FULL |
+                                IXGBE_LINK_SPEED_1GB_FULL;
+               else
+                       *speed = IXGBE_LINK_SPEED_10GB_FULL;
+       } else {
+               *speed = IXGBE_LINK_SPEED_10GB_FULL |
+                        IXGBE_LINK_SPEED_1GB_FULL;
+               *autoneg = true;
+       }
+       return 0;
+}
+
+/**
+ * ixgbe_get_lasi_ext_t_x550em - Determime external Base T PHY interrupt cause
+ * @hw: pointer to hardware structure
+ * @lsc: pointer to boolean flag which indicates whether external Base T
+ *      PHY interrupt is lsc
+ *
+ * Determime if external Base T PHY interrupt cause is high temperature
+ * failure alarm or link status change.
+ *
+ * Return IXGBE_ERR_OVERTEMP if interrupt is high temperature
+ * failure alarm, else return PHY access status.
+ **/
+static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc)
+{
+       u32 status;
+       u16 reg;
+
+       *lsc = false;
+
+       /* Vendor alarm triggered */
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_CHIP_STD_INT_FLAG,
+                                     IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+                                     &reg);
+
+       if (status || !(reg & IXGBE_MDIO_GLOBAL_VEN_ALM_INT_EN))
+               return status;
+
+       /* Vendor Auto-Neg alarm triggered or Global alarm 1 triggered */
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_FLAG,
+                                     IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+                                     &reg);
+
+       if (status || !(reg & (IXGBE_MDIO_GLOBAL_AN_VEN_ALM_INT_EN |
+                               IXGBE_MDIO_GLOBAL_ALARM_1_INT)))
+               return status;
+
+       /* High temperature failure alarm triggered */
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_ALARM_1,
+                                     IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+                                     &reg);
+
+       if (status)
+               return status;
+
+       /* If high temperature failure, then return over temp error and exit */
+       if (reg & IXGBE_MDIO_GLOBAL_ALM_1_HI_TMP_FAIL) {
+               /* power down the PHY in case the PHY FW didn't already */
+               ixgbe_set_copper_phy_power(hw, false);
+               return IXGBE_ERR_OVERTEMP;
+       }
+
+       /* Vendor alarm 2 triggered */
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_CHIP_STD_INT_FLAG,
+                                     IXGBE_MDIO_AUTO_NEG_DEV_TYPE, &reg);
+
+       if (status || !(reg & IXGBE_MDIO_GLOBAL_STD_ALM2_INT))
+               return status;
+
+       /* link connect/disconnect event occurred */
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_TX_ALARM2,
+                                     IXGBE_MDIO_AUTO_NEG_DEV_TYPE, &reg);
+
+       if (status)
+               return status;
+
+       /* Indicate LSC */
+       if (reg & IXGBE_MDIO_AUTO_NEG_VEN_LSC)
+               *lsc = true;
+
+       return 0;
+}
+
+/**
+ * ixgbe_enable_lasi_ext_t_x550em - Enable external Base T PHY interrupts
+ * @hw: pointer to hardware structure
+ *
+ * Enable link status change and temperature failure alarm for the external
+ * Base T PHY
+ *
+ * Returns PHY access status
+ **/
+static s32 ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw)
+{
+       u32 status;
+       u16 reg;
+       bool lsc;
+
+       /* Clear interrupt flags */
+       status = ixgbe_get_lasi_ext_t_x550em(hw, &lsc);
+
+       /* Enable link status change alarm */
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK,
+                                     IXGBE_MDIO_AUTO_NEG_DEV_TYPE, &reg);
+       if (status)
+               return status;
+
+       reg |= IXGBE_MDIO_PMA_TX_VEN_LASI_INT_EN;
+
+       status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK,
+                                      IXGBE_MDIO_AUTO_NEG_DEV_TYPE, reg);
+       if (status)
+               return status;
+
+       /* Enables high temperature failure alarm */
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK,
+                                     IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+                                     &reg);
+       if (status)
+               return status;
+
+       reg |= IXGBE_MDIO_GLOBAL_INT_HI_TEMP_EN;
+
+       status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK,
+                                      IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+                                      reg);
+       if (status)
+               return status;
+
+       /* Enable vendor Auto-Neg alarm and Global Interrupt Mask 1 alarm */
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_MASK,
+                                     IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+                                     &reg);
+       if (status)
+               return status;
+
+       reg |= (IXGBE_MDIO_GLOBAL_AN_VEN_ALM_INT_EN |
+               IXGBE_MDIO_GLOBAL_ALARM_1_INT);
+
+       status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_MASK,
+                                      IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+                                      reg);
+       if (status)
+               return status;
+
+       /* Enable chip-wide vendor alarm */
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_STD_MASK,
+                                     IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+                                     &reg);
+       if (status)
+               return status;
+
+       reg |= IXGBE_MDIO_GLOBAL_VEN_ALM_INT_EN;
+
+       status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_GLOBAL_INT_CHIP_STD_MASK,
+                                      IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+                                      reg);
+
+       return status;
+}
+
+/**
+ * ixgbe_handle_lasi_ext_t_x550em - Handle external Base T PHY interrupt
+ * @hw: pointer to hardware structure
+ *
+ * Handle external Base T PHY interrupt. If high temperature
+ * failure alarm then return error, else if link status change
+ * then setup internal/external PHY link
+ *
+ * Return IXGBE_ERR_OVERTEMP if interrupt is high temperature
+ * failure alarm, else return PHY access status.
+ **/
+static s32 ixgbe_handle_lasi_ext_t_x550em(struct ixgbe_hw *hw)
+{
+       struct ixgbe_phy_info *phy = &hw->phy;
+       bool lsc;
+       u32 status;
+
+       status = ixgbe_get_lasi_ext_t_x550em(hw, &lsc);
        if (status)
                return status;
 
-       reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_OVRRD_EN;
-       reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_CZERO_EN;
-       reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_CPLUS1_OVRRD_EN;
-       reg_val |= IXGBE_KRM_TX_COEFF_CTRL_1_CMINUS1_OVRRD_EN;
-       status = ixgbe_write_iosf_sb_reg_x550(hw,
-                               IXGBE_KRM_TX_COEFF_CTRL_1(hw->bus.lan_id),
-                               IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
-       if (status)
-               return status;
+       if (lsc && phy->ops.setup_internal_link)
+               return phy->ops.setup_internal_link(hw);
+
+       return 0;
+}
+
+/**
+ * ixgbe_setup_kr_speed_x550em - Configure the KR PHY for link speed.
+ * @hw: pointer to hardware structure
+ * @speed: link speed
+ *
+ * Configures the integrated KR PHY.
+ **/
+static s32 ixgbe_setup_kr_speed_x550em(struct ixgbe_hw *hw,
+                                      ixgbe_link_speed speed)
+{
+       s32 status;
+       u32 reg_val;
 
-       /* Toggle port SW reset by AN reset. */
        status = ixgbe_read_iosf_sb_reg_x550(hw,
-                               IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
-                               IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
+                                       IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
+                                       IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
        if (status)
                return status;
 
+       reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_ENABLE;
+       reg_val &= ~(IXGBE_KRM_LINK_CTRL_1_TETH_AN_FEC_REQ |
+                    IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_FEC);
+       reg_val &= ~(IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KR |
+                    IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KX);
+
+       /* Advertise 10G support. */
+       if (speed & IXGBE_LINK_SPEED_10GB_FULL)
+               reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KR;
+
+       /* Advertise 1G support. */
+       if (speed & IXGBE_LINK_SPEED_1GB_FULL)
+               reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KX;
+
+       /* Restart auto-negotiation. */
        reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART;
        status = ixgbe_write_iosf_sb_reg_x550(hw,
-                               IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
-                               IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
+                                       IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
+                                       IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
 
        return status;
 }
@@ -990,85 +1710,82 @@ static s32 ixgbe_setup_kx4_x550em(struct ixgbe_hw *hw)
  **/
 static s32 ixgbe_setup_kr_x550em(struct ixgbe_hw *hw)
 {
-       s32 status;
-       u32 reg_val;
+       return ixgbe_setup_kr_speed_x550em(hw, hw->phy.autoneg_advertised);
+}
 
-       status = ixgbe_read_iosf_sb_reg_x550(hw,
-                                       IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
-                                       IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
-       if (status)
-               return status;
+/** ixgbe_ext_phy_t_x550em_get_link - Get ext phy link status
+ *  @hw: address of hardware structure
+ *  @link_up: address of boolean to indicate link status
+ *
+ *  Returns error code if unable to get link status.
+ **/
+static s32 ixgbe_ext_phy_t_x550em_get_link(struct ixgbe_hw *hw, bool *link_up)
+{
+       u32 ret;
+       u16 autoneg_status;
 
-       reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_ENABLE;
-       reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_FEC_REQ;
-       reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_FEC;
-       reg_val &= ~(IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KR |
-                    IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KX);
+       *link_up = false;
 
-       /* Advertise 10G support. */
-       if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL)
-               reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KR;
+       /* read this twice back to back to indicate current status */
+       ret = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS,
+                                  IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+                                  &autoneg_status);
+       if (ret)
+               return ret;
 
-       /* Advertise 1G support. */
-       if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL)
-               reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KX;
+       ret = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS,
+                                  IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+                                  &autoneg_status);
+       if (ret)
+               return ret;
 
-       /* Restart auto-negotiation. */
-       reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART;
-       status = ixgbe_write_iosf_sb_reg_x550(hw,
-                                       IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
-                                       IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
+       *link_up = !!(autoneg_status & IXGBE_MDIO_AUTO_NEG_LINK_STATUS);
 
-       return status;
+       return 0;
 }
 
-/** ixgbe_setup_internal_phy_x550em - Configure integrated KR PHY
+/** ixgbe_setup_internal_phy_t_x550em - Configure KR PHY to X557 link
  *  @hw: point to hardware structure
  *
- *  Configures the integrated KR PHY to talk to the external PHY. The base
- *  driver will call this function when it gets notification via interrupt from
- *  the external PHY. This function forces the internal PHY into iXFI mode at
- *  the correct speed.
+ *  Configures the link between the integrated KR PHY and the external X557 PHY
+ *  The driver will call this function when it gets a link status change
+ *  interrupt from the X557 PHY. This function configures the link speed
+ *  between the PHYs to match the link speed of the BASE-T link.
  *
- *  A return of a non-zero value indicates an error, and the base driver should
- *  not report link up.
+ * A return of a non-zero value indicates an error, and the base driver should
+ * not report link up.
  **/
-static s32 ixgbe_setup_internal_phy_x550em(struct ixgbe_hw *hw)
+static s32 ixgbe_setup_internal_phy_t_x550em(struct ixgbe_hw *hw)
 {
-       u32 status;
-       u16 lasi, autoneg_status, speed;
        ixgbe_link_speed force_speed;
+       bool link_up;
+       u32 status;
+       u16 speed;
 
-       /* Verify that the external link status has changed */
-       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_XENPAK_LASI_STATUS,
-                                     IXGBE_MDIO_PMA_PMD_DEV_TYPE, &lasi);
+       if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper)
+               return IXGBE_ERR_CONFIG;
+
+       /* If link is not up, then there is no setup necessary so return  */
+       status = ixgbe_ext_phy_t_x550em_get_link(hw, &link_up);
        if (status)
                return status;
 
-       /* If there was no change in link status, we can just exit */
-       if (!(lasi & IXGBE_XENPAK_LASI_LINK_STATUS_ALARM))
+       if (!link_up)
                return 0;
 
-       /* we read this twice back to back to indicate current status */
-       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS,
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_STAT,
                                      IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
-                                     &autoneg_status);
+                                     &speed);
        if (status)
                return status;
 
-       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS,
-                                     IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
-                                     &autoneg_status);
+       /* If link is not still up, then no setup is necessary so return */
+       status = ixgbe_ext_phy_t_x550em_get_link(hw, &link_up);
        if (status)
                return status;
 
-       /* If link is not up return an error indicating treat link as down */
-       if (!(autoneg_status & IXGBE_MDIO_AUTO_NEG_LINK_STATUS))
-               return IXGBE_ERR_INVALID_LINK_SETTINGS;
-
-       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_STAT,
-                                     IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
-                                     &speed);
+       if (!link_up)
+               return 0;
 
        /* clear everything but the speed and duplex bits */
        speed &= IXGBE_MDIO_AUTO_NEG_VENDOR_STATUS_MASK;
@@ -1088,6 +1805,160 @@ static s32 ixgbe_setup_internal_phy_x550em(struct ixgbe_hw *hw)
        return ixgbe_setup_ixfi_x550em(hw, &force_speed);
 }
 
+/** ixgbe_reset_phy_t_X550em - Performs X557 PHY reset and enables LASI
+ *  @hw: pointer to hardware structure
+ **/
+static s32 ixgbe_reset_phy_t_X550em(struct ixgbe_hw *hw)
+{
+       s32 status;
+
+       status = ixgbe_reset_phy_generic(hw);
+
+       if (status)
+               return status;
+
+       /* Configure Link Status Alarm and Temperature Threshold interrupts */
+       return ixgbe_enable_lasi_ext_t_x550em(hw);
+}
+
+/** ixgbe_get_lcd_x550em - Determine lowest common denominator
+ *  @hw: pointer to hardware structure
+ *  @lcd_speed: pointer to lowest common link speed
+ *
+ *  Determine lowest common link speed with link partner.
+ **/
+static s32 ixgbe_get_lcd_t_x550em(struct ixgbe_hw *hw,
+                                 ixgbe_link_speed *lcd_speed)
+{
+       u16 an_lp_status;
+       s32 status;
+       u16 word = hw->eeprom.ctrl_word_3;
+
+       *lcd_speed = IXGBE_LINK_SPEED_UNKNOWN;
+
+       status = hw->phy.ops.read_reg(hw, IXGBE_AUTO_NEG_LP_STATUS,
+                                     IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+                                     &an_lp_status);
+       if (status)
+               return status;
+
+       /* If link partner advertised 1G, return 1G */
+       if (an_lp_status & IXGBE_AUTO_NEG_LP_1000BASE_CAP) {
+               *lcd_speed = IXGBE_LINK_SPEED_1GB_FULL;
+               return status;
+       }
+
+       /* If 10G disabled for LPLU via NVM D10GMP, then return no valid LCD */
+       if ((hw->bus.lan_id && (word & NVM_INIT_CTRL_3_D10GMP_PORT1)) ||
+           (word & NVM_INIT_CTRL_3_D10GMP_PORT0))
+               return status;
+
+       /* Link partner not capable of lower speeds, return 10G */
+       *lcd_speed = IXGBE_LINK_SPEED_10GB_FULL;
+       return status;
+}
+
+/** ixgbe_enter_lplu_x550em - Transition to low power states
+ *  @hw: pointer to hardware structure
+ *
+ *  Configures Low Power Link Up on transition to low power states
+ *  (from D0 to non-D0). Link is required to enter LPLU so avoid resetting
+ *  the X557 PHY immediately prior to entering LPLU.
+ **/
+static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw)
+{
+       u16 an_10g_cntl_reg, autoneg_reg, speed;
+       s32 status;
+       ixgbe_link_speed lcd_speed;
+       u32 save_autoneg;
+       bool link_up;
+
+       /* SW LPLU not required on later HW revisions. */
+       if (IXGBE_FUSES0_REV1 & IXGBE_READ_REG(hw, IXGBE_FUSES0_GROUP(0)))
+               return 0;
+
+       /* If blocked by MNG FW, then don't restart AN */
+       if (ixgbe_check_reset_blocked(hw))
+               return 0;
+
+       status = ixgbe_ext_phy_t_x550em_get_link(hw, &link_up);
+       if (status)
+               return status;
+
+       status = hw->eeprom.ops.read(hw, NVM_INIT_CTRL_3,
+                                    &hw->eeprom.ctrl_word_3);
+       if (status)
+               return status;
+
+       /* If link is down, LPLU disabled in NVM, WoL disabled, or
+        * manageability disabled, then force link down by entering
+        * low power mode.
+        */
+       if (!link_up || !(hw->eeprom.ctrl_word_3 & NVM_INIT_CTRL_3_LPLU) ||
+           !(hw->wol_enabled || ixgbe_mng_present(hw)))
+               return ixgbe_set_copper_phy_power(hw, false);
+
+       /* Determine LCD */
+       status = ixgbe_get_lcd_t_x550em(hw, &lcd_speed);
+       if (status)
+               return status;
+
+       /* If no valid LCD link speed, then force link down and exit. */
+       if (lcd_speed == IXGBE_LINK_SPEED_UNKNOWN)
+               return ixgbe_set_copper_phy_power(hw, false);
+
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_STAT,
+                                     IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+                                     &speed);
+       if (status)
+               return status;
+
+       /* If no link now, speed is invalid so take link down */
+       status = ixgbe_ext_phy_t_x550em_get_link(hw, &link_up);
+       if (status)
+               return ixgbe_set_copper_phy_power(hw, false);
+
+       /* clear everything but the speed bits */
+       speed &= IXGBE_MDIO_AUTO_NEG_VEN_STAT_SPEED_MASK;
+
+       /* If current speed is already LCD, then exit. */
+       if (((speed == IXGBE_MDIO_AUTO_NEG_VENDOR_STATUS_1GB) &&
+            (lcd_speed == IXGBE_LINK_SPEED_1GB_FULL)) ||
+           ((speed == IXGBE_MDIO_AUTO_NEG_VENDOR_STATUS_10GB) &&
+            (lcd_speed == IXGBE_LINK_SPEED_10GB_FULL)))
+               return status;
+
+       /* Clear AN completed indication */
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_TX_ALARM,
+                                     IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+                                     &autoneg_reg);
+       if (status)
+               return status;
+
+       status = hw->phy.ops.read_reg(hw, IXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG,
+                                     IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+                                     &an_10g_cntl_reg);
+       if (status)
+               return status;
+
+       status = hw->phy.ops.read_reg(hw,
+                                     IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG,
+                                     IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+                                     &autoneg_reg);
+       if (status)
+               return status;
+
+       save_autoneg = hw->phy.autoneg_advertised;
+
+       /* Setup link at least common link speed */
+       status = hw->mac.ops.setup_link(hw, lcd_speed, false);
+
+       /* restore autoneg from before setting lplu speed */
+       hw->phy.autoneg_advertised = save_autoneg;
+
+       return status;
+}
+
 /** ixgbe_init_phy_ops_X550em - PHY/SFP specific init
  *  @hw: pointer to hardware structure
  *
@@ -1098,25 +1969,32 @@ static s32 ixgbe_setup_internal_phy_x550em(struct ixgbe_hw *hw)
 static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
 {
        struct ixgbe_phy_info *phy = &hw->phy;
+       ixgbe_link_speed speed;
        s32 ret_val;
-       u32 esdp;
 
-       if (hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP) {
-               esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
-               phy->phy_semaphore_mask = IXGBE_GSSR_SHARED_I2C_SM;
+       hw->mac.ops.set_lan_id(hw);
 
-               if (hw->bus.lan_id) {
-                       esdp &= ~(IXGBE_ESDP_SDP1_NATIVE | IXGBE_ESDP_SDP1);
-                       esdp |= IXGBE_ESDP_SDP1_DIR;
+       if (hw->mac.ops.get_media_type(hw) == ixgbe_media_type_fiber) {
+               phy->phy_semaphore_mask = IXGBE_GSSR_SHARED_I2C_SM;
+               ixgbe_setup_mux_ctl(hw);
+
+               /* Save NW management interface connected on board. This is used
+                * to determine internal PHY mode.
+                */
+               phy->nw_mng_if_sel = IXGBE_READ_REG(hw, IXGBE_NW_MNG_IF_SEL);
+
+               /* If internal PHY mode is KR, then initialize KR link */
+               if (phy->nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE) {
+                       speed = IXGBE_LINK_SPEED_10GB_FULL |
+                               IXGBE_LINK_SPEED_1GB_FULL;
+                       ret_val = ixgbe_setup_kr_speed_x550em(hw, speed);
                }
-               esdp &= ~(IXGBE_ESDP_SDP0_NATIVE | IXGBE_ESDP_SDP0_DIR);
-               IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
        }
 
        /* Identify the PHY or SFP module */
        ret_val = phy->ops.identify(hw);
 
-       /* Setup function pointers based on detected SFP module and speeds */
+       /* Setup function pointers based on detected hardware */
        ixgbe_init_mac_link_ops_X550em(hw);
        if (phy->sfp_type != ixgbe_sfp_type_unknown)
                phy->ops.reset = NULL;
@@ -1134,11 +2012,35 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
                phy->ops.write_reg = ixgbe_write_phy_reg_x550em;
                break;
        case ixgbe_phy_x550em_ext_t:
-               phy->ops.setup_internal_link = ixgbe_setup_internal_phy_x550em;
+               /* Save NW management interface connected on board. This is used
+                * to determine internal PHY mode
+                */
+               phy->nw_mng_if_sel = IXGBE_READ_REG(hw, IXGBE_NW_MNG_IF_SEL);
+
+               /* If internal link mode is XFI, then setup iXFI internal link,
+                * else setup KR now.
+                */
+               if (!(phy->nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) {
+                       phy->ops.setup_internal_link =
+                                       ixgbe_setup_internal_phy_t_x550em;
+               } else {
+                       speed = IXGBE_LINK_SPEED_10GB_FULL |
+                               IXGBE_LINK_SPEED_1GB_FULL;
+                       ret_val = ixgbe_setup_kr_speed_x550em(hw, speed);
+               }
+
+               /* setup SW LPLU only for first revision */
+               if (!(IXGBE_FUSES0_REV1 & IXGBE_READ_REG(hw,
+                                                       IXGBE_FUSES0_GROUP(0))))
+                       phy->ops.enter_lplu = ixgbe_enter_lplu_t_x550em;
+
+               phy->ops.handle_lasi = ixgbe_handle_lasi_ext_t_x550em;
+               phy->ops.reset = ixgbe_reset_phy_t_X550em;
                break;
        default:
                break;
        }
+
        return ret_val;
 }
 
@@ -1177,67 +2079,37 @@ static enum ixgbe_media_type ixgbe_get_media_type_X550em(struct ixgbe_hw *hw)
  **/
 static s32 ixgbe_init_ext_t_x550em(struct ixgbe_hw *hw)
 {
-       u32 status;
+       s32 status;
        u16 reg;
-       u32 retries = 2;
-
-       do {
-               /* decrement retries counter and exit if we hit 0 */
-               if (retries < 1) {
-                       hw_dbg(hw, "External PHY not yet finished resetting.");
-                       return IXGBE_ERR_PHY;
-               }
-               retries--;
-
-               status = hw->phy.ops.read_reg(hw,
-                                             IXGBE_MDIO_TX_VENDOR_ALARMS_3,
-                                             IXGBE_MDIO_PMA_PMD_DEV_TYPE,
-                                             &reg);
-               if (status)
-                       return status;
-
-               /* Verify PHY FW reset has completed */
-       } while ((reg & IXGBE_MDIO_TX_VENDOR_ALARMS_3_RST_MASK) != 1);
-
-       /* Set port to low power mode */
-       status = hw->phy.ops.read_reg(hw,
-                                     IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL,
-                                     IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
-                                     &reg);
-       if (status)
-               return status;
 
-       /* Enable the transmitter */
        status = hw->phy.ops.read_reg(hw,
-                                     IXGBE_MDIO_PMD_STD_TX_DISABLE_CNTR,
+                                     IXGBE_MDIO_TX_VENDOR_ALARMS_3,
                                      IXGBE_MDIO_PMA_PMD_DEV_TYPE,
                                      &reg);
        if (status)
                return status;
 
-       reg &= ~IXGBE_MDIO_PMD_GLOBAL_TX_DISABLE;
-
-       status = hw->phy.ops.write_reg(hw,
-                                      IXGBE_MDIO_PMD_STD_TX_DISABLE_CNTR,
-                                      IXGBE_MDIO_PMA_PMD_DEV_TYPE,
-                                      reg);
-       if (status)
-               return status;
+       /* If PHY FW reset completed bit is set then this is the first
+        * SW instance after a power on so the PHY FW must be un-stalled.
+        */
+       if (reg & IXGBE_MDIO_TX_VENDOR_ALARMS_3_RST_MASK) {
+               status = hw->phy.ops.read_reg(hw,
+                                       IXGBE_MDIO_GLOBAL_RES_PR_10,
+                                       IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+                                       &reg);
+               if (status)
+                       return status;
 
-       /* Un-stall the PHY FW */
-       status = hw->phy.ops.read_reg(hw,
-                                     IXGBE_MDIO_GLOBAL_RES_PR_10,
-                                     IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
-                                     &reg);
-       if (status)
-               return status;
+               reg &= ~IXGBE_MDIO_POWER_UP_STALL;
 
-       reg &= ~IXGBE_MDIO_POWER_UP_STALL;
+               status = hw->phy.ops.write_reg(hw,
+                                       IXGBE_MDIO_GLOBAL_RES_PR_10,
+                                       IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+                                       reg);
+               if (status)
+                       return status;
+       }
 
-       status = hw->phy.ops.write_reg(hw,
-                                      IXGBE_MDIO_GLOBAL_RES_PR_10,
-                                      IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
-                                      reg);
        return status;
 }
 
@@ -1254,6 +2126,7 @@ static s32 ixgbe_reset_hw_X550em(struct ixgbe_hw *hw)
        s32 status;
        u32 ctrl = 0;
        u32 i;
+       u32 hlreg0;
        bool link_up = false;
 
        /* Call adapter stop to disable Tx/Rx and clear interrupts */
@@ -1338,6 +2211,15 @@ mac_reset_top:
        hw->mac.num_rar_entries = 128;
        hw->mac.ops.init_rx_addrs(hw);
 
+       if (hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T) {
+               hlreg0 = IXGBE_READ_REG(hw, IXGBE_HLREG0);
+               hlreg0 &= ~IXGBE_HLREG0_MDCSPD;
+               IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg0);
+       }
+
+       if (hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP)
+               ixgbe_setup_mux_ctl(hw);
+
        return status;
 }
 
@@ -1390,6 +2272,62 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw,
        IXGBE_WRITE_REG(hw, IXGBE_PFFLPH, (u32)(pfflp >> 32));
 }
 
+/**
+ * ixgbe_set_mux - Set mux for port 1 access with CS4227
+ * @hw: pointer to hardware structure
+ * @state: set mux if 1, clear if 0
+ */
+static void ixgbe_set_mux(struct ixgbe_hw *hw, u8 state)
+{
+       u32 esdp;
+
+       if (!hw->bus.lan_id)
+               return;
+       esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
+       if (state)
+               esdp |= IXGBE_ESDP_SDP1;
+       else
+               esdp &= ~IXGBE_ESDP_SDP1;
+       IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
+       IXGBE_WRITE_FLUSH(hw);
+}
+
+/**
+ * ixgbe_acquire_swfw_sync_X550em - Acquire SWFW semaphore
+ * @hw: pointer to hardware structure
+ * @mask: Mask to specify which semaphore to acquire
+ *
+ * Acquires the SWFW semaphore and sets the I2C MUX
+ */
+static s32 ixgbe_acquire_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask)
+{
+       s32 status;
+
+       status = ixgbe_acquire_swfw_sync_X540(hw, mask);
+       if (status)
+               return status;
+
+       if (mask & IXGBE_GSSR_I2C_MASK)
+               ixgbe_set_mux(hw, 1);
+
+       return 0;
+}
+
+/**
+ * ixgbe_release_swfw_sync_X550em - Release SWFW semaphore
+ * @hw: pointer to hardware structure
+ * @mask: Mask to specify which semaphore to release
+ *
+ * Releases the SWFW semaphore and sets the I2C MUX
+ */
+static void ixgbe_release_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask)
+{
+       if (mask & IXGBE_GSSR_I2C_MASK)
+               ixgbe_set_mux(hw, 0);
+
+       ixgbe_release_swfw_sync_X540(hw, mask);
+}
+
 #define X550_COMMON_MAC \
        .init_hw                        = &ixgbe_init_hw_generic, \
        .start_hw                       = &ixgbe_start_hw_X540, \
@@ -1398,7 +2336,6 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw,
        .get_mac_addr                   = &ixgbe_get_mac_addr_generic, \
        .get_device_caps                = &ixgbe_get_device_caps_generic, \
        .stop_adapter                   = &ixgbe_stop_adapter_generic, \
-       .get_bus_info                   = &ixgbe_get_bus_info_generic, \
        .set_lan_id                     = &ixgbe_set_lan_id_multi_port_pcie, \
        .read_analog_reg8               = NULL, \
        .write_analog_reg8              = NULL, \
@@ -1428,8 +2365,6 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw,
                                &ixgbe_set_source_address_pruning_X550, \
        .set_ethertype_anti_spoofing    = \
                                &ixgbe_set_ethertype_anti_spoofing_X550, \
-       .acquire_swfw_sync              = &ixgbe_acquire_swfw_sync_X540, \
-       .release_swfw_sync              = &ixgbe_release_swfw_sync_X540, \
        .disable_rx_buff                = &ixgbe_disable_rx_buff_generic, \
        .enable_rx_buff                 = &ixgbe_enable_rx_buff_generic, \
        .get_thermal_sensor_data        = NULL, \
@@ -1447,7 +2382,10 @@ static struct ixgbe_mac_operations mac_ops_X550 = {
        .get_wwn_prefix         = &ixgbe_get_wwn_prefix_generic,
        .setup_link             = &ixgbe_setup_mac_link_X540,
        .get_link_capabilities  = &ixgbe_get_copper_link_capabilities_generic,
+       .get_bus_info           = &ixgbe_get_bus_info_generic,
        .setup_sfp              = NULL,
+       .acquire_swfw_sync      = &ixgbe_acquire_swfw_sync_X540,
+       .release_swfw_sync      = &ixgbe_release_swfw_sync_X540,
 };
 
 static struct ixgbe_mac_operations mac_ops_X550EM_x = {
@@ -1458,8 +2396,10 @@ static struct ixgbe_mac_operations mac_ops_X550EM_x = {
        .get_wwn_prefix         = NULL,
        .setup_link             = NULL, /* defined later */
        .get_link_capabilities  = &ixgbe_get_link_capabilities_X550em,
+       .get_bus_info           = &ixgbe_get_bus_info_X550em,
        .setup_sfp              = ixgbe_setup_sfp_modules_X550em,
-
+       .acquire_swfw_sync      = &ixgbe_acquire_swfw_sync_X550em,
+       .release_swfw_sync      = &ixgbe_release_swfw_sync_X550em,
 };
 
 #define X550_COMMON_EEP \
@@ -1490,6 +2430,10 @@ static struct ixgbe_eeprom_operations eeprom_ops_X550EM_x = {
        .read_i2c_sff8472       = &ixgbe_read_i2c_sff8472_generic, \
        .read_i2c_eeprom        = &ixgbe_read_i2c_eeprom_generic, \
        .write_i2c_eeprom       = &ixgbe_write_i2c_eeprom_generic, \
+       .read_reg               = &ixgbe_read_phy_reg_generic, \
+       .write_reg              = &ixgbe_write_phy_reg_generic, \
+       .setup_link             = &ixgbe_setup_phy_link_generic, \
+       .set_phy_power          = NULL, \
        .check_overtemp         = &ixgbe_tn_check_overtemp, \
        .get_firmware_version   = &ixgbe_get_phy_firmware_version_generic,
 
@@ -1497,20 +2441,25 @@ static struct ixgbe_phy_operations phy_ops_X550 = {
        X550_COMMON_PHY
        .init                   = NULL,
        .identify               = &ixgbe_identify_phy_generic,
-       .read_reg               = &ixgbe_read_phy_reg_generic,
-       .write_reg              = &ixgbe_write_phy_reg_generic,
-       .setup_link             = &ixgbe_setup_phy_link_generic,
-       .read_i2c_combined      = &ixgbe_read_i2c_combined_generic,
-       .write_i2c_combined     = &ixgbe_write_i2c_combined_generic,
 };
 
 static struct ixgbe_phy_operations phy_ops_X550EM_x = {
        X550_COMMON_PHY
        .init                   = &ixgbe_init_phy_ops_X550em,
        .identify               = &ixgbe_identify_phy_x550em,
-       .read_reg               = NULL, /* defined later */
-       .write_reg              = NULL, /* defined later */
-       .setup_link             = NULL, /* defined later */
+       .read_i2c_combined      = &ixgbe_read_i2c_combined_generic,
+       .write_i2c_combined     = &ixgbe_write_i2c_combined_generic,
+       .read_i2c_combined_unlocked = &ixgbe_read_i2c_combined_generic_unlocked,
+       .write_i2c_combined_unlocked =
+                                    &ixgbe_write_i2c_combined_generic_unlocked,
+};
+
+static const u32 ixgbe_mvals_X550[IXGBE_MVALS_IDX_LIMIT] = {
+       IXGBE_MVALS_INIT(X550)
+};
+
+static const u32 ixgbe_mvals_X550EM_x[IXGBE_MVALS_IDX_LIMIT] = {
+       IXGBE_MVALS_INIT(X550EM_x)
 };
 
 struct ixgbe_info ixgbe_X550_info = {
@@ -1520,13 +2469,15 @@ struct ixgbe_info ixgbe_X550_info = {
        .eeprom_ops             = &eeprom_ops_X550,
        .phy_ops                = &phy_ops_X550,
        .mbx_ops                = &mbx_ops_generic,
+       .mvals                  = ixgbe_mvals_X550,
 };
 
 struct ixgbe_info ixgbe_X550EM_x_info = {
        .mac                    = ixgbe_mac_X550EM_x,
-       .get_invariants         = &ixgbe_get_invariants_X540,
+       .get_invariants         = &ixgbe_get_invariants_X550_x,
        .mac_ops                = &mac_ops_X550EM_x,
        .eeprom_ops             = &eeprom_ops_X550EM_x,
        .phy_ops                = &phy_ops_X550EM_x,
        .mbx_ops                = &mbx_ops_generic,
+       .mvals                  = ixgbe_mvals_X550EM_x,
 };