ixgbe/base: rework X550em PHY setup function
[dpdk.git] / drivers / net / ixgbe / base / ixgbe_x550.c
index 4664583..bc0ca53 100644 (file)
@@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE.
 #include "ixgbe_common.h"
 #include "ixgbe_phy.h"
 
+
 /**
  *  ixgbe_init_ops_X550 - Inits func ptrs and MAC type
  *  @hw: pointer to hardware structure
@@ -81,6 +82,204 @@ s32 ixgbe_init_ops_X550(struct ixgbe_hw *hw)
        return ret_val;
 }
 
+/**
+ * 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 ixgbe_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 ixgbe_write_i2c_combined_unlocked(hw, IXGBE_CS4227, reg, value);
+}
+
+/**
+ * ixgbe_get_cs4227_status - Return CS4227 status
+ * @hw: pointer to hardware structure
+ *
+ * Returns error if CS4227 not successfully initialized
+ **/
+STATIC s32 ixgbe_get_cs4227_status(struct ixgbe_hw *hw)
+{
+       s32 status;
+       u16 value = 0;
+       u8 retry;
+
+       for (retry = 0; retry < IXGBE_CS4227_RETRIES; ++retry) {
+               status = ixgbe_read_cs4227(hw, IXGBE_CS4227_GLOBAL_ID_LSB,
+                                          &value);
+               if (status != IXGBE_SUCCESS)
+                       return status;
+               if (value == IXGBE_CS4227_GLOBAL_ID_VALUE)
+                       break;
+               msec_delay(IXGBE_CS4227_CHECK_DELAY);
+       }
+       if (value != IXGBE_CS4227_GLOBAL_ID_VALUE)
+               return IXGBE_ERR_PHY;
+
+       status = ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH,
+                                   IXGBE_CS4227_SCRATCH_VALUE);
+       if (status != IXGBE_SUCCESS)
+               return status;
+       status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value);
+       if (status != IXGBE_SUCCESS)
+               return status;
+       if (value != IXGBE_CS4227_SCRATCH_VALUE)
+               return IXGBE_ERR_PHY;
+       return IXGBE_SUCCESS;
+}
+
+/**
+ * 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_unlocked(hw, reg, IXGBE_PE, value);
+       if (status != IXGBE_SUCCESS)
+               ERROR_REPORT2(IXGBE_ERROR_CAUTION,
+                             "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_unlocked(hw, reg, IXGBE_PE, value);
+       if (status != IXGBE_SUCCESS)
+               ERROR_REPORT2(IXGBE_ERROR_CAUTION,
+                             "port expander access failed with %d\n", status);
+       return status;
+}
+
+/**
+ * ixgbe_reset_cs4227 - Reset CS4227 using port expander
+ * @hw: pointer to hardware structure
+ *
+ * Returns error code
+ **/
+STATIC s32 ixgbe_reset_cs4227(struct ixgbe_hw *hw)
+{
+       s32 status;
+       u8 reg;
+
+       status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, &reg);
+       if (status != IXGBE_SUCCESS)
+               return status;
+       reg |= IXGBE_PE_BIT1;
+       status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg);
+       if (status != IXGBE_SUCCESS)
+               return status;
+
+       status = ixgbe_read_pe(hw, IXGBE_PE_CONFIG, &reg);
+       if (status != IXGBE_SUCCESS)
+               return status;
+       reg &= ~IXGBE_PE_BIT1;
+       status = ixgbe_write_pe(hw, IXGBE_PE_CONFIG, reg);
+       if (status != IXGBE_SUCCESS)
+               return status;
+
+       status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, &reg);
+       if (status != IXGBE_SUCCESS)
+               return status;
+       reg &= ~IXGBE_PE_BIT1;
+       status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg);
+       if (status != IXGBE_SUCCESS)
+               return status;
+
+       usec_delay(IXGBE_CS4227_RESET_HOLD);
+
+       status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, &reg);
+       if (status != IXGBE_SUCCESS)
+               return status;
+       reg |= IXGBE_PE_BIT1;
+       status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg);
+       if (status != IXGBE_SUCCESS)
+               return status;
+
+       msec_delay(IXGBE_CS4227_RESET_DELAY);
+
+       return IXGBE_SUCCESS;
+}
+
+/**
+ * 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;
+       u8 retry;
+
+       for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) {
+               status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask);
+               if (status != IXGBE_SUCCESS) {
+                       ERROR_REPORT2(IXGBE_ERROR_CAUTION,
+                                     "semaphore failed with %d\n", status);
+                       return;
+               }
+               status = ixgbe_get_cs4227_status(hw);
+               if (status == IXGBE_SUCCESS) {
+                       hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+                       msec_delay(hw->eeprom.semaphore_delay);
+                       return;
+               }
+               ixgbe_reset_cs4227(hw);
+               hw->mac.ops.release_swfw_sync(hw, swfw_mask);
+               msec_delay(hw->eeprom.semaphore_delay);
+       }
+       ERROR_REPORT2(IXGBE_ERROR_CAUTION,
+                     "Unable to initialize CS4227, err=%d\n", status);
+}
+
+/**
+ * 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_identify_phy_x550em - Get PHY type based on device id
  * @hw: pointer to hardware structure
@@ -89,19 +288,12 @@ s32 ixgbe_init_ops_X550(struct ixgbe_hw *hw)
  */
 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);
                break;
