ixgbe: Implement support for firmware-controlled PHYs
authorMark Rustad <mark.d.rustad@intel.com>
Wed, 14 Dec 2016 19:02:16 +0000 (11:02 -0800)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Tue, 3 Jan 2017 21:03:39 +0000 (13:03 -0800)
Implement support for devices that have firmware-controlled PHYs.

Signed-off-by: Mark Rustad <mark.d.rustad@intel.com>
Tested-by: Krishneil Singh <krishneil.k.singh@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/ixgbe/ixgbe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c

index ef81c3d8c2952fa305390232bf9e93a901f402f2..53fb427b1c29a343d3df5c814a4a56a45cc968cc 100644 (file)
@@ -661,6 +661,8 @@ struct ixgbe_adapter {
 #define IXGBE_FLAG2_PHY_INTERRUPT              BIT(11)
 #define IXGBE_FLAG2_UDP_TUN_REREG_NEEDED       BIT(12)
 #define IXGBE_FLAG2_VLAN_PROMISC               BIT(13)
+#define IXGBE_FLAG2_EEE_CAPABLE                        BIT(14)
+#define IXGBE_FLAG2_EEE_ENABLED                        BIT(15)
 
        /* Tx fast path data */
        int num_tx_queues;
@@ -862,6 +864,7 @@ enum ixgbe_boards {
        board_X550,
        board_X550EM_x,
        board_x550em_a,
+       board_x550em_a_fw,
 };
 
 extern const struct ixgbe_info ixgbe_82598_info;
@@ -870,6 +873,7 @@ extern const struct ixgbe_info ixgbe_X540_info;
 extern const struct ixgbe_info ixgbe_X550_info;
 extern const struct ixgbe_info ixgbe_X550EM_x_info;
 extern const struct ixgbe_info ixgbe_x550em_a_info;
+extern const struct ixgbe_info ixgbe_x550em_a_fw_info;
 #ifdef CONFIG_IXGBE_DCB
 extern const struct dcbnl_rtnl_ops dcbnl_ops;
 #endif
index 851f4855550665ee52f3f00d24a736d2136cf2ef..094e1d63309a7a252eae31db2875185d9f3cb0e1 100644 (file)
@@ -100,6 +100,8 @@ bool ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw)
                case IXGBE_DEV_ID_X550T1:
                case IXGBE_DEV_ID_X550EM_X_10G_T:
                case IXGBE_DEV_ID_X550EM_A_10G_T:
+               case IXGBE_DEV_ID_X550EM_A_1G_T:
+               case IXGBE_DEV_ID_X550EM_A_1G_T_L:
                        supported = true;
                        break;
                default:
@@ -3382,6 +3384,13 @@ s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed,
                else
                        *speed = IXGBE_LINK_SPEED_100_FULL;
                break;
+       case IXGBE_LINKS_SPEED_10_X550EM_A:
+               *speed = IXGBE_LINK_SPEED_UNKNOWN;
+               if (hw->device_id == IXGBE_DEV_ID_X550EM_A_1G_T ||
+                   hw->device_id == IXGBE_DEV_ID_X550EM_A_1G_T_L) {
+                       *speed = IXGBE_LINK_SPEED_10_FULL;
+               }
+               break;
        default:
                *speed = IXGBE_LINK_SPEED_UNKNOWN;
        }
index 8789f214a4f95711faf9307c589e938ef6fe6f29..17589068da78617c8a3d9e81d07cfb307d2a7df8 100644 (file)
@@ -197,15 +197,17 @@ static int ixgbe_get_settings(struct net_device *netdev,
                                   SUPPORTED_1000baseKX_Full :
                                   SUPPORTED_1000baseT_Full;
        if (supported_link & IXGBE_LINK_SPEED_100_FULL)
-               ecmd->supported |= ixgbe_isbackplane(hw->phy.media_type) ?
-                                  SUPPORTED_1000baseKX_Full :
-                                  SUPPORTED_100baseT_Full;
+               ecmd->supported |= SUPPORTED_100baseT_Full;
+       if (supported_link & IXGBE_LINK_SPEED_10_FULL)
+               ecmd->supported |= SUPPORTED_10baseT_Full;
 
        /* default advertised speed if phy.autoneg_advertised isn't set */
        ecmd->advertising = ecmd->supported;
        /* set the advertised speeds */
        if (hw->phy.autoneg_advertised) {
                ecmd->advertising = 0;
+               if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10_FULL)
+                       ecmd->advertising |= ADVERTISED_10baseT_Full;
                if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL)
                        ecmd->advertising |= ADVERTISED_100baseT_Full;
                if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL)
