ixgbe/base: new X550 device ids
[dpdk.git] / lib / librte_pmd_ixgbe / ixgbe / ixgbe_x550.c
index 86cf691..0488bd5 100644 (file)
@@ -109,9 +109,11 @@ STATIC s32 ixgbe_identify_phy_x550em(struct ixgbe_hw *hw)
                hw->phy.type = ixgbe_phy_x550em_kx4;
                break;
        case IXGBE_DEV_ID_X550EM_X_KR:
-       case IXGBE_DEV_ID_X550EM_X:
                hw->phy.type = ixgbe_phy_x550em_kr;
                break;
+       case IXGBE_DEV_ID_X550EM_X_1G_T:
+       case IXGBE_DEV_ID_X550EM_X_10G_T:
+               return ixgbe_identify_phy_generic(hw);
        default:
                break;
        }
@@ -185,8 +187,6 @@ s32 ixgbe_init_ops_X550EM(struct ixgbe_hw *hw)
                                    ixgbe_get_supported_physical_layer_X550em;
 
        /* PHY */
-       phy->ops.read_reg = ixgbe_read_phy_reg_x550em;
-       phy->ops.write_reg = ixgbe_write_phy_reg_x550em;
        phy->ops.init = ixgbe_init_phy_ops_X550em;
        phy->ops.identify = ixgbe_identify_phy_x550em;
        if (mac->ops.get_media_type(hw) != ixgbe_media_type_copper)
@@ -403,8 +403,7 @@ s32 ixgbe_setup_eee_X550(struct ixgbe_hw *hw, bool enable_eee)
 
                        hw->phy.ops.write_reg(hw, IXGBE_MDIO_AUTO_NEG_EEE_ADVT,
                                IXGBE_MDIO_AUTO_NEG_DEV_TYPE, autoneg_eee_reg);