@@ -173,6 +365,10 @@ s32 ixgbe_init_ops_X550EM(struct ixgbe_hw *hw)
        mac->ops.disable_sec_rx_path = NULL;
        mac->ops.enable_sec_rx_path = NULL;
 
+       /* AUTOC register is not present in x550EM. */
+       mac->ops.prot_autoc_read = NULL;
+       mac->ops.prot_autoc_write = NULL;
+
        /* X550EM bus type is internal*/
        hw->bus.type = ixgbe_bus_type_internal;
        mac->ops.get_bus_info = ixgbe_get_bus_info_X550em;
@@ -186,6 +382,10 @@ s32 ixgbe_init_ops_X550EM(struct ixgbe_hw *hw)
        mac->ops.get_supported_physical_layer =
                                    ixgbe_get_supported_physical_layer_X550em;
 
+               mac->ops.setup_fc = ixgbe_setup_fc_X550em;
+       mac->ops.acquire_swfw_sync = ixgbe_acquire_swfw_sync_X550em;
+       mac->ops.release_swfw_sync = ixgbe_release_swfw_sync_X550em;
+
        /* PHY */
        phy->ops.init = ixgbe_init_phy_ops_X550em;
        phy->ops.identify = ixgbe_identify_phy_x550em;
@@ -854,10 +1054,14 @@ void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw)
        /* CS4227 does not support autoneg, so disable the laser control
         * functions for SFP+ fiber
         */
-        if (hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP) {
+        if (hw->mac.ops.get_media_type(hw) == ixgbe_media_type_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;
         }
 }
 