@@ -237,6 +239,7 @@ static int ixgbe_get_settings(struct net_device *netdev,
        case ixgbe_phy_tn:
        case ixgbe_phy_aq:
        case ixgbe_phy_x550em_ext_t:
+       case ixgbe_phy_fw:
        case ixgbe_phy_cu_unknown:
                ecmd->supported |= SUPPORTED_TP;
                ecmd->advertising |= ADVERTISED_TP;
@@ -346,6 +349,9 @@ static int ixgbe_get_settings(struct net_device *netdev,
                case IXGBE_LINK_SPEED_100_FULL:
                        ethtool_cmd_speed_set(ecmd, SPEED_100);
                        break;
+               case IXGBE_LINK_SPEED_10_FULL:
+                       ethtool_cmd_speed_set(ecmd, SPEED_10);
+                       break;
                default:
                        break;
                }
@@ -394,6 +400,9 @@ static int ixgbe_set_settings(struct net_device *netdev,
                if (ecmd->advertising & ADVERTISED_100baseT_Full)
                        advertised |= IXGBE_LINK_SPEED_100_FULL;
 
+               if (ecmd->advertising & ADVERTISED_10baseT_Full)
+                       advertised |= IXGBE_LINK_SPEED_10_FULL;
+
                if (old == advertised)
                        return err;
                /* this sets the link speed and restarts auto-neg */
@@ -3173,6 +3182,9 @@ static int ixgbe_get_module_info(struct net_device *dev,
        u8 sff8472_rev, addr_mode;
        bool page_swap = false;
 
+       if (hw->phy.type == ixgbe_phy_fw)
+               return -ENXIO;
+
        /* Check whether we support SFF-8472 or not */
        status = hw->phy.ops.read_i2c_eeprom(hw,
                                             IXGBE_SFF_SFF_8472_COMP,
@@ -3218,6 +3230,9 @@ static int ixgbe_get_module_eeprom(struct net_device *dev,
        if (ee->len == 0)
                return -EINVAL;
 
+       if (hw->phy.type == ixgbe_phy_fw)
+               return -ENXIO;
+
        for (i = ee->offset; i < ee->offset + ee->len; i++) {
                /* I2C reads can take long time */
                if (test_bit(__IXGBE_IN_SFP_INIT, &adapter->state))
@@ -3237,6 +3252,136 @@ static int ixgbe_get_module_eeprom(struct net_device *dev,
        return 0;
 }
 
+static const struct {
+       ixgbe_link_speed mac_speed;
+       u32 supported;
+} ixgbe_ls_map[] = {
+       { IXGBE_LINK_SPEED_10_FULL, SUPPORTED_10baseT_Full },
+       { IXGBE_LINK_SPEED_100_FULL, SUPPORTED_100baseT_Full },
+       { IXGBE_LINK_SPEED_1GB_FULL, SUPPORTED_1000baseT_Full },
+       { IXGBE_LINK_SPEED_2_5GB_FULL, SUPPORTED_2500baseX_Full },
+       { IXGBE_LINK_SPEED_10GB_FULL, SUPPORTED_10000baseT_Full },
+};
+
+static const struct {
+       u32 lp_advertised;
+       u32 mac_speed;
+} ixgbe_lp_map[] = {
+       { FW_PHY_ACT_UD_2_100M_TX_EEE, SUPPORTED_100baseT_Full },
+       { FW_PHY_ACT_UD_2_1G_T_EEE, SUPPORTED_1000baseT_Full },
+       { FW_PHY_ACT_UD_2_10G_T_EEE, SUPPORTED_10000baseT_Full },
+       { FW_PHY_ACT_UD_2_1G_KX_EEE, SUPPORTED_1000baseKX_Full },
+       { FW_PHY_ACT_UD_2_10G_KX4_EEE, SUPPORTED_10000baseKX4_Full },
+       { FW_PHY_ACT_UD_2_10G_KR_EEE, SUPPORTED_10000baseKR_Full},
+};
+
+static int
+ixgbe_get_eee_fw(struct ixgbe_adapter *adapter, struct ethtool_eee *edata)
+{
+       u32 info[FW_PHY_ACT_DATA_COUNT] = { 0 };
+       struct ixgbe_hw *hw = &adapter->hw;
+       s32 rc;
+       u16 i;
+
+       rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_UD_2, &info);
+       if (rc)
+               return rc;
+
+       edata->lp_advertised = 0;
+       for (i = 0; i < ARRAY_SIZE(ixgbe_lp_map); ++i) {
+               if (info[0] & ixgbe_lp_map[i].lp_advertised)
+                       edata->lp_advertised |= ixgbe_lp_map[i].mac_speed;
+       }
+
+       edata->supported = 0;
+       for (i = 0; i < ARRAY_SIZE(ixgbe_ls_map); ++i) {
+               if (hw->phy.eee_speeds_supported & ixgbe_ls_map[i].mac_speed)
+                       edata->supported |= ixgbe_ls_map[i].supported;
+       }
+
+       edata->advertised = 0;
+       for (i = 0; i < ARRAY_SIZE(ixgbe_ls_map); ++i) {
+               if (hw->phy.eee_speeds_advertised & ixgbe_ls_map[i].mac_speed)
+                       edata->advertised |= ixgbe_ls_map[i].supported;
+       }
+
+       edata->eee_enabled = !!edata->advertised;
+       edata->tx_lpi_enabled = edata->eee_enabled;
+       if (edata->advertised & edata->lp_advertised)
+               edata->eee_active = true;
+
+       return 0;
+}
+
+static int ixgbe_get_eee(struct net_device *netdev, struct ethtool_eee *edata)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       if (!(adapter->flags2 & IXGBE_FLAG2_EEE_CAPABLE))
+               return -EOPNOTSUPP;
+
+       if (hw->phy.eee_speeds_supported && hw->phy.type == ixgbe_phy_fw)
+               return ixgbe_get_eee_fw(adapter, edata);
+
+       return -EOPNOTSUPP;
+}
+
+static int ixgbe_set_eee(struct net_device *netdev, struct ethtool_eee *edata)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+       struct ixgbe_hw *hw = &adapter->hw;
+       struct ethtool_eee eee_data;
+       s32 ret_val;
+
+       if (!(adapter->flags2 & IXGBE_FLAG2_EEE_CAPABLE))
+               return -EOPNOTSUPP;
+
+       memset(&eee_data, 0, sizeof(struct ethtool_eee));
+
+       ret_val = ixgbe_get_eee(netdev, &eee_data);
+       if (ret_val)
+               return ret_val;
+
+       if (eee_data.eee_enabled && !edata->eee_enabled) {
+               if (eee_data.tx_lpi_enabled != edata->tx_lpi_enabled) {
+                       e_err(drv, "Setting EEE tx-lpi is not supported\n");
+                       return -EINVAL;
+               }
+
+               if (eee_data.tx_lpi_timer != edata->tx_lpi_timer) {
+                       e_err(drv,
+                             "Setting EEE Tx LPI timer is not supported\n");
+                       return -EINVAL;
+               }
+
+               if (eee_data.advertised != edata->advertised) {
+                       e_err(drv,
+                             "Setting EEE advertised speeds is not supported\n");
+                       return -EINVAL;
+               }
+       }
+
+       if (eee_data.eee_enabled != edata->eee_enabled) {
+               if (edata->eee_enabled) {
+                       adapter->flags2 |= IXGBE_FLAG2_EEE_ENABLED;
+                       hw->phy.eee_speeds_advertised =
+                                                  hw->phy.eee_speeds_supported;
+               } else {
+                       adapter->flags2 &= ~IXGBE_FLAG2_EEE_ENABLED;
+                       hw->phy.eee_speeds_advertised = 0;
+               }
+
+               /* reset link */
+               if (netif_running(netdev))
+                       ixgbe_reinit_locked(adapter);
+               else
+                       ixgbe_reset(adapter);
+       }
+
+       return 0;
+}
+
 static const struct ethtool_ops ixgbe_ethtool_ops = {
        .get_settings           = ixgbe_get_settings,
        .set_settings           = ixgbe_set_settings,
@@ -3269,6 +3414,8 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
        .get_rxfh_key_size      = ixgbe_get_rxfh_key_size,
        .get_rxfh               = ixgbe_get_rxfh,
        .set_rxfh               = ixgbe_set_rxfh,
+       .get_eee                = ixgbe_get_eee,
+       .set_eee                = ixgbe_set_eee,
        .get_channels           = ixgbe_get_channels,
        .set_channels           = ixgbe_set_channels,
        .get_ts_info            = ixgbe_get_ts_info,
index 9ca167c5d1fa5df2d49ef9e7819669584919e400..0c6eca5707911debb7984e56fbfd78f5e4fe6c02 100644 (file)
@@ -86,6 +86,7 @@ static const struct ixgbe_info *ixgbe_info_tbl[] = {
        [board_X550]            = &ixgbe_X550_info,
        [board_X550EM_x]        = &ixgbe_X550EM_x_info,
        [board_x550em_a]        = &ixgbe_x550em_a_info,
+       [board_x550em_a_fw]     = &ixgbe_x550em_a_fw_info,
 };
 
 /* ixgbe_pci_tbl - PCI Device ID Table
@@ -140,6 +141,8 @@ static const struct pci_device_id ixgbe_pci_tbl[] = {
        {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_SGMII_L), board_x550em_a },
        {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_10G_T), board_x550em_a},
        {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_SFP), board_x550em_a },
+       {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_1G_T), board_x550em_a_fw },
+       {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_1G_T_L), board_x550em_a_fw },
        /* required last entry */
        {0, }
 };
@@ -180,6 +183,7 @@ MODULE_VERSION(DRV_VERSION);
 static struct workqueue_struct *ixgbe_wq;
 
 static bool ixgbe_check_cfg_remove(struct ixgbe_hw *hw, struct pci_dev *pdev);
+static void ixgbe_watchdog_link_is_down(struct ixgbe_adapter *);
 
 static int ixgbe_read_pci_cfg_word_parent(struct ixgbe_adapter *adapter,
                                          u32 reg, u16 *value)
@@ -2447,6 +2451,7 @@ static void ixgbe_check_overtemp_subtask(struct ixgbe_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
        u32 eicr = adapter->interrupt_event;
+       s32 rc;
 
        if (test_bit(__IXGBE_DOWN, &adapter->state))
                return;
@@ -2485,6 +2490,12 @@ static void ixgbe_check_overtemp_subtask(struct ixgbe_adapter *adapter)
                        return;
 
                break;
+       case IXGBE_DEV_ID_X550EM_A_1G_T:
+       case IXGBE_DEV_ID_X550EM_A_1G_T_L:
+               rc = hw->phy.ops.check_overtemp(hw);
+               if (rc != IXGBE_ERR_OVERTEMP)
+                       return;
+               break;
        default:
                if (adapter->hw.mac.type >= ixgbe_mac_X540)
                        return;
@@ -2531,6 +2542,18 @@ static void ixgbe_check_overtemp_event(struct ixgbe_adapter *adapter, u32 eicr)
                        return;
                }
                return;
+       case ixgbe_mac_x550em_a:
+               if (eicr & IXGBE_EICR_GPI_SDP0_X550EM_a) {
+                       adapter->interrupt_event = eicr;
+                       adapter->flags2 |= IXGBE_FLAG2_TEMP_SENSOR_EVENT;
+                       ixgbe_service_event_schedule(adapter);
+                       IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC,
+                                       IXGBE_EICR_GPI_SDP0_X550EM_a);
+                       IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICR,
+                                       IXGBE_EICR_GPI_SDP0_X550EM_a);
+               }
+               return;
+       case ixgbe_mac_X550:
        case ixgbe_mac_X540:
                if (!(eicr & IXGBE_EICR_TS))
                        return;
@@ -5294,6 +5317,8 @@ void ixgbe_reinit_locked(struct ixgbe_adapter *adapter)
 
        while (test_and_set_bit(__IXGBE_RESETTING, &adapter->state))
                usleep_range(1000, 2000);
+       if (adapter->hw.phy.type == ixgbe_phy_fw)
+               ixgbe_watchdog_link_is_down(adapter);
        ixgbe_down(adapter);
        /*
         * If SR-IOV enabled then wait a bit before bringing the adapter
@@ -5553,6 +5578,31 @@ void ixgbe_down(struct ixgbe_adapter *adapter)
        ixgbe_clean_all_rx_rings(adapter);
 }
 
+/**
+ * ixgbe_eee_capable - helper function to determine EEE support on X550
+ * @adapter: board private structure
+ */
+static void ixgbe_set_eee_capable(struct ixgbe_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       switch (hw->device_id) {
+       case IXGBE_DEV_ID_X550EM_A_1G_T:
+       case IXGBE_DEV_ID_X550EM_A_1G_T_L:
+               if (!hw->phy.eee_speeds_supported)
+                       break;
+               adapter->flags2 |= IXGBE_FLAG2_EEE_CAPABLE;
+               if (!hw->phy.eee_speeds_advertised)
+                       break;
+               adapter->flags2 |= IXGBE_FLAG2_EEE_ENABLED;
+               break;
+       default:
+               adapter->flags2 &= ~IXGBE_FLAG2_EEE_CAPABLE;
+               adapter->flags2 &= ~IXGBE_FLAG2_EEE_ENABLED;
+               break;
+       }
+}
+
 /**
  * ixgbe_tx_timeout - Respond to a Tx Hang
  * @netdev: network interface device structure
@@ -5717,6 +5767,14 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter,
                break;
        case ixgbe_mac_x550em_a:
                adapter->flags |= IXGBE_FLAG_GENEVE_OFFLOAD_CAPABLE;
+               switch (hw->device_id) {
+               case IXGBE_DEV_ID_X550EM_A_1G_T:
+               case IXGBE_DEV_ID_X550EM_A_1G_T_L:
+                       adapter->flags2 |= IXGBE_FLAG2_TEMP_SENSOR_CAPABLE;
+                       break;
+               default:
+                       break;
+               }
        /* fall through */
        case ixgbe_mac_X550EM_x:
 #ifdef CONFIG_IXGBE_DCB
@@ -5730,6 +5788,8 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter,
 #endif /* IXGBE_FCOE */
        /* Fall Through */
        case ixgbe_mac_X550:
+               if (hw->mac.type == ixgbe_mac_X550)
+                       adapter->flags2 |= IXGBE_FLAG2_TEMP_SENSOR_CAPABLE;
 #ifdef CONFIG_IXGBE_DCA
                adapter->flags &= ~IXGBE_FLAG_DCA_CAPABLE;
 #endif
@@ -6807,6 +6867,9 @@ static void ixgbe_watchdog_link_is_up(struct ixgbe_adapter *adapter)
        case IXGBE_LINK_SPEED_100_FULL:
                speed_str = "100 Mbps";
                break;
+       case IXGBE_LINK_SPEED_10_FULL:
+               speed_str = "10 Mbps";
+               break;
        default:
                speed_str = "unknown speed";
                break;
@@ -9595,6 +9658,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        hw->phy.reset_if_overtemp = true;
        err = hw->mac.ops.reset_hw(hw);
        hw->phy.reset_if_overtemp = false;
+       ixgbe_set_eee_capable(adapter);
        if (err == IXGBE_ERR_SFP_NOT_PRESENT) {
                err = 0;
        } else if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) {
index 67a399bab6f02a025c349787c68e4c402088c852..2fcde8777a2902a94b914d471abf83999492f7cd 100644 (file)
@@ -784,6 +784,9 @@ s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
        if (speed & IXGBE_LINK_SPEED_100_FULL)
                hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_100_FULL;
 
+       if (speed & IXGBE_LINK_SPEED_10_FULL)
+               hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10_FULL;
+
        /* Setup link based on the new speed settings */
        hw->phy.ops.setup_link(hw);
 
index a8f2d70352414d8031d02a6b14fcb746b22dcef1..b2cd8d443fc53be1e5af6b6c0b2946ff4144972a 100644 (file)
@@ -92,6 +92,8 @@
 #define IXGBE_DEV_ID_X550EM_A_SGMII_L  0x15C7
 #define IXGBE_DEV_ID_X550EM_A_10G_T    0x15C8
 #define IXGBE_DEV_ID_X550EM_A_SFP      0x15CE
+#define IXGBE_DEV_ID_X550EM_A_1G_T     0x15E4
+#define IXGBE_DEV_ID_X550EM_A_1G_T_L   0x15E5
 
 /* VF Device IDs */
 #define IXGBE_DEV_ID_82599_VF          0x10ED
@@ -1914,6 +1916,7 @@ enum {
 #define IXGBE_LINKS_SPEED_10G_82599 0x30000000
 #define IXGBE_LINKS_SPEED_1G_82599  0x20000000
 #define IXGBE_LINKS_SPEED_100_82599 0x10000000
+#define IXGBE_LINKS_SPEED_10_X550EM_A 0
 #define IXGBE_LINK_UP_TIME      90 /* 9.0 Seconds */
 #define IXGBE_AUTO_NEG_TIME     45 /* 4.5 Seconds */
 
@@ -2926,6 +2929,7 @@ typedef u32 ixgbe_autoneg_advertised;
 /* Link speed */
 typedef u32 ixgbe_link_speed;
 #define IXGBE_LINK_SPEED_UNKNOWN       0
+#define IXGBE_LINK_SPEED_10_FULL       0x0002
 #define IXGBE_LINK_SPEED_100_FULL      0x0008
 #define IXGBE_LINK_SPEED_1GB_FULL      0x0020
 #define IXGBE_LINK_SPEED_2_5GB_FULL    0x0400
@@ -3141,6 +3145,7 @@ enum ixgbe_phy_type {
        ixgbe_phy_qsfp_unknown,
        ixgbe_phy_sfp_unsupported,
        ixgbe_phy_sgmii,
+       ixgbe_phy_fw,
        ixgbe_phy_generic
 };
 
@@ -3555,6 +3560,8 @@ struct ixgbe_phy_info {
        bool                            reset_disable;
        ixgbe_autoneg_advertised        autoneg_advertised;
        ixgbe_link_speed                speeds_supported;
+       ixgbe_link_speed                eee_speeds_supported;
+       ixgbe_link_speed                eee_speeds_advertised;
        enum ixgbe_smart_speed          smart_speed;
        bool                            smart_speed_active;
        bool                            multispeed_fiber;
index 9dc6079f2ac4b24e6fa4e7f316208d4f03813e08..200f847fd8f31e58005ac72f3d90d4e97b11f96d 100644 (file)
@@ -63,6 +63,18 @@ static s32 ixgbe_get_invariants_X550_a(struct ixgbe_hw *hw)
        return 0;
 }
 
+static s32 ixgbe_get_invariants_X550_a_fw(struct ixgbe_hw *hw)
+{
+       struct ixgbe_phy_info *phy = &hw->phy;
+
+       /* Start with X540 invariants, since so similar */
+       ixgbe_get_invariants_X540(hw);
+
+       phy->ops.set_phy_power = NULL;
+
+       return 0;
+}
+
 /** ixgbe_setup_mux_ctl - Setup ESDP register for I2C mux control
  *  @hw: pointer to hardware structure
  **/
@@ -447,6 +459,159 @@ s32 ixgbe_fw_phy_activity(struct ixgbe_hw *hw, u16 activity,
        return IXGBE_ERR_HOST_INTERFACE_COMMAND;
 }
 
+static const struct {
+       u16 fw_speed;
+       ixgbe_link_speed phy_speed;
+} ixgbe_fw_map[] = {
+       { FW_PHY_ACT_LINK_SPEED_10, IXGBE_LINK_SPEED_10_FULL },
+       { FW_PHY_ACT_LINK_SPEED_100, IXGBE_LINK_SPEED_100_FULL },
+       { FW_PHY_ACT_LINK_SPEED_1G, IXGBE_LINK_SPEED_1GB_FULL },
+       { FW_PHY_ACT_LINK_SPEED_2_5G, IXGBE_LINK_SPEED_2_5GB_FULL },
+       { FW_PHY_ACT_LINK_SPEED_5G, IXGBE_LINK_SPEED_5GB_FULL },
+       { FW_PHY_ACT_LINK_SPEED_10G, IXGBE_LINK_SPEED_10GB_FULL },
+};
+
+/**
+ * ixgbe_get_phy_id_fw - Get the phy ID via firmware command
+ * @hw: pointer to hardware structure
+ *
+ * Returns error code
+ */
+static s32 ixgbe_get_phy_id_fw(struct ixgbe_hw *hw)
+{
+       u32 info[FW_PHY_ACT_DATA_COUNT] = { 0 };
+       u16 phy_speeds;
+       u16 phy_id_lo;
+       s32 rc;
+       u16 i;
+
+       if (hw->phy.id)
+               return 0;
+
+       rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_GET_PHY_INFO, &info);
+       if (rc)
+               return rc;
+
+       hw->phy.speeds_supported = 0;
+       phy_speeds = info[0] & FW_PHY_INFO_SPEED_MASK;
+       for (i = 0; i < ARRAY_SIZE(ixgbe_fw_map); ++i) {
+               if (phy_speeds & ixgbe_fw_map[i].fw_speed)
+                       hw->phy.speeds_supported |= ixgbe_fw_map[i].phy_speed;
+       }
+
+       hw->phy.id = info[0] & FW_PHY_INFO_ID_HI_MASK;
+       phy_id_lo = info[1] & FW_PHY_INFO_ID_LO_MASK;
+       hw->phy.id |= phy_id_lo & IXGBE_PHY_REVISION_MASK;
+       hw->phy.revision = phy_id_lo & ~IXGBE_PHY_REVISION_MASK;
+       if (!hw->phy.id || hw->phy.id == IXGBE_PHY_REVISION_MASK)
+               return IXGBE_ERR_PHY_ADDR_INVALID;
+
+       hw->phy.autoneg_advertised = hw->phy.speeds_supported;
+       hw->phy.eee_speeds_supported = IXGBE_LINK_SPEED_100_FULL |
+                                      IXGBE_LINK_SPEED_1GB_FULL;
+       hw->phy.eee_speeds_advertised = hw->phy.eee_speeds_supported;
+       return 0;
+}
+
+/**
+ * ixgbe_identify_phy_fw - Get PHY type based on firmware command
+ * @hw: pointer to hardware structure
+ *
+ * Returns error code
+ */
+static s32 ixgbe_identify_phy_fw(struct ixgbe_hw *hw)
+{
+       if (hw->bus.lan_id)
+               hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY1_SM;
+       else
+               hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY0_SM;
+
+       hw->phy.type = ixgbe_phy_fw;
+       hw->phy.ops.read_reg = NULL;
+       hw->phy.ops.write_reg = NULL;
+       return ixgbe_get_phy_id_fw(hw);
+}
+
+/**
+ * ixgbe_shutdown_fw_phy - Shutdown a firmware-controlled PHY
+ * @hw: pointer to hardware structure
+ *
+ * Returns error code
+ */
+static s32 ixgbe_shutdown_fw_phy(struct ixgbe_hw *hw)
+{
+       u32 setup[FW_PHY_ACT_DATA_COUNT] = { 0 };
+
+       setup[0] = FW_PHY_ACT_FORCE_LINK_DOWN_OFF;
+       return ixgbe_fw_phy_activity(hw, FW_PHY_ACT_FORCE_LINK_DOWN, &setup);
+}
+
+/**
+ * ixgbe_setup_fw_link - Setup firmware-controlled PHYs
+ * @hw: pointer to hardware structure
+ */
+static s32 ixgbe_setup_fw_link(struct ixgbe_hw *hw)
+{
+       u32 setup[FW_PHY_ACT_DATA_COUNT] = { 0 };
+       s32 rc;
+       u16 i;
+
+       if (hw->phy.reset_disable || ixgbe_check_reset_blocked(hw))
+               return 0;
+
+       if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) {
+               hw_err(hw, "rx_pause not valid in strict IEEE mode\n");
+               return IXGBE_ERR_INVALID_LINK_SETTINGS;
+       }
+
+       switch (hw->fc.requested_mode) {
+       case ixgbe_fc_full:
+               setup[0] |= FW_PHY_ACT_SETUP_LINK_PAUSE_RXTX <<
+                           FW_PHY_ACT_SETUP_LINK_PAUSE_SHIFT;
+               break;
+       case ixgbe_fc_rx_pause:
+               setup[0] |= FW_PHY_ACT_SETUP_LINK_PAUSE_RX <<
+                           FW_PHY_ACT_SETUP_LINK_PAUSE_SHIFT;
+               break;
+       case ixgbe_fc_tx_pause:
+               setup[0] |= FW_PHY_ACT_SETUP_LINK_PAUSE_TX <<
+                           FW_PHY_ACT_SETUP_LINK_PAUSE_SHIFT;
+               break;
+       default:
+               break;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(ixgbe_fw_map); ++i) {
+               if (hw->phy.autoneg_advertised & ixgbe_fw_map[i].phy_speed)
+                       setup[0] |= ixgbe_fw_map[i].fw_speed;
+       }
+       setup[0] |= FW_PHY_ACT_SETUP_LINK_HP | FW_PHY_ACT_SETUP_LINK_AN;
+
+       if (hw->phy.eee_speeds_advertised)
+               setup[0] |= FW_PHY_ACT_SETUP_LINK_EEE;
+
+       rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_SETUP_LINK, &setup);
+       if (rc)
+               return rc;
+       if (setup[0] == FW_PHY_ACT_SETUP_LINK_RSP_DOWN)
+               return IXGBE_ERR_OVERTEMP;
+       return 0;
+}
+
+/**
+ * ixgbe_fc_autoneg_fw - Set up flow control for FW-controlled PHYs
+ * @hw: pointer to hardware structure
+ *
+ * Called at init time to set up flow control.
+ */
+static s32 ixgbe_fc_autoneg_fw(struct ixgbe_hw *hw)
+{
+       if (hw->fc.requested_mode == ixgbe_fc_default)
+               hw->fc.requested_mode = ixgbe_fc_full;
+
+       return ixgbe_setup_fw_link(hw);
+}
+
 /** ixgbe_init_eeprom_params_X550 - Initialize EEPROM params
  *  @hw: pointer to hardware structure
  *
@@ -1794,6 +1959,125 @@ ixgbe_setup_sgmii(struct ixgbe_hw *hw, __always_unused ixgbe_link_speed speed,
        return rc;
 }
 
+/**
+ * ixgbe_setup_sgmii_fw - Set up link for sgmii with firmware-controlled PHYs
+ * @hw: pointer to hardware structure
+ */
+static s32 ixgbe_setup_sgmii_fw(struct ixgbe_hw *hw, ixgbe_link_speed speed,
+                               bool autoneg_wait)
+{
+       struct ixgbe_mac_info *mac = &hw->mac;
+       u32 lval, sval, flx_val;
+       s32 rc;
+
+       rc = mac->ops.read_iosf_sb_reg(hw,
+                                      IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
+                                      IXGBE_SB_IOSF_TARGET_KR_PHY, &lval);
+       if (rc)
+               return rc;
+
+       lval &= ~IXGBE_KRM_LINK_CTRL_1_TETH_AN_ENABLE;
+       lval &= ~IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_MASK;
+       lval |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_SGMII_EN;
+       lval |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_CLAUSE_37_EN;
+       lval &= ~IXGBE_KRM_LINK_CTRL_1_TETH_FORCE_SPEED_1G;
+       rc = mac->ops.write_iosf_sb_reg(hw,
+                                       IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
+                                       IXGBE_SB_IOSF_TARGET_KR_PHY, lval);
+       if (rc)
+               return rc;
+
+       rc = mac->ops.read_iosf_sb_reg(hw,
+                                      IXGBE_KRM_SGMII_CTRL(hw->bus.lan_id),
+                                      IXGBE_SB_IOSF_TARGET_KR_PHY, &sval);
+       if (rc)
+               return rc;
+
+       sval &= ~IXGBE_KRM_SGMII_CTRL_MAC_TAR_FORCE_10_D;
+       sval &= ~IXGBE_KRM_SGMII_CTRL_MAC_TAR_FORCE_100_D;
+       rc = mac->ops.write_iosf_sb_reg(hw,
+                                       IXGBE_KRM_SGMII_CTRL(hw->bus.lan_id),
+                                       IXGBE_SB_IOSF_TARGET_KR_PHY, sval);
+       if (rc)
+               return rc;
+
+       rc = mac->ops.write_iosf_sb_reg(hw,
+                                       IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
+                                       IXGBE_SB_IOSF_TARGET_KR_PHY, lval);
+       if (rc)
+               return rc;
+
+       rc = mac->ops.read_iosf_sb_reg(hw,
+                                   IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id),
+                                   IXGBE_SB_IOSF_TARGET_KR_PHY, &flx_val);
+       if (rc)
+               return rc;
+
+       flx_val &= ~IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_MASK;
+       flx_val |= IXGBE_KRM_PMD_FLX_MASK_ST20_SPEED_AN;
+       flx_val &= ~IXGBE_KRM_PMD_FLX_MASK_ST20_AN_EN;
+       flx_val |= IXGBE_KRM_PMD_FLX_MASK_ST20_SGMII_EN;
+       flx_val |= IXGBE_KRM_PMD_FLX_MASK_ST20_AN37_EN;
+
+       rc = mac->ops.write_iosf_sb_reg(hw,
+                                   IXGBE_KRM_PMD_FLX_MASK_ST20(hw->bus.lan_id),
+                                   IXGBE_SB_IOSF_TARGET_KR_PHY, flx_val);
+       if (rc)
+               return rc;
+
+       ixgbe_restart_an_internal_phy_x550em(hw);
+
+       return hw->phy.ops.setup_link_speed(hw, speed, autoneg_wait);
+}
+
+/**
+ * ixgbe_fc_autoneg_sgmii_x550em_a - Enable flow control IEEE clause 37
+ * @hw: pointer to hardware structure
+ *
+ * Enable flow control according to IEEE clause 37.
+ */
+static void ixgbe_fc_autoneg_sgmii_x550em_a(struct ixgbe_hw *hw)
+{
+       s32 status = IXGBE_ERR_FC_NOT_NEGOTIATED;
+       u32 info[FW_PHY_ACT_DATA_COUNT] = { 0 };
+       ixgbe_link_speed speed;
+       bool link_up;
+
+       /* AN should have completed when the cable was plugged in.
+        * Look for reasons to bail out.  Bail out if:
+        * - FC autoneg is disabled, or if
+        * - link is not up.
+        */
+       if (hw->fc.disable_fc_autoneg)
+               goto out;
+
+       hw->mac.ops.check_link(hw, &speed, &link_up, false);
+       if (!link_up)
+               goto out;
+
+       /* Check if auto-negotiation has completed */
+       status = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_GET_LINK_INFO, &info);
+       if (status || !(info[0] & FW_PHY_ACT_GET_LINK_INFO_AN_COMPLETE)) {
+               status = IXGBE_ERR_FC_NOT_NEGOTIATED;
+               goto out;
+       }
+
+       /* Negotiate the flow control */
+       status = ixgbe_negotiate_fc(hw, info[0], info[0],
+                                   FW_PHY_ACT_GET_LINK_INFO_FC_RX,
+                                   FW_PHY_ACT_GET_LINK_INFO_FC_TX,
+                                   FW_PHY_ACT_GET_LINK_INFO_LP_FC_RX,
+                                   FW_PHY_ACT_GET_LINK_INFO_LP_FC_TX);
+
+out:
+       if (!status) {
+               hw->fc.fc_was_autonegged = true;
+       } else {
+               hw->fc.fc_was_autonegged = false;
+               hw->fc.current_mode = hw->fc.requested_mode;
+       }
+}
+
 /** ixgbe_init_mac_link_ops_X550em_a - Init mac link function pointers
  *  @hw: pointer to hardware structure
  **/
