ixgbe/base: rework X550em PHY setup function
[dpdk.git] / drivers / net / ixgbe / base / ixgbe_x550.c
index 2698c54..bc0ca53 100644 (file)
@@ -365,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;
@@ -378,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;
@@ -1142,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;
@@ -1287,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;
 }
@@ -1575,35 +1538,27 @@ s32 ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw,
 }
 
 /**
- * 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.
  */
-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);
@@ -1616,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,
@@ -2334,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);
+}