@@ -915,20 +1119,12 @@ s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
 {
        struct ixgbe_phy_info *phy = &hw->phy;
        s32 ret_val;
-       u32 esdp;
 
        DEBUGFUNC("ixgbe_init_phy_ops_X550em");
 
-       if (hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP) {
-               esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
+       if (hw->mac.ops.get_media_type(hw) == ixgbe_media_type_fiber) {
                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);
        }
 
        /* Identify the PHY or SFP module */
@@ -954,7 +1150,9 @@ 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;
+               phy->ops.setup_internal_link =
+                                        ixgbe_setup_internal_phy_t_x550em;
+               phy->ops.enter_lplu = ixgbe_enter_lplu_t_x550em;
                break;
        default:
                break;
@@ -972,6 +1170,7 @@ s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
  */
 s32 ixgbe_reset_hw_X550em(struct ixgbe_hw *hw)
 {
+       struct ixgbe_hic_hdr fw_cmd;
        ixgbe_link_speed link_speed;
        s32 status;
        u32 ctrl = 0;
@@ -980,6 +1179,22 @@ s32 ixgbe_reset_hw_X550em(struct ixgbe_hw *hw)
 
        DEBUGFUNC("ixgbe_reset_hw_X550em");
 
+       fw_cmd.cmd = FW_PHY_MGMT_REQ_CMD;
+       fw_cmd.buf_len = 0;
+       fw_cmd.cmd_or_resp.cmd_resv = 0;
+       fw_cmd.checksum = FW_DEFAULT_CHECKSUM;
+       status = ixgbe_host_interface_command(hw, (u32 *)&fw_cmd,
+                                             sizeof(fw_cmd),
+                                             IXGBE_HI_PHY_MGMT_REQ_TIMEOUT,
+                                             true);
+       if (status)
+               ERROR_REPORT2(IXGBE_ERROR_CAUTION,
+                             "PHY mgmt command failed with %d\n", status);
+       else if (fw_cmd.cmd_or_resp.ret_status != FW_CEM_RESP_STATUS_SUCCESS)
+               ERROR_REPORT2(IXGBE_ERROR_CAUTION,
+                             "PHY mgmt command returned %d\n",
+                             fw_cmd.cmd_or_resp.ret_status);
+
        /* Call adapter stop to disable Tx/Rx and clear interrupts */
        status = hw->mac.ops.stop_adapter(hw);
        if (status != IXGBE_SUCCESS)
@@ -1068,6 +1283,9 @@ mac_reset_top:
        hw->mac.ops.init_rx_addrs(hw);
 
 
+       if (hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP)
+               ixgbe_setup_mux_ctl(hw);
+
        return status;
 }
 
@@ -1079,84 +1297,37 @@ s32 ixgbe_init_ext_t_x550em(struct ixgbe_hw *hw)
 {
        u32 status;
        u16 reg;
-       u32 retries = 1;
-
-       /* TODO: The number of attempts and delay between attempts is undefined */
-       do {
-               /* decrement retries counter and exit if we hit 0 */
-               if (retries < 1) {
-                       ERROR_REPORT1(IXGBE_ERROR_INVALID_STATE,
-                                     "External PHY not yet finished resetting.");
-                       return IXGBE_ERR_PHY;
-               }
-               retries--;
 
-               usec_delay(0);
-
-               status = hw->phy.ops.read_reg(hw,
-                                             IXGBE_MDIO_TX_VENDOR_ALARMS_3,
-                                             IXGBE_MDIO_PMA_PMD_DEV_TYPE,
-                                             &reg);
-
-               if (status != IXGBE_SUCCESS)
-                       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 != IXGBE_SUCCESS)
-               return status;
-
-       reg |= IXGBE_MDIO_PHY_SET_LOW_POWER_MODE;
-
-       status = hw->phy.ops.write_reg(hw,
-                                      IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL,
-                                      IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
-                                      reg);
-
-       if (status != IXGBE_SUCCESS)
-               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 != IXGBE_SUCCESS)
                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 != IXGBE_SUCCESS)
-               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);
 
-       /* 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 != IXGBE_SUCCESS)
+                       return status;
 
-       if (status != IXGBE_SUCCESS)
-               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);
 
-       status = hw->phy.ops.write_reg(hw,
-                                      IXGBE_MDIO_GLOBAL_RES_PR_10,
-                                      IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
-                                      reg);
+               if (status != IXGBE_SUCCESS)
+                       return status;
+       }
 
        return status;
 }
@@ -1179,8 +1350,8 @@ s32 ixgbe_setup_kr_x550em(struct ixgbe_hw *hw)
                return 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_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);
 
@@ -1352,35 +1523,42 @@ STATIC s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed)
 }
 
 /**
- * ixgbe_setup_internal_phy_x550em - Configure integrated KR PHY
+ *  ixgbe_setup_mac_link_sfp_x550em - Configure the KR PHY for SFP.
+ *  @hw: pointer to hardware structure
+ *
+ *  Configures the integrated KR PHY for SFP support.
+ **/
+s32 ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw,
+                                   ixgbe_link_speed speed,
+                                   bool autoneg_wait_to_complete)
+{
+       UNREFERENCED_1PARAMETER(autoneg_wait_to_complete);
+
+       return ixgbe_setup_ixfi_x550em(hw, &speed);
+}
+
+/**
+ * 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.
  */
-s32 ixgbe_setup_internal_phy_x550em(struct ixgbe_hw *hw)
+s32 ixgbe_setup_internal_phy_t_x550em(struct ixgbe_hw *hw)
 {
        u32 status;
-       u16 lasi, autoneg_status, speed;
+       u16 autoneg_status, speed;
        ixgbe_link_speed force_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 (status != IXGBE_SUCCESS)
-               return status;
+       if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper)
+               return IXGBE_ERR_CONFIG;
 