@@ -1806,6 +2090,17 @@ static void ixgbe_init_mac_link_ops_X550em_a(struct ixgbe_hw *hw)
                mac->ops.setup_fc = NULL;
                mac->ops.fc_autoneg = ixgbe_fc_autoneg_fiber_x550em_a;
                break;
+       case ixgbe_media_type_copper:
+               if (hw->device_id != IXGBE_DEV_ID_X550EM_A_1G_T &&
+                   hw->device_id != IXGBE_DEV_ID_X550EM_A_1G_T_L) {
+                       mac->ops.setup_link = ixgbe_setup_mac_link_t_X550em;
+                       break;
+               }
+               mac->ops.fc_autoneg = ixgbe_fc_autoneg_sgmii_x550em_a;
+               mac->ops.setup_fc = ixgbe_fc_autoneg_fw;
+               mac->ops.setup_link = ixgbe_setup_sgmii_fw;
+               mac->ops.check_link = ixgbe_check_mac_link_generic;
+               break;
        case ixgbe_media_type_backplane:
                mac->ops.fc_autoneg = ixgbe_fc_autoneg_backplane_x550em_a;
                mac->ops.setup_fc = ixgbe_setup_fc_backplane_x550em_a;
@@ -1853,7 +2148,7 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw)
                mac->ops.setup_link = ixgbe_setup_mac_link_t_X550em;
                mac->ops.setup_fc = ixgbe_setup_fc_generic;
                mac->ops.check_link = ixgbe_check_link_t_X550em;