-               } else if (hw->device_id == IXGBE_DEV_ID_X550EM_X_KR ||
-                          hw->device_id == IXGBE_DEV_ID_X550EM_X) {
+               } else if (hw->device_id == IXGBE_DEV_ID_X550EM_X_KR) {
                        status = ixgbe_read_iosf_sb_reg_x550(hw,
                                IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
                                IXGBE_SB_IOSF_TARGET_KR_PHY, &link_reg);
@@ -434,8 +433,7 @@ s32 ixgbe_setup_eee_X550(struct ixgbe_hw *hw, bool enable_eee)
 
                        hw->phy.ops.write_reg(hw, IXGBE_MDIO_AUTO_NEG_EEE_ADVT,
                                IXGBE_MDIO_AUTO_NEG_DEV_TYPE, autoneg_eee_reg);
-               } else if (hw->device_id == IXGBE_DEV_ID_X550EM_X_KR ||
-                          hw->device_id == IXGBE_DEV_ID_X550EM_X) {
+               } else if (hw->device_id == IXGBE_DEV_ID_X550EM_X_KR) {
                        status = ixgbe_read_iosf_sb_reg_x550(hw,
                                IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
                                IXGBE_SB_IOSF_TARGET_KR_PHY, &link_reg);
@@ -766,7 +764,6 @@ enum ixgbe_media_type ixgbe_get_media_type_X550em(struct ixgbe_hw *hw)
 
        /* Detect if there is a copper PHY attached. */
        switch (hw->device_id) {
-       case IXGBE_DEV_ID_X550EM_X:
        case IXGBE_DEV_ID_X550EM_X_KR:
        case IXGBE_DEV_ID_X550EM_X_KX4:
                media_type = ixgbe_media_type_backplane;
@@ -774,6 +771,10 @@ enum ixgbe_media_type ixgbe_get_media_type_X550em(struct ixgbe_hw *hw)
        case IXGBE_DEV_ID_X550EM_X_SFP:
                media_type = ixgbe_media_type_fiber;
                break;
+       case IXGBE_DEV_ID_X550EM_X_1G_T:
+       case IXGBE_DEV_ID_X550EM_X_10G_T:
+               media_type = ixgbe_media_type_copper;
+               break;
        default:
                media_type = ixgbe_media_type_unknown;
                break;
@@ -942,8 +943,18 @@ s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
 
        /* Set functions pointers based on phy type */
        switch (hw->phy.type) {
+       case ixgbe_phy_x550em_kx4:
+               phy->ops.setup_link = ixgbe_setup_kx4_x550em;
+               phy->ops.read_reg = ixgbe_read_phy_reg_x550em;
+               phy->ops.write_reg = ixgbe_write_phy_reg_x550em;
+               break;
        case ixgbe_phy_x550em_kr:
                phy->ops.setup_link = ixgbe_setup_kr_x550em;
+               phy->ops.read_reg = ixgbe_read_phy_reg_x550em;
+               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;
                break;
        default:
                break;
@@ -985,6 +996,13 @@ s32 ixgbe_reset_hw_X550em(struct ixgbe_hw *hw)
        if (status == IXGBE_ERR_SFP_NOT_SUPPORTED)
                return status;
 
+       /* start the external PHY */
+       if (hw->phy.type == ixgbe_phy_x550em_ext_t) {
+               status = ixgbe_init_ext_t_x550em(hw);
+               if (status)
+                       return status;
+       }
+
        /* Setup SFP module if there is one present. */
        if (hw->phy.sfp_setup_needed) {
                status = hw->mac.ops.setup_sfp(hw);
@@ -1049,6 +1067,97 @@ mac_reset_top:
        hw->mac.num_rar_entries = 128;
        hw->mac.ops.init_rx_addrs(hw);
 
+
+       return status;
+}
+
+/**
+ * ixgbe_init_ext_t_x550em - Start (unstall) the external Base T PHY.
+ * @hw: pointer to hardware structure
+ */
+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_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;
+
+       /* 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;
+
+       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);
+
        return status;
 }
 
@@ -1093,12 +1202,51 @@ s32 ixgbe_setup_kr_x550em(struct ixgbe_hw *hw)
 }
 
 /**
- *  ixgbe_setup_ixfi_x550em - Configure the KR PHY for iXFI.
+ *  ixgbe_setup_kx4_x550em - Configure the KX4 PHY.
  *  @hw: pointer to hardware structure
  *
- *  Configures the integrated KR PHY to use iXFI mode.
+ *  Configures the integrated KX4 PHY.
  **/
-s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw)
+s32 ixgbe_setup_kx4_x550em(struct ixgbe_hw *hw)
+{
+       s32 status;
+       u32 reg_val;
+
+       status = ixgbe_read_iosf_sb_reg_x550(hw, IXGBE_KX4_LINK_CNTL_1,
+               IXGBE_SB_IOSF_TARGET_KX4_PCS0 + hw->bus.lan_id, &reg_val);
+       if (status)
+               return status;
+
+       reg_val &= ~(IXGBE_KX4_LINK_CNTL_1_TETH_AN_CAP_KX4 |
+                       IXGBE_KX4_LINK_CNTL_1_TETH_AN_CAP_KX);
+
+       reg_val |= IXGBE_KX4_LINK_CNTL_1_TETH_AN_ENABLE;
+
+       /* Advertise 10G support. */
+       if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL)
+               reg_val |= IXGBE_KX4_LINK_CNTL_1_TETH_AN_CAP_KX4;
+
+       /* Advertise 1G support. */
+       if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL)
+               reg_val |= IXGBE_KX4_LINK_CNTL_1_TETH_AN_CAP_KX;
+
+       /* Restart auto-negotiation. */
+       reg_val |= IXGBE_KX4_LINK_CNTL_1_TETH_AN_RESTART;
+       status = ixgbe_write_iosf_sb_reg_x550(hw, IXGBE_KX4_LINK_CNTL_1,
+               IXGBE_SB_IOSF_TARGET_KX4_PCS0 + hw->bus.lan_id, reg_val);
+
+       return status;
+}
+
+/**
+ *  ixgbe_setup_ixfi_x550em - Configure the KR PHY for iXFI mode.
+ *  @hw: pointer to hardware structure
+ *  @speed: the link speed to force
+ *
+ *  Configures the integrated KR PHY to use iXFI mode. Used to connect an
+ *  internal and external PHY at a specific speed, without autonegotiation.
+ **/
+STATIC s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed)
 {
        s32 status;
        u32 reg_val;
@@ -1112,7 +1260,20 @@ s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw)
 
        reg_val &= ~IXGBE_KRM_LINK_CTRL_1_TETH_AN_ENABLE;
        reg_val &= ~IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_MASK;
-       reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_10G;
+
+       /* Select forced link speed for internal PHY. */
+       switch (*speed) {
+       case IXGBE_LINK_SPEED_10GB_FULL:
+               reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_10G;
+               break;
+       case IXGBE_LINK_SPEED_1GB_FULL:
+               reg_val |= IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_1G;
+               break;
+       default:
+               /* Other link speeds are not supported by internal KR PHY. */
+               return IXGBE_ERR_LINK_SETUP;
+       }
+
        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);
@@ -1190,6 +1351,74 @@ s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw)
        return status;
 }
 