-       /* If there was no change in link status, we can just exit */
-       if (!(lasi & IXGBE_XENPAK_LASI_LINK_STATUS_ALARM))
-               return IXGBE_SUCCESS;
-
-       /* we read this twice back to back to indicate current status */
+       /* 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);
@@ -1393,9 +1571,9 @@ s32 ixgbe_setup_internal_phy_x550em(struct ixgbe_hw *hw)
        if (status != IXGBE_SUCCESS)
                return status;
 
-       /* If link is not up return an error indicating treat link as down */
+       /* If link is not up, then there is no setup necessary so return  */
        if (!(autoneg_status & IXGBE_MDIO_AUTO_NEG_LINK_STATUS))
-               return IXGBE_ERR_INVALID_LINK_SETTINGS;
+               return IXGBE_SUCCESS;
 
        status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_VENDOR_STAT,
                                      IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
@@ -2111,3 +2289,317 @@ void ixgbe_disable_rx_x550(struct ixgbe_hw *hw)
                }
        }
 }
+
+/**
+ * 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.
+ **/
+s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw)
+{
+       u16 autoneg_status, an_10g_cntl_reg, autoneg_reg, speed;
+       s32 status;
+       ixgbe_link_speed lcd_speed;
+
+       /* If blocked by MNG FW, then don't restart AN */
+       if (ixgbe_check_reset_blocked(hw))
+               return IXGBE_SUCCESS;
+
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS,
+                                     IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+                                     &autoneg_status);
+
+       if (status != IXGBE_SUCCESS)
+               return status;
+
+       status = ixgbe_read_eeprom(hw, NVM_INIT_CTRL_3, &hw->eeprom.ctrl_word_3);
+
+       if (status != IXGBE_SUCCESS)
+               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 (!(autoneg_status & IXGBE_MDIO_AUTO_NEG_LINK_STATUS) ||
+           !(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 != IXGBE_SUCCESS)
+               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 != IXGBE_SUCCESS)
+               return status;
+
+       /* 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_status);
+
+       if (status != IXGBE_SUCCESS)
+               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 != IXGBE_SUCCESS)
+               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 != IXGBE_SUCCESS)
+               return status;
+
+       /* Set AN advertizement to only include LCD  */
+       if (lcd_speed == IXGBE_LINK_SPEED_1GB_FULL) {
+               an_10g_cntl_reg &= ~IXGBE_MII_10GBASE_T_ADVERTISE;
+               autoneg_reg |= IXGBE_MII_1GBASE_T_ADVERTISE;
+       }
+
+       if (lcd_speed == IXGBE_LINK_SPEED_10GB_FULL) {
+               an_10g_cntl_reg |= IXGBE_MII_10GBASE_T_ADVERTISE;
+               autoneg_reg &= ~IXGBE_MII_1GBASE_T_ADVERTISE;
+       }
+
+       status = hw->phy.ops.write_reg(hw, IXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG,
+                             IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+                             an_10g_cntl_reg);
+
+       if (status != IXGBE_SUCCESS)
+               return status;
+
+       status = hw->phy.ops.write_reg(hw,
+                             IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG,
+                             IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+                             autoneg_reg);
+
+       if (status != IXGBE_SUCCESS)
+               return status;
+
+       /* Restart PHY auto-negotiation. */
+       status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_CONTROL,
+                            IXGBE_MDIO_AUTO_NEG_DEV_TYPE, &autoneg_reg);
+
+       if (status != IXGBE_SUCCESS)
+               return status;
+
+       autoneg_reg |= IXGBE_MII_RESTART;
+
+       status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_AUTO_NEG_CONTROL,
+                             IXGBE_MDIO_AUTO_NEG_DEV_TYPE, autoneg_reg);
+
+       if (status != IXGBE_SUCCESS)
+               return status;
+
+       status = ixgbe_setup_ixfi_x550em(hw, &lcd_speed);
+
+       return status;
+}
+
+/**
+ * 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.
+ **/
+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 != IXGBE_SUCCESS)
+               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_setup_fc_X550em - Set up flow control
+ *  @hw: pointer to hardware structure
+ *
+ *  Called at init time to set up flow control.
+ **/
+s32 ixgbe_setup_fc_X550em(struct ixgbe_hw *hw)
+{
+       s32 ret_val = IXGBE_SUCCESS;
+       u32 pause, asm_dir, reg_val;
+
+       DEBUGFUNC("ixgbe_setup_fc_X550em");
+
+       /* Validate the requested mode */
+       if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) {
+               ERROR_REPORT1(IXGBE_ERROR_UNSUPPORTED,
+                       "ixgbe_fc_rx_pause not valid in strict IEEE mode\n");
+               ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
+               goto out;
+       }
+
+       /* 10gig parts do not have a word in the EEPROM to determine the
+        * default flow control setting, so we explicitly set it to full.
+        */
+       if (hw->fc.requested_mode == ixgbe_fc_default)
+               hw->fc.requested_mode = ixgbe_fc_full;
+
+       /* Determine PAUSE and ASM_DIR bits. */
+       switch (hw->fc.requested_mode) {
+       case ixgbe_fc_none:
+               pause = 0;
+               asm_dir = 0;
+               break;
+       case ixgbe_fc_tx_pause:
+               pause = 0;
+               asm_dir = 1;
+               break;
+       case ixgbe_fc_rx_pause:
+               /* Rx Flow control is enabled and Tx Flow control is
+                * disabled by software override. Since there really
+                * isn't a way to advertise that we are capable of RX
+                * Pause ONLY, we will advertise that we support both
+                * symmetric and asymmetric Rx PAUSE, as such we fall
+                * through to the fc_full statement.  Later, we will
+                * disable the adapter's ability to send PAUSE frames.
+                */
+       case ixgbe_fc_full:
+               pause = 1;
+               asm_dir = 1;
+               break;
+       default:
+               ERROR_REPORT1(IXGBE_ERROR_ARGUMENT,
+                       "Flow control param set incorrectly\n");
+               ret_val = IXGBE_ERR_CONFIG;
+               goto out;
+       }
+
+       if (hw->phy.media_type == ixgbe_media_type_backplane) {
+               ret_val = ixgbe_read_iosf_sb_reg_x550(hw,
+                       IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id),
+                       IXGBE_SB_IOSF_TARGET_KR_PHY, &reg_val);
+               if (ret_val != IXGBE_SUCCESS)
+                       goto out;
+               reg_val &= ~(IXGBE_KRM_AN_CNTL_1_SYM_PAUSE |
+                       IXGBE_KRM_AN_CNTL_1_ASM_PAUSE);
+               if (pause)
+                       reg_val |= IXGBE_KRM_AN_CNTL_1_SYM_PAUSE;
+               if (asm_dir)
+                       reg_val |= IXGBE_KRM_AN_CNTL_1_ASM_PAUSE;
+               ret_val = ixgbe_write_iosf_sb_reg_x550(hw,
+                       IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id),
+                       IXGBE_SB_IOSF_TARGET_KR_PHY, reg_val);
+
+               /* Not all devices fully support AN. */
+               if (hw->device_id == IXGBE_DEV_ID_X550EM_X_KR)
+                       hw->fc.disable_fc_autoneg = true;
+       }
+
+out:
+       return ret_val;
+}
+
+/**
+ * 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
+ **/
+s32 ixgbe_acquire_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask)
+{
+       s32 status;
+
+       DEBUGFUNC("ixgbe_acquire_swfw_sync_X550em");
+
+       status = ixgbe_acquire_swfw_sync_X540(hw, mask);
+       if (status)
+               return status;
+
+       if (mask & IXGBE_GSSR_I2C_MASK)
+               ixgbe_set_mux(hw, 1);
+
+       return IXGBE_SUCCESS;
+}
+
+/**
+ *  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
+ **/
+void ixgbe_release_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask)
+{
+       DEBUGFUNC("ixgbe_release_swfw_sync_X550em");
+
+       if (mask & IXGBE_GSSR_I2C_MASK)
+               ixgbe_set_mux(hw, 0);
+
+       ixgbe_release_swfw_sync_X540(hw, mask);
+}