-               return;
+               break;
        case ixgbe_media_type_backplane:
                if (hw->device_id == IXGBE_DEV_ID_X550EM_A_SGMII ||
                    hw->device_id == IXGBE_DEV_ID_X550EM_A_SGMII_L)
@@ -1896,6 +2191,12 @@ static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw,
                                              ixgbe_link_speed *speed,
                                              bool *autoneg)
 {
+       if (hw->phy.type == ixgbe_phy_fw) {
+               *autoneg = true;
+               *speed = hw->phy.speeds_supported;
+               return 0;
+       }
+
        /* SFP */
        if (hw->phy.media_type == ixgbe_media_type_fiber) {
                /* CS4227 SFP must not enable auto-negotiation */
@@ -2733,6 +3034,50 @@ static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw)
        return status;
 }
 
+/**
+ * ixgbe_reset_phy_fw - Reset firmware-controlled PHYs
+ * @hw: pointer to hardware structure
+ */
+static s32 ixgbe_reset_phy_fw(struct ixgbe_hw *hw)
+{
+       u32 store[FW_PHY_ACT_DATA_COUNT] = { 0 };
+       s32 rc;
+
+       if (hw->phy.reset_disable || ixgbe_check_reset_blocked(hw))
+               return 0;
+
+       rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_PHY_SW_RESET, &store);
+       if (rc)
+               return rc;
+       memset(store, 0, sizeof(store));
+
+       rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_INIT_PHY, &store);
+       if (rc)
+               return rc;
+
+       return ixgbe_setup_fw_link(hw);
+}
+
+/**
+ * ixgbe_check_overtemp_fw - Check firmware-controlled PHYs for overtemp
+ * @hw: pointer to hardware structure
+ */
+static s32 ixgbe_check_overtemp_fw(struct ixgbe_hw *hw)
+{
+       u32 store[FW_PHY_ACT_DATA_COUNT] = { 0 };
+       s32 rc;
+
+       rc = ixgbe_fw_phy_activity(hw, FW_PHY_ACT_GET_LINK_INFO, &store);
+       if (rc)
+               return rc;
+
+       if (store[0] & FW_PHY_ACT_GET_LINK_INFO_TEMP) {
+               ixgbe_shutdown_fw_phy(hw);
+               return IXGBE_ERR_OVERTEMP;
+       }
+       return 0;
+}
+
 /**
  * ixgbe_read_mng_if_sel_x550em - Read NW_MNG_IF_SEL register
  * @hw: pointer to hardware structure
@@ -2819,6 +3164,10 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
                phy->ops.handle_lasi = ixgbe_handle_lasi_ext_t_x550em;
                phy->ops.reset = ixgbe_reset_phy_t_X550em;
                break;
+       case ixgbe_phy_fw:
+               phy->ops.setup_link = ixgbe_setup_fw_link;
+               phy->ops.reset = ixgbe_reset_phy_fw;
+               break;
        default:
                break;
        }
@@ -2856,6 +3205,8 @@ static enum ixgbe_media_type ixgbe_get_media_type_X550em(struct ixgbe_hw *hw)
        case IXGBE_DEV_ID_X550EM_X_1G_T:
        case IXGBE_DEV_ID_X550EM_X_10G_T:
        case IXGBE_DEV_ID_X550EM_A_10G_T:
+       case IXGBE_DEV_ID_X550EM_A_1G_T:
+       case IXGBE_DEV_ID_X550EM_A_1G_T_L:
                media_type = ixgbe_media_type_copper;
                break;
        default:
@@ -2923,6 +3274,13 @@ static void ixgbe_set_mdio_speed(struct ixgbe_hw *hw)
                hlreg0 &= ~IXGBE_HLREG0_MDCSPD;
                IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg0);
                break;
+       case IXGBE_DEV_ID_X550EM_A_1G_T:
+       case IXGBE_DEV_ID_X550EM_A_1G_T_L:
+               /* Select fast MDIO clock speed for these devices */
+               hlreg0 = IXGBE_READ_REG(hw, IXGBE_HLREG0);
+               hlreg0 |= IXGBE_HLREG0_MDCSPD;
+               IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg0);
+               break;
        default:
                break;
        }