+/**
+ * ixgbe_setup_internal_phy_x550em - Configure integrated KR PHY
+ * @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.
+ *
+ * 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)
+{
+       u32 status;
+       u16 lasi, 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 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 */
+       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 = 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;
+
+       /* 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);
+
+       /* clear everything but the speed and duplex bits */
+       speed &= IXGBE_MDIO_AUTO_NEG_VENDOR_STATUS_MASK;
+
+       switch (speed) {
+       case IXGBE_MDIO_AUTO_NEG_VENDOR_STATUS_10GB_FULL:
+               force_speed = IXGBE_LINK_SPEED_10GB_FULL;
+               break;
+       case IXGBE_MDIO_AUTO_NEG_VENDOR_STATUS_1GB_FULL:
+               force_speed = IXGBE_LINK_SPEED_1GB_FULL;
+               break;
+       default:
+               /* Internal PHY does not support anything else */
+               return IXGBE_ERR_INVALID_LINK_SETTINGS;
+       }
+
+       return ixgbe_setup_ixfi_x550em(hw, &force_speed);
+}
+
 /**
  *  ixgbe_setup_phy_loopback_x550em - Configure the KR PHY for loopback.
  *  @hw: pointer to hardware structure
@@ -1501,19 +1730,28 @@ out:
  * Returns error status for any failure
  */
 STATIC s32 ixgbe_checksum_ptr_x550(struct ixgbe_hw *hw, u16 ptr,
-                                  u16 size, u16 *csum)
+                                  u16 size, u16 *csum, u16 *buffer,
+                                  u32 buffer_size)
 {
        u16 buf[256];
        s32 status;
        u16 length, bufsz, i, start;
+       u16 *local_buffer;
 
        bufsz = sizeof(buf) / sizeof(buf[0]);
 
        /* Read a chunk at the pointer location */
-       status = ixgbe_read_ee_hostif_buffer_X550(hw, ptr, bufsz, buf);
-       if (status) {
-               DEBUGOUT("Failed to read EEPROM image\n");
-               return status;
+       if (!buffer) {
+               status = ixgbe_read_ee_hostif_buffer_X550(hw, ptr, bufsz, buf);
+               if (status) {
+                       DEBUGOUT("Failed to read EEPROM image\n");
+                       return status;
+               }
+               local_buffer = buf;
+       } else {
+               if (buffer_size < ptr)
+                       return  IXGBE_ERR_PARAM;
+               local_buffer = &buffer[ptr];
        }
 
        if (size) {
@@ -1521,7 +1759,7 @@ STATIC s32 ixgbe_checksum_ptr_x550(struct ixgbe_hw *hw, u16 ptr,
                length = size;
        } else {
                start = 1;
-               length = buf[0];
+               length = local_buffer[0];
 
                /* Skip pointer section if length is invalid. */
                if (length == 0xFFFF || length == 0 ||
@@ -1529,8 +1767,11 @@ STATIC s32 ixgbe_checksum_ptr_x550(struct ixgbe_hw *hw, u16 ptr,
                        return IXGBE_SUCCESS;
        }
 
+       if (buffer && ((u32)start + (u32)length > buffer_size))
+               return IXGBE_ERR_PARAM;
+
        for (i = start; length; i++, length--) {
-               if (i == bufsz) {
+               if (i == bufsz && !buffer) {
                        ptr += bufsz;
                        i = 0;
                        if (length < bufsz)
@@ -1544,20 +1785,23 @@ STATIC s32 ixgbe_checksum_ptr_x550(struct ixgbe_hw *hw, u16 ptr,
                                return status;
                        }
                }
-               *csum += buf[i];
+               *csum += local_buffer[i];
        }
        return IXGBE_SUCCESS;
 }
 
 /**
- *  ixgbe_calc_eeprom_checksum_X550 - Calculates and returns the checksum
+ *  ixgbe_calc_checksum_X550 - Calculates and returns the checksum
  *  @hw: pointer to hardware structure
+ *  @buffer: pointer to buffer containing calculated checksum
+ *  @buffer_size: size of buffer
  *
  *  Returns a negative error code on error, or the 16-bit checksum
  **/
-s32 ixgbe_calc_eeprom_checksum_X550(struct ixgbe_hw *hw)
+s32 ixgbe_calc_checksum_X550(struct ixgbe_hw *hw, u16 *buffer, u32 buffer_size)
 {
        u16 eeprom_ptrs[IXGBE_EEPROM_LAST_WORD + 1];
+       u16 *local_buffer;
        s32 status;
        u16 checksum = 0;
        u16 pointer, i, size;
@@ -1566,13 +1810,20 @@ s32 ixgbe_calc_eeprom_checksum_X550(struct ixgbe_hw *hw)
 
        hw->eeprom.ops.init_params(hw);
 
-       /* Read pointer area */
-       status = ixgbe_read_ee_hostif_buffer_X550(hw, 0,
-                                                 IXGBE_EEPROM_LAST_WORD + 1,
-                                                 eeprom_ptrs);
-       if (status) {
-               DEBUGOUT("Failed to read EEPROM image\n");
-               return status;
+       if (!buffer) {
+               /* Read pointer area */
+               status = ixgbe_read_ee_hostif_buffer_X550(hw, 0,
+                                                    IXGBE_EEPROM_LAST_WORD + 1,
+                                                    eeprom_ptrs);
+               if (status) {
+                       DEBUGOUT("Failed to read EEPROM image\n");
+                       return status;
+               }
+               local_buffer = eeprom_ptrs;
+       } else {
+               if (buffer_size < IXGBE_EEPROM_LAST_WORD)
+                       return IXGBE_ERR_PARAM;
+               local_buffer = buffer;
        }
 
        /*
@@ -1581,7 +1832,7 @@ s32 ixgbe_calc_eeprom_checksum_X550(struct ixgbe_hw *hw)
         */
        for (i = 0; i <= IXGBE_EEPROM_LAST_WORD; i++)
                if (i != IXGBE_EEPROM_CHECKSUM)
-                       checksum += eeprom_ptrs[i];
+                       checksum += local_buffer[i];
 
        /*
         * Include all data from pointers 0x3, 0x6-0xE.  This excludes the
@@ -1591,7 +1842,7 @@ s32 ixgbe_calc_eeprom_checksum_X550(struct ixgbe_hw *hw)
                if (i == IXGBE_PHY_PTR || i == IXGBE_OPTION_ROM_PTR)
                        continue;
 
-               pointer = eeprom_ptrs[i];
+               pointer = local_buffer[i];
 
                /* Skip pointer section if the pointer is invalid. */
                if (pointer == 0xFFFF || pointer == 0 ||
@@ -1611,7 +1862,8 @@ s32 ixgbe_calc_eeprom_checksum_X550(struct ixgbe_hw *hw)
                        break;
                }
 
-               status = ixgbe_checksum_ptr_x550(hw, pointer, size, &checksum);
+               status = ixgbe_checksum_ptr_x550(hw, pointer, size, &checksum,
+                                               buffer, buffer_size);
                if (status)
                        return status;
        }
@@ -1621,6 +1873,17 @@ s32 ixgbe_calc_eeprom_checksum_X550(struct ixgbe_hw *hw)
        return (s32)checksum;
 }
 
+/**
+ *  ixgbe_calc_eeprom_checksum_X550 - Calculates and returns the checksum
+ *  @hw: pointer to hardware structure
+ *
+ *  Returns a negative error code on error, or the 16-bit checksum
+ **/
+s32 ixgbe_calc_eeprom_checksum_X550(struct ixgbe_hw *hw)
+{
+       return ixgbe_calc_checksum_X550(hw, NULL, 0);
+}
+
 /**
  *  ixgbe_validate_eeprom_checksum_X550 - Validate EEPROM checksum
  *  @hw: pointer to hardware structure
@@ -1748,6 +2011,7 @@ s32 ixgbe_update_flash_X550(struct ixgbe_hw *hw)
 u32 ixgbe_get_supported_physical_layer_X550em(struct ixgbe_hw *hw)
 {
        u32 physical_layer = IXGBE_PHYSICAL_LAYER_UNKNOWN;
+       u16 ext_ability = 0;
 
        DEBUGFUNC("ixgbe_get_supported_physical_layer_X550em");
 
@@ -1762,6 +2026,15 @@ u32 ixgbe_get_supported_physical_layer_X550em(struct ixgbe_hw *hw)
                physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_KX4 |
                                 IXGBE_PHYSICAL_LAYER_1000BASE_KX;
                break;
+       case ixgbe_phy_x550em_ext_t:
+               hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_EXT_ABILITY,
+                                    IXGBE_MDIO_PMA_PMD_DEV_TYPE,
+                                    &ext_ability);
+               if (ext_ability & IXGBE_MDIO_PHY_10GBASET_ABILITY)
+                       physical_layer |= IXGBE_PHYSICAL_LAYER_10GBASE_T;
+               if (ext_ability & IXGBE_MDIO_PHY_1000BASET_ABILITY)
+                       physical_layer |= IXGBE_PHYSICAL_LAYER_1000BASE_T;
+               break;
        default:
                break;
        }