@@ -3434,6 +3792,27 @@ static struct ixgbe_mac_operations mac_ops_x550em_a = {
        .write_iosf_sb_reg      = ixgbe_write_iosf_sb_reg_x550a,
 };
 
+static struct ixgbe_mac_operations mac_ops_x550em_a_fw = {
+       X550_COMMON_MAC
+       .led_on                 = ixgbe_led_on_generic,
+       .led_off                = ixgbe_led_off_generic,
+       .init_led_link_act      = ixgbe_init_led_link_act_generic,
+       .reset_hw               = ixgbe_reset_hw_X550em,
+       .get_media_type         = ixgbe_get_media_type_X550em,
+       .get_san_mac_addr       = NULL,
+       .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_a,
+       .release_swfw_sync      = ixgbe_release_swfw_sync_x550em_a,
+       .setup_fc               = ixgbe_setup_fc_x550em,
+       .fc_autoneg             = ixgbe_fc_autoneg,
+       .read_iosf_sb_reg       = ixgbe_read_iosf_sb_reg_x550a,
+       .write_iosf_sb_reg      = ixgbe_write_iosf_sb_reg_x550a,
+};
+
 #define X550_COMMON_EEP \
        .read                   = &ixgbe_read_ee_hostif_X550, \
        .read_buffer            = &ixgbe_read_ee_hostif_buffer_X550, \
@@ -3463,11 +3842,11 @@ static const struct ixgbe_eeprom_operations eeprom_ops_X550EM_x = {
        .read_i2c_eeprom        = &ixgbe_read_i2c_eeprom_generic, \
        .write_i2c_eeprom       = &ixgbe_write_i2c_eeprom_generic, \
        .setup_link             = &ixgbe_setup_phy_link_generic, \
-       .set_phy_power          = NULL, \
-       .check_overtemp         = &ixgbe_tn_check_overtemp,
+       .set_phy_power          = NULL,
 
 static const struct ixgbe_phy_operations phy_ops_X550 = {
        X550_COMMON_PHY
+       .check_overtemp         = &ixgbe_tn_check_overtemp,
        .init                   = NULL,
        .identify               = &ixgbe_identify_phy_generic,
        .read_reg               = &ixgbe_read_phy_reg_generic,
@@ -3476,6 +3855,7 @@ static const struct ixgbe_phy_operations phy_ops_X550 = {
 
 static const struct ixgbe_phy_operations phy_ops_X550EM_x = {
        X550_COMMON_PHY
+       .check_overtemp         = &ixgbe_tn_check_overtemp,
        .init                   = &ixgbe_init_phy_ops_X550em,
        .identify               = &ixgbe_identify_phy_x550em,
        .read_reg               = &ixgbe_read_phy_reg_generic,
@@ -3484,6 +3864,7 @@ static const struct ixgbe_phy_operations phy_ops_X550EM_x = {
 
 static const struct ixgbe_phy_operations phy_ops_x550em_a = {
        X550_COMMON_PHY
+       .check_overtemp         = &ixgbe_tn_check_overtemp,
        .init                   = &ixgbe_init_phy_ops_X550em,
        .identify               = &ixgbe_identify_phy_x550em,
        .read_reg               = &ixgbe_read_phy_reg_x550a,
@@ -3492,6 +3873,17 @@ static const struct ixgbe_phy_operations phy_ops_x550em_a = {
        .write_reg_mdi          = &ixgbe_write_phy_reg_mdi,
 };
 
+static const struct ixgbe_phy_operations phy_ops_x550em_a_fw = {
+       X550_COMMON_PHY
+       .check_overtemp         = ixgbe_check_overtemp_fw,
+       .init                   = ixgbe_init_phy_ops_X550em,
+       .identify               = ixgbe_identify_phy_fw,
+       .read_reg               = NULL,
+       .write_reg              = NULL,
+       .read_reg_mdi           = NULL,
+       .write_reg_mdi          = NULL,
+};
+
 static const struct ixgbe_link_operations link_ops_x550em_x = {
        .read_link              = &ixgbe_read_i2c_combined_generic,
        .read_link_unlocked     = &ixgbe_read_i2c_combined_generic_unlocked,
@@ -3541,3 +3933,13 @@ const struct ixgbe_info ixgbe_x550em_a_info = {
        .mbx_ops                = &mbx_ops_generic,
        .mvals                  = ixgbe_mvals_x550em_a,
 };
+
+const struct ixgbe_info ixgbe_x550em_a_fw_info = {
+       .mac                    = ixgbe_mac_x550em_a,
+       .get_invariants         = ixgbe_get_invariants_X550_a_fw,
+       .mac_ops                = &mac_ops_x550em_a_fw,
+       .eeprom_ops             = &eeprom_ops_X550EM_x,
+       .phy_ops                = &phy_ops_x550em_a_fw,
+       .mbx_ops                = &mbx_ops_generic,
+       .mvals                  = ixgbe_mvals_x550em_a,
+};