Print this page
6064 ixgbe needs X550 support

@@ -27,16 +27,16 @@
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2013 Saso Kiselkov. All rights reserved.
  * Copyright (c) 2013 OSN Online Service Nuernberg GmbH. All rights reserved.
+ * Copyright 2016 OmniTI Computer Consulting, Inc. All rights reserved.
  */
 
 #include "ixgbe_sw.h"
 
 static char ixgbe_ident[] = "Intel 10Gb Ethernet";
-static char ixgbe_version[] = "ixgbe 1.1.7";
 
 /*
  * Local function protoypes
  */
 static int ixgbe_register_mac(ixgbe_t *);

@@ -63,10 +63,11 @@
 static void ixgbe_setup_rx_ring(ixgbe_rx_ring_t *);
 static void ixgbe_setup_tx_ring(ixgbe_tx_ring_t *);
 static void ixgbe_setup_rss(ixgbe_t *);
 static void ixgbe_setup_vmdq(ixgbe_t *);
 static void ixgbe_setup_vmdq_rss(ixgbe_t *);
+static void ixgbe_setup_rss_table(ixgbe_t *);
 static void ixgbe_init_unicst(ixgbe_t *);
 static int ixgbe_unicst_find(ixgbe_t *, const uint8_t *);
 static void ixgbe_setup_multicst(ixgbe_t *);
 static void ixgbe_get_hw_state(ixgbe_t *);
 static void ixgbe_setup_vmdq_rss_conf(ixgbe_t *ixgbe);

@@ -74,10 +75,11 @@
 static void ixgbe_init_params(ixgbe_t *);
 static int ixgbe_get_prop(ixgbe_t *, char *, int, int, int);
 static void ixgbe_driver_link_check(ixgbe_t *);
 static void ixgbe_sfp_check(void *);
 static void ixgbe_overtemp_check(void *);
+static void ixgbe_phy_check(void *);
 static void ixgbe_link_timer(void *);
 static void ixgbe_local_timer(void *);
 static void ixgbe_arm_watchdog_timer(ixgbe_t *);
 static void ixgbe_restart_watchdog_timer(ixgbe_t *);
 static void ixgbe_disable_adapter_interrupts(ixgbe_t *);

@@ -312,22 +314,46 @@
         200,            /* default interrupt throttle rate */
         64,             /* maximum total msix vectors */
         16,             /* maximum number of ring vectors */
         2,              /* maximum number of other vectors */
         (IXGBE_EICR_LSC
-        | IXGBE_EICR_GPI_SDP1
-        | IXGBE_EICR_GPI_SDP2), /* "other" interrupt types handled */
+        | IXGBE_EICR_GPI_SDP1_X540
+        | IXGBE_EICR_GPI_SDP2_X540), /* "other" interrupt types handled */
 
-        (IXGBE_SDP1_GPIEN
-        | IXGBE_SDP2_GPIEN), /* "other" interrupt types enable mask */
+        (IXGBE_SDP1_GPIEN_X540
+        | IXGBE_SDP2_GPIEN_X540), /* "other" interrupt types enable mask */
 
         (IXGBE_FLAG_DCA_CAPABLE
         | IXGBE_FLAG_RSS_CAPABLE
         | IXGBE_FLAG_VMDQ_CAPABLE
         | IXGBE_FLAG_RSC_CAPABLE) /* capability flags */
 };
 
+static adapter_info_t ixgbe_X550_cap = {
+        128,            /* maximum number of rx queues */
+        1,              /* minimum number of rx queues */
+        128,            /* default number of rx queues */
+        64,             /* maximum number of rx groups */
+        1,              /* minimum number of rx groups */
+        1,              /* default number of rx groups */
+        128,            /* maximum number of tx queues */
+        1,              /* minimum number of tx queues */
+        8,              /* default number of tx queues */
+        15500,          /* maximum MTU size */
+        0xFF8,          /* maximum interrupt throttle rate */
+        0,              /* minimum interrupt throttle rate */
+        0x200,          /* default interrupt throttle rate */
+        64,             /* maximum total msix vectors */
+        16,             /* maximum number of ring vectors */
+        2,              /* maximum number of other vectors */
+        IXGBE_EICR_LSC, /* "other" interrupt types handled */
+        0,              /* "other" interrupt types enable mask */
+        (IXGBE_FLAG_RSS_CAPABLE
+        | IXGBE_FLAG_VMDQ_CAPABLE
+        | IXGBE_FLAG_RSC_CAPABLE) /* capability flags */
+};
+
 /*
  * Module Initialization Functions.
  */
 
 int

@@ -424,11 +450,11 @@
 
         /* Attach the instance pointer to the dev_info data structure */
         ddi_set_driver_private(devinfo, ixgbe);
 
         /*
-         * Initialize for fma support
+         * Initialize for FMA support
          */
         ixgbe->fm_capabilities = ixgbe_get_prop(ixgbe, PROP_FM_CAPABLE,
             0, 0x0f, DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
             DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE);
         ixgbe_fm_init(ixgbe);

@@ -533,10 +559,21 @@
                 goto attach_fail;
         }
         ixgbe->attach_progress |= ATTACH_PROGRESS_OVERTEMP_TASKQ;
 
         /*
+         * Create a taskq for processing external PHY interrupts
+         */
+        (void) sprintf(taskqname, "ixgbe%d_phy_taskq", instance);
+        if ((ixgbe->phy_taskq = ddi_taskq_create(devinfo, taskqname,
+            1, TASKQ_DEFAULTPRI, 0)) == NULL) {
+                ixgbe_error(ixgbe, "phy_taskq create failed");
+                goto attach_fail;
+        }
+        ixgbe->attach_progress |= ATTACH_PROGRESS_PHY_TASKQ;
+
+        /*
          * Initialize driver parameters
          */
         if (ixgbe_init_driver_settings(ixgbe) != IXGBE_SUCCESS) {
                 ixgbe_error(ixgbe, "Failed to initialize driver settings");
                 goto attach_fail;

@@ -567,10 +604,15 @@
                 ddi_fm_service_impact(ixgbe->dip, DDI_SERVICE_LOST);
                 goto attach_fail;
         }
 
         /*
+         * Initialize adapter capabilities
+         */
+        ixgbe_init_params(ixgbe);
+
+        /*
          * Initialize statistics
          */
         if (ixgbe_init_stats(ixgbe) != IXGBE_SUCCESS) {
                 ixgbe_error(ixgbe, "Failed to initialize statistics");
                 goto attach_fail;

@@ -603,11 +645,11 @@
                 ixgbe_error(ixgbe, "Failed to enable DDI interrupts");
                 goto attach_fail;
         }
         ixgbe->attach_progress |= ATTACH_PROGRESS_ENABLE_INTR;
 
-        ixgbe_log(ixgbe, "%s, %s", ixgbe_ident, ixgbe_version);
+        ixgbe_log(ixgbe, "%s", ixgbe_ident);
         atomic_or_32(&ixgbe->ixgbe_state, IXGBE_INITIALIZED);
 
         return (DDI_SUCCESS);
 
 attach_fail:

@@ -786,10 +828,17 @@
         if (ixgbe->attach_progress & ATTACH_PROGRESS_OVERTEMP_TASKQ) {
                 ddi_taskq_destroy(ixgbe->overtemp_taskq);
         }
 
         /*
+         * Remove taskq for external PHYs
+         */
+        if (ixgbe->attach_progress & ATTACH_PROGRESS_PHY_TASKQ) {
+                ddi_taskq_destroy(ixgbe->phy_taskq);
+        }
+
+        /*
          * Remove interrupts
          */
         if (ixgbe->attach_progress & ATTACH_PROGRESS_ALLOC_INTR) {
                 ixgbe_rem_intrs(ixgbe);
         }

@@ -955,10 +1004,29 @@
                  * For now, X540 is all set in its capab structure.
                  * As other X540 variants show up, things can change here.
                  */
                 break;
 
+        case ixgbe_mac_X550:
+        case ixgbe_mac_X550EM_x:
+                IXGBE_DEBUGLOG_0(ixgbe, "identify X550 adapter\n");
+                ixgbe->capab = &ixgbe_X550_cap;
+
+                if (hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP)
+                        ixgbe->capab->flags |= IXGBE_FLAG_SFP_PLUG_CAPABLE;
+
+                /*
+                 * Link detection on X552 SFP+ and X552/X557-AT
+                 */
+                if (hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP ||
+                    hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T) {
+                        ixgbe->capab->other_intr |=
+                            IXGBE_EIMS_GPI_SDP0_BY_MAC(hw);
+                        ixgbe->capab->other_gpie |= IXGBE_SDP0_GPIEN_X540;
+                }
+                break;
+
         default:
                 IXGBE_DEBUGLOG_1(ixgbe,
                     "adapter not supported in ixgbe_identify_hardware(): %d\n",
                     hw->mac.type);
                 return (IXGBE_FAILURE);

@@ -1009,12 +1077,10 @@
         /*
          * Get conf file properties, including link settings
          * jumbo frames, ring number, descriptor number, etc.
          */
         ixgbe_get_conf(ixgbe);
-
-        ixgbe_init_params(ixgbe);
 }
 
 /*
  * ixgbe_init_driver_settings - Initialize driver settings.
  *

@@ -1245,21 +1311,57 @@
 static int
 ixgbe_init(ixgbe_t *ixgbe)
 {
         struct ixgbe_hw *hw = &ixgbe->hw;
         u8 pbanum[IXGBE_PBANUM_LENGTH];
+        int rv;
 
         mutex_enter(&ixgbe->gen_lock);
 
         /*
-         * Reset chipset to put the hardware in a known state
-         * before we try to do anything with the eeprom.
+         * Configure/Initialize hardware
          */
-        if (ixgbe_reset_hw(hw) != IXGBE_SUCCESS) {
+        rv = ixgbe_init_hw(hw);
+        if (rv != IXGBE_SUCCESS) {
+                switch (rv) {
+
+                /*
+                 * The first three errors are not prohibitive to us progressing
+                 * further, and are maily advisory in nature. In the case of a
+                 * SFP module not being present or not deemed supported by the
+                 * common code, we adivse the operator of this fact but carry on
+                 * instead of failing hard, as SFPs can be inserted or replaced
+                 * while the driver is running. In the case of a unknown error,
+                 * we fail-hard, logging the reason and emitting a FMA event.
+                 */
+                case IXGBE_ERR_EEPROM_VERSION:
+                        ixgbe_error(ixgbe,
+                            "This Intel 10Gb Ethernet device is pre-release and"
+                            " contains outdated firmware. Please contact your"
+                            " hardware vendor for a replacement.");
+                        break;
+                case IXGBE_ERR_SFP_NOT_PRESENT:
+                        ixgbe_error(ixgbe,
+                            "No SFP+ module detected on this interface. Please "
+                            "install a supported SFP+ module for this "
+                            "interface to become operational.");
+                        break;
+                case IXGBE_ERR_SFP_NOT_SUPPORTED:
+                        ixgbe_error(ixgbe,
+                            "Unsupported SFP+ module detected. Please replace "
+                            "it with a supported SFP+ module per Intel "
+                            "documentation, or bypass this check with "
+                            "allow_unsupported_sfp=1 in ixgbe.conf.");
+                        break;
+                default:
+                        ixgbe_error(ixgbe,
+                            "Failed to initialize hardware. ixgbe_init_hw "
+                            "returned %d", rv);
                 ixgbe_fm_ereport(ixgbe, DDI_FM_DEVICE_INVAL_STATE);
                 goto init_fail;
         }
+        }
 
         /*
          * Need to init eeprom before validating the checksum.
          */
         if (ixgbe_init_eeprom_params(hw) < 0) {

@@ -1295,10 +1397,15 @@
         hw->fc.low_water[0] = DEFAULT_FCRTL;
         hw->fc.pause_time = DEFAULT_FCPAUSE;
         hw->fc.send_xon = B_TRUE;
 
         /*
+         * Initialize flow control
+         */
+        (void) ixgbe_start_hw(hw);
+
+        /*
          * Initialize link settings
          */
         (void) ixgbe_driver_setup_link(ixgbe, B_FALSE);
 
         /*

@@ -1342,11 +1449,11 @@
  */
 static int
 ixgbe_chip_start(ixgbe_t *ixgbe)
 {
         struct ixgbe_hw *hw = &ixgbe->hw;
-        int ret_val, i;
+        int i;
 
         ASSERT(mutex_owned(&ixgbe->gen_lock));
 
         /*
          * Get the mac address

@@ -1365,26 +1472,10 @@
                 ixgbe_error(ixgbe, "Invalid mac address");
                 return (IXGBE_FAILURE);
         }
 
         /*
-         * Configure/Initialize hardware
-         */
-        ret_val = ixgbe_init_hw(hw);
-        if (ret_val != IXGBE_SUCCESS) {
-                if (ret_val == IXGBE_ERR_EEPROM_VERSION) {
-                        ixgbe_error(ixgbe,
-                            "This 82599 device is pre-release and contains"
-                            " outdated firmware, please contact your hardware"
-                            " vendor for a replacement.");
-                } else {
-                        ixgbe_error(ixgbe, "Failed to initialize hardware");
-                        return (IXGBE_FAILURE);
-                }
-        }
-
-        /*
          * Re-enable relaxed ordering for performance.  It is disabled
          * by default in the hardware init.
          */
         if (ixgbe->relax_order_enable == B_TRUE)
                 ixgbe_enable_relaxed_ordering(hw);

@@ -1410,12 +1501,38 @@
         for (i = 0; i < ixgbe->intr_cnt; i++) {
                 IXGBE_WRITE_REG(hw, IXGBE_EITR(i), ixgbe->intr_throttling[i]);
         }
 
         /*
-         * Save the state of the phy
+         * Disable Wake-on-LAN
          */
+        IXGBE_WRITE_REG(hw, IXGBE_WUC, 0);
+
+        /*
+         * Some adapters offer Energy Efficient Ethernet (EEE) support.
+         * Due to issues with EEE in e1000g/igb, we disable this by default
+         * as a precautionary measure.
+         *
+         * Currently, the only known adapter which supports EEE in the ixgbe
+         * line is 8086,15AB (IXGBE_DEV_ID_X550EM_X_KR), and only after the
+         * first revision of it, as well as any X550 with MAC type 6 (non-EM)
+         */
+        (void) ixgbe_setup_eee(hw, B_FALSE);
+
+        /*
+         * Turn on any present SFP Tx laser
+         */
+        ixgbe_enable_tx_laser(hw);
+
+        /*
+         * Power on the PHY
+         */
+        (void) ixgbe_set_phy_power(hw, B_TRUE);
+
+        /*
+         * Save the state of the PHY
+         */
         ixgbe_get_hw_state(ixgbe);
 
         /*
          * Make sure driver has control
          */

@@ -1429,17 +1546,19 @@
  */
 static void
 ixgbe_chip_stop(ixgbe_t *ixgbe)
 {
         struct ixgbe_hw *hw = &ixgbe->hw;
+        int rv;
 
         ASSERT(mutex_owned(&ixgbe->gen_lock));
 
         /*
-         * Tell firmware driver is no longer in control
+         * Stop interupt generation and disable Tx unit
          */
-        ixgbe_release_driver_control(hw);
+        hw->adapter_stopped = B_FALSE;
+        (void) ixgbe_stop_adapter(hw);
 
         /*
          * Reset the chipset
          */
         (void) ixgbe_reset_hw(hw);

@@ -1446,10 +1565,36 @@
 
         /*
          * Reset PHY
          */
         (void) ixgbe_reset_phy(hw);
+
+        /*
+         * Enter LPLU (Low Power, Link Up) mode, if available. Avoid resetting
+         * the PHY while doing so. Else, just power down the PHY.
+         */
+        if (hw->phy.ops.enter_lplu != NULL) {
+                hw->phy.reset_disable = B_TRUE;
+                rv = hw->phy.ops.enter_lplu(hw);
+                if (rv != IXGBE_SUCCESS)
+                        ixgbe_error(ixgbe, "Error while entering LPLU: %d", rv);
+                hw->phy.reset_disable = B_FALSE;
+        } else {
+                (void) ixgbe_set_phy_power(hw, B_FALSE);
+        }
+
+        /*
+         * Turn off any present SFP Tx laser
+         * Expected for health and safety reasons
+         */
+        ixgbe_disable_tx_laser(hw);
+
+        /*
+         * Tell firmware driver is no longer in control
+         */
+        ixgbe_release_driver_control(hw);
+
 }
 
 /*
  * ixgbe_reset - Reset the chipset and re-start the driver.
  *

@@ -1647,10 +1792,11 @@
  * ixgbe_start - Start the driver/chipset.
  */
 int
 ixgbe_start(ixgbe_t *ixgbe, boolean_t alloc_buffer)
 {
+        struct ixgbe_hw *hw = &ixgbe->hw;
         int i;
 
         ASSERT(mutex_owned(&ixgbe->gen_lock));
 
         if (alloc_buffer) {

@@ -1682,10 +1828,28 @@
         if (ixgbe_chip_start(ixgbe) != IXGBE_SUCCESS) {
                 ixgbe_fm_ereport(ixgbe, DDI_FM_DEVICE_INVAL_STATE);
                 goto start_failure;
         }
 
+        /*
+         * Configure link now for X550
+         *
+         * X550 possesses a LPLU (Low-Power Link Up) mode which keeps the
+         * resting state of the adapter at a 1Gb FDX speed. Prior to the X550,
+         * the resting state of the link would be the maximum speed that
+         * autonegotiation will allow (usually 10Gb, infrastructure allowing)
+         * so we never bothered with explicitly setting the link to 10Gb as it
+         * would already be at that state on driver attach. With X550, we must
+         * trigger a re-negotiation of the link in order to switch from a LPLU
+         * 1Gb link to 10Gb (cable and link partner permitting.)
+         */
+        if (hw->mac.type == ixgbe_mac_X550 ||
+            hw->mac.type == ixgbe_mac_X550EM_x) {
+                (void) ixgbe_driver_setup_link(ixgbe, B_TRUE);
+                ixgbe_get_hw_state(ixgbe);
+        }
+
         if (ixgbe_check_acc_handle(ixgbe->osdep.reg_handle) != DDI_FM_OK) {
                 goto start_failure;
         }
 
         /*

@@ -2189,18 +2353,20 @@
          * WTHRESH defaults to 1 (writeback each descriptor)
          */
         reg_val = IXGBE_READ_REG(hw, IXGBE_RXDCTL(rx_ring->hw_index));
         reg_val |= IXGBE_RXDCTL_ENABLE; /* enable queue */
 
-        /* Not a valid value for 82599 or X540 */
+        /* Not a valid value for 82599, X540 or X550 */
         if (hw->mac.type == ixgbe_mac_82598EB) {
                 reg_val |= 0x0020;      /* pthresh */
         }
         IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(rx_ring->hw_index), reg_val);
 
         if (hw->mac.type == ixgbe_mac_82599EB ||
-            hw->mac.type == ixgbe_mac_X540) {
+            hw->mac.type == ixgbe_mac_X540 ||
+            hw->mac.type == ixgbe_mac_X550 ||
+            hw->mac.type == ixgbe_mac_X550EM_x) {
                 reg_val = IXGBE_READ_REG(hw, IXGBE_RDRXCTL);
                 reg_val |= (IXGBE_RDRXCTL_CRCSTRIP | IXGBE_RDRXCTL_AGGDIS);
                 IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, reg_val);
         }
 

@@ -2222,10 +2388,16 @@
         uint32_t reg_val;
         uint32_t ring_mapping;
         uint32_t i, index;
         uint32_t psrtype_rss_bit;
 
+        /*
+         * Ensure that Rx is disabled while setting up
+         * the Rx unit and Rx descriptor ring(s)
+         */
+        ixgbe_disable_rx(hw);
+
         /* PSRTYPE must be configured for 82599 */
         if (ixgbe->classify_mode != IXGBE_CLASSIFY_VMDQ &&
             ixgbe->classify_mode != IXGBE_CLASSIFY_VMDQ_RSS) {
                 reg_val = IXGBE_PSRTYPE_TCPHDR | IXGBE_PSRTYPE_UDPHDR |
                     IXGBE_PSRTYPE_IPV4HDR | IXGBE_PSRTYPE_IPV6HDR;

@@ -2246,24 +2418,28 @@
                         IXGBE_WRITE_REG(hw, IXGBE_PSRTYPE(i), reg_val);
                 }
         }
 
         /*
-         * Set filter control in FCTRL to accept broadcast packets and do
-         * not pass pause frames to host.  Flow control settings are already
-         * in this register, so preserve them.
+         * Set filter control in FCTRL to determine types of packets are passed
+         * up to the driver.
+         * - Pass broadcast packets.
+         * - Do not pass flow control pause frames (82598-specific)
          */
         reg_val = IXGBE_READ_REG(hw, IXGBE_FCTRL);
-        reg_val |= IXGBE_FCTRL_BAM;     /* broadcast accept mode */
-        reg_val |= IXGBE_FCTRL_DPF;     /* discard pause frames */
+        reg_val |= IXGBE_FCTRL_BAM; /* Broadcast Accept Mode */
+        if (hw->mac.type == ixgbe_mac_82598EB) {
+                reg_val |= IXGBE_FCTRL_DPF; /* Discard Pause Frames */
+        }
         IXGBE_WRITE_REG(hw, IXGBE_FCTRL, reg_val);
 
         /*
          * Hardware checksum settings
          */
         if (ixgbe->rx_hcksum_enable) {
-                reg_val = IXGBE_RXCSUM_IPPCSE;  /* IP checksum */
+                reg_val = IXGBE_READ_REG(hw, IXGBE_RXCSUM);
+                reg_val |= IXGBE_RXCSUM_IPPCSE; /* IP checksum */
                 IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, reg_val);
         }
 
         /*
          * Setup VMDq and RSS for multiple receive queues

@@ -2297,15 +2473,18 @@
                 break;
         }
 
         /*
          * Enable the receive unit.  This must be done after filter
-         * control is set in FCTRL.
+         * control is set in FCTRL. On 82598, we disable the descriptor monitor.
+         * 82598 is the only adapter which defines this RXCTRL option.
          */
-        reg_val = (IXGBE_RXCTRL_RXEN    /* Enable Receive Unit */
-            | IXGBE_RXCTRL_DMBYPS);     /* descriptor monitor bypass */
-        IXGBE_WRITE_REG(hw, IXGBE_RXCTRL, reg_val);
+        reg_val = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
+        if (hw->mac.type == ixgbe_mac_82598EB)
+                reg_val |= IXGBE_RXCTRL_DMBYPS; /* descriptor monitor bypass */
+        reg_val |= IXGBE_RXCTRL_RXEN;
+        (void) ixgbe_enable_rx_dma(hw, reg_val);
 
         /*
          * ixgbe_setup_rx_ring must be called after configuring RXCTRL
          */
         for (i = 0; i < ixgbe->num_rx_rings; i++) {

@@ -2328,22 +2507,25 @@
          * The Max Frame Size in MHADD/MAXFRS will be internally increased
          * by four bytes if the packet has a VLAN field, so includes MTU,
          * ethernet header and frame check sequence.
          * Register is MAXFRS in 82599.
          */
-        reg_val = (ixgbe->default_mtu + sizeof (struct ether_header)
+        reg_val = IXGBE_READ_REG(hw, IXGBE_MHADD);
+        reg_val &= ~IXGBE_MHADD_MFS_MASK;
+        reg_val |= (ixgbe->default_mtu + sizeof (struct ether_header)
             + ETHERFCSL) << IXGBE_MHADD_MFS_SHIFT;
         IXGBE_WRITE_REG(hw, IXGBE_MHADD, reg_val);
 
         /*
          * Setup Jumbo Frame enable bit
          */
-        if (ixgbe->default_mtu > ETHERMTU) {
                 reg_val = IXGBE_READ_REG(hw, IXGBE_HLREG0);
+        if (ixgbe->default_mtu > ETHERMTU)
                 reg_val |= IXGBE_HLREG0_JUMBOEN;
+        else
+                reg_val &= ~IXGBE_HLREG0_JUMBOEN;
                 IXGBE_WRITE_REG(hw, IXGBE_HLREG0, reg_val);
-        }
 
         /*
          * Setup RSC for multiple receive queues.
          */
         if (ixgbe->lro_enable) {

@@ -2494,10 +2676,12 @@
                                     ring_mapping);
                                 break;
 
                         case ixgbe_mac_82599EB:
                         case ixgbe_mac_X540:
+                        case ixgbe_mac_X550:
+                        case ixgbe_mac_X550EM_x:
                                 IXGBE_WRITE_REG(hw, IXGBE_TQSM(i >> 2),
                                     ring_mapping);
                                 break;
 
                         default:

@@ -2513,10 +2697,12 @@
                         IXGBE_WRITE_REG(hw, IXGBE_TQSMR(i >> 2), ring_mapping);
                         break;
 
                 case ixgbe_mac_82599EB:
                 case ixgbe_mac_X540:
+                case ixgbe_mac_X550:
+                case ixgbe_mac_X550EM_x:
                         IXGBE_WRITE_REG(hw, IXGBE_TQSM(i >> 2), ring_mapping);
                         break;
 
                 default:
                         break;

@@ -2529,14 +2715,16 @@
         reg_val = IXGBE_READ_REG(hw, IXGBE_HLREG0);
         reg_val |= IXGBE_HLREG0_TXCRCEN | IXGBE_HLREG0_TXPADEN;
         IXGBE_WRITE_REG(hw, IXGBE_HLREG0, reg_val);
 
         /*
-         * enable DMA for 82599 and X540 parts
+         * enable DMA for 82599, X540 and X550 parts
          */
         if (hw->mac.type == ixgbe_mac_82599EB ||
-            hw->mac.type == ixgbe_mac_X540) {
+            hw->mac.type == ixgbe_mac_X540 ||
+            hw->mac.type == ixgbe_mac_X550 ||
+            hw->mac.type == ixgbe_mac_X550EM_x) {
                 /* DMATXCTL.TE must be set after all Tx config is complete */
                 reg_val = IXGBE_READ_REG(hw, IXGBE_DMATXCTL);
                 reg_val |= IXGBE_DMATXCTL_TE;
                 IXGBE_WRITE_REG(hw, IXGBE_DMATXCTL, reg_val);
 

@@ -2566,38 +2754,18 @@
  */
 static void
 ixgbe_setup_rss(ixgbe_t *ixgbe)
 {
         struct ixgbe_hw *hw = &ixgbe->hw;
-        uint32_t i, mrqc, rxcsum;
-        uint32_t random;
-        uint32_t reta;
-        uint32_t ring_per_group;
+        uint32_t mrqc;
 
         /*
-         * Fill out redirection table
+         * Initialize RETA/ERETA table
          */
-        reta = 0;
-        ring_per_group = ixgbe->num_rx_rings / ixgbe->num_rx_groups;
+        ixgbe_setup_rss_table(ixgbe);
 
-        for (i = 0; i < 128; i++) {
-                reta = (reta << 8) | (i % ring_per_group) |
-                    ((i % ring_per_group) << 4);
-                if ((i & 3) == 3)
-                        IXGBE_WRITE_REG(hw, IXGBE_RETA(i >> 2), reta);
-        }
-
         /*
-         * Fill out hash function seeds with a random constant
-         */
-        for (i = 0; i < 10; i++) {
-                (void) random_get_pseudo_bytes((uint8_t *)&random,
-                    sizeof (uint32_t));
-                IXGBE_WRITE_REG(hw, IXGBE_RSSRK(i), random);
-        }
-
-        /*
          * Enable RSS & perform hash on these packet types
          */
         mrqc = IXGBE_MRQC_RSSEN |
             IXGBE_MRQC_RSS_FIELD_IPV4 |
             IXGBE_MRQC_RSS_FIELD_IPV4_TCP |

@@ -2607,20 +2775,10 @@
             IXGBE_MRQC_RSS_FIELD_IPV6 |
             IXGBE_MRQC_RSS_FIELD_IPV6_TCP |
             IXGBE_MRQC_RSS_FIELD_IPV6_UDP |
             IXGBE_MRQC_RSS_FIELD_IPV6_EX_UDP;
         IXGBE_WRITE_REG(hw, IXGBE_MRQC, mrqc);
-
-        /*
-         * Disable Packet Checksum to enable RSS for multiple receive queues.
-         * It is an adapter hardware limitation that Packet Checksum is
-         * mutually exclusive with RSS.
-         */
-        rxcsum = IXGBE_READ_REG(hw, IXGBE_RXCSUM);
-        rxcsum |= IXGBE_RXCSUM_PCSD;
-        rxcsum &= ~IXGBE_RXCSUM_IPPCSE;
-        IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, rxcsum);
 }
 
 /*
  * ixgbe_setup_vmdq - Setup MAC classification feature
  */

@@ -2645,10 +2803,12 @@
                 IXGBE_WRITE_REG(hw, IXGBE_VMD_CTL, vmdctl);
                 break;
 
         case ixgbe_mac_82599EB:
         case ixgbe_mac_X540:
+        case ixgbe_mac_X550:
+        case ixgbe_mac_X550EM_x:
                 /*
                  * Enable VMDq-only.
                  */
                 vmdctl = IXGBE_MRQC_VMDQEN;
                 IXGBE_WRITE_REG(hw, IXGBE_MRQC, vmdctl);

@@ -2681,38 +2841,19 @@
  */
 static void
 ixgbe_setup_vmdq_rss(ixgbe_t *ixgbe)
 {
         struct ixgbe_hw *hw = &ixgbe->hw;
-        uint32_t i, mrqc, rxcsum;
-        uint32_t random;
-        uint32_t reta;
-        uint32_t ring_per_group;
-        uint32_t vmdctl, vtctl;
+        uint32_t i, mrqc;
+        uint32_t vtctl, vmdctl;
 
         /*
-         * Fill out redirection table
+         * Initialize RETA/ERETA table
          */
-        reta = 0;
-        ring_per_group = ixgbe->num_rx_rings / ixgbe->num_rx_groups;
-        for (i = 0; i < 128; i++) {
-                reta = (reta << 8) | (i % ring_per_group) |
-                    ((i % ring_per_group) << 4);
-                if ((i & 3) == 3)
-                        IXGBE_WRITE_REG(hw, IXGBE_RETA(i >> 2), reta);
-        }
+        ixgbe_setup_rss_table(ixgbe);
 
         /*
-         * Fill out hash function seeds with a random constant
-         */
-        for (i = 0; i < 10; i++) {
-                (void) random_get_pseudo_bytes((uint8_t *)&random,
-                    sizeof (uint32_t));
-                IXGBE_WRITE_REG(hw, IXGBE_RSSRK(i), random);
-        }
-
-        /*
          * Enable and setup RSS and VMDq
          */
         switch (hw->mac.type) {
         case ixgbe_mac_82598EB:
                 /*

@@ -2739,10 +2880,12 @@
                 IXGBE_WRITE_REG(hw, IXGBE_VMD_CTL, vmdctl);
                 break;
 
         case ixgbe_mac_82599EB:
         case ixgbe_mac_X540:
+        case ixgbe_mac_X550:
+        case ixgbe_mac_X550EM_x:
                 /*
                  * Enable RSS & Setup RSS Hash functions
                  */
                 mrqc = IXGBE_MRQC_RSS_FIELD_IPV4 |
                     IXGBE_MRQC_RSS_FIELD_IPV4_TCP |

@@ -2774,22 +2917,14 @@
         default:
                 break;
 
         }
 
-        /*
-         * Disable Packet Checksum to enable RSS for multiple receive queues.
-         * It is an adapter hardware limitation that Packet Checksum is
-         * mutually exclusive with RSS.
-         */
-        rxcsum = IXGBE_READ_REG(hw, IXGBE_RXCSUM);
-        rxcsum |= IXGBE_RXCSUM_PCSD;
-        rxcsum &= ~IXGBE_RXCSUM_IPPCSE;
-        IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, rxcsum);
-
         if (hw->mac.type == ixgbe_mac_82599EB ||
-            hw->mac.type == ixgbe_mac_X540) {
+            hw->mac.type == ixgbe_mac_X540 ||
+            hw->mac.type == ixgbe_mac_X550 ||
+            hw->mac.type == ixgbe_mac_X550EM_x) {
                 /*
                  * Enable Virtualization and Replication.
                  */
                 vtctl = IXGBE_VT_CTL_VT_ENABLE | IXGBE_VT_CTL_REPLEN;
                 IXGBE_WRITE_REG(hw, IXGBE_VT_CTL, vtctl);

@@ -2801,10 +2936,103 @@
                 IXGBE_WRITE_REG(hw, IXGBE_VFRE(1), IXGBE_VFRE_ENABLE_ALL);
         }
 }
 
 /*
+ * ixgbe_setup_rss_table - Setup RSS table
+ */
+static void
+ixgbe_setup_rss_table(ixgbe_t *ixgbe)
+{
+        struct ixgbe_hw *hw = &ixgbe->hw;
+        uint32_t i, j;
+        uint32_t random;
+        uint32_t reta;
+        uint32_t ring_per_group;
+        uint32_t ring;
+        uint32_t table_size;
+        uint32_t index_mult;
+        uint32_t rxcsum;
+
+        /*
+         * Set multiplier for RETA setup and table size based on MAC type.
+         * RETA table sizes vary by model:
+         *
+         * 82598, 82599, X540: 128 table entries.
+         * X550: 512 table entries.
+         */
+        index_mult = 0x1;
+        table_size = 128;
+        switch (ixgbe->hw.mac.type) {
+        case ixgbe_mac_82598EB:
+                index_mult = 0x11;
+                break;
+        case ixgbe_mac_X550:
+        case ixgbe_mac_X550EM_x:
+                table_size = 512;
+                break;
+        default:
+                break;
+        }
+
+        /*
+         * Fill out RSS redirection table. The configuation of the indices is
+         * hardware-dependent.
+         *
+         *  82598: 8 bits wide containing two 4 bit RSS indices
+         *  82599, X540: 8 bits wide containing one 4 bit RSS index
+         *  X550: 8 bits wide containing one 6 bit RSS index
+         */
+        reta = 0;
+        ring_per_group = ixgbe->num_rx_rings / ixgbe->num_rx_groups;
+
+        for (i = 0, j = 0; i < table_size; i++, j++) {
+                if (j == ring_per_group) j = 0;
+
+                /*
+                 * The low 8 bits are for hash value (n+0);
+                 * The next 8 bits are for hash value (n+1), etc.
+                 */
+                ring = (j * index_mult);
+                reta = reta >> 8;
+                reta = reta | (((uint32_t)ring) << 24);
+
+                if ((i & 3) == 3)
+                        /*
+                         * The first 128 table entries are programmed into the
+                         * RETA register, with any beyond that (eg; on X550)
+                         * into ERETA.
+                         */
+                        if (i < 128)
+                                IXGBE_WRITE_REG(hw, IXGBE_RETA(i >> 2), reta);
+                        else
+                                IXGBE_WRITE_REG(hw, IXGBE_ERETA((i >> 2) - 32),
+                                    reta);
+                        reta = 0;
+        }
+
+        /*
+         * Fill out hash function seeds with a random constant
+         */
+        for (i = 0; i < 10; i++) {
+                (void) random_get_pseudo_bytes((uint8_t *)&random,
+                    sizeof (uint32_t));
+                IXGBE_WRITE_REG(hw, IXGBE_RSSRK(i), random);
+        }
+
+        /*
+         * Disable Packet Checksum to enable RSS for multiple receive queues.
+         * It is an adapter hardware limitation that Packet Checksum is
+         * mutually exclusive with RSS.
+         */
+        rxcsum = IXGBE_READ_REG(hw, IXGBE_RXCSUM);
+        rxcsum |= IXGBE_RXCSUM_PCSD;
+        rxcsum &= ~IXGBE_RXCSUM_IPPCSE;
+        IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, rxcsum);
+}
+
+/*
  * ixgbe_init_unicst - Initialize the unicast addresses.
  */
 static void
 ixgbe_init_unicst(ixgbe_t *ixgbe)
 {

@@ -2997,20 +3225,22 @@
 
                 break;
 
         case ixgbe_mac_82599EB:
         case ixgbe_mac_X540:
+        case ixgbe_mac_X550:
+        case ixgbe_mac_X550EM_x:
                 /*
                  * 82599 supports the following combination:
                  * vmdq no. x rss no.
                  * [33..64] x [1..2]
                  * [2..32]  x [1..4]
                  * 1 x [1..16]
                  * However 8 rss queue per pool (vmdq) is sufficient for
                  * most cases.
                  *
-                 * For now, treat X540 like the 82599.
+                 * For now, treat X540 and X550 like the 82599.
                  */
                 ring_per_group = ixgbe->num_rx_rings / ixgbe->num_rx_groups;
                 if (ixgbe->num_rx_groups == 1) {
                         ixgbe->num_rx_rings = min(8, ring_per_group);
                 } else if (ixgbe->num_rx_groups <= 32) {

@@ -3169,13 +3399,15 @@
         ixgbe->tx_head_wb_enable = ixgbe_get_prop(ixgbe, PROP_TX_HEAD_WB_ENABLE,
             0, 1, DEFAULT_TX_HEAD_WB_ENABLE);
         ixgbe->relax_order_enable = ixgbe_get_prop(ixgbe,
             PROP_RELAX_ORDER_ENABLE, 0, 1, DEFAULT_RELAX_ORDER_ENABLE);
 
-        /* Head Write Back not recommended for 82599 and X540 */
+        /* Head Write Back not recommended for 82599, X540 and X550 */
         if (hw->mac.type == ixgbe_mac_82599EB ||
-            hw->mac.type == ixgbe_mac_X540) {
+            hw->mac.type == ixgbe_mac_X540 ||
+            hw->mac.type == ixgbe_mac_X550 ||
+            hw->mac.type == ixgbe_mac_X550EM_x) {
                 ixgbe->tx_head_wb_enable = B_FALSE;
         }
 
         /*
          * ixgbe LSO needs the tx h/w checksum support.

@@ -3194,11 +3426,11 @@
         if (ixgbe->rx_hcksum_enable == B_FALSE) {
                 ixgbe->lro_enable = B_FALSE;
         }
 
         /*
-         * ixgbe LRO only been supported by 82599 and X540 now
+         * ixgbe LRO only supported by 82599, X540 and X550
          */
         if (hw->mac.type == ixgbe_mac_82598EB) {
                 ixgbe->lro_enable = B_FALSE;
         }
         ixgbe->tx_copy_thresh = ixgbe_get_prop(ixgbe, PROP_TX_COPY_THRESHOLD,

@@ -3224,30 +3456,101 @@
         ixgbe->intr_throttling[0] = ixgbe_get_prop(ixgbe, PROP_INTR_THROTTLING,
             ixgbe->capab->min_intr_throttle,
             ixgbe->capab->max_intr_throttle,
             ixgbe->capab->def_intr_throttle);
         /*
-         * 82599 and X540 require the interrupt throttling rate is
-         * a multiple of 8. This is enforced by the register
-         * definiton.
+         * 82599, X540 and X550 require the interrupt throttling rate is
+         * a multiple of 8. This is enforced by the register definiton.
          */
-        if (hw->mac.type == ixgbe_mac_82599EB || hw->mac.type == ixgbe_mac_X540)
+        if (hw->mac.type == ixgbe_mac_82599EB ||
+            hw->mac.type == ixgbe_mac_X540 ||
+            hw->mac.type == ixgbe_mac_X550 ||
+            hw->mac.type == ixgbe_mac_X550EM_x)
                 ixgbe->intr_throttling[0] = ixgbe->intr_throttling[0] & 0xFF8;
 
         hw->allow_unsupported_sfp = ixgbe_get_prop(ixgbe,
             PROP_ALLOW_UNSUPPORTED_SFP, 0, 1, DEFAULT_ALLOW_UNSUPPORTED_SFP);
 }
 
 static void
 ixgbe_init_params(ixgbe_t *ixgbe)
 {
+        struct ixgbe_hw *hw = &ixgbe->hw;
+        ixgbe_link_speed speeds_supported = 0;
+        boolean_t negotiate;
+
+        /*
+         * Get a list of speeds the adapter supports. If the hw struct hasn't
+         * been populated with this information yet, retrieve it from the
+         * adapter and save it to our own variable.
+         *
+         * On certain adapters, such as ones which use SFPs, the contents of
+         * hw->phy.speeds_supported (and hw->phy.autoneg_advertised) are not
+         * updated, so we must rely on calling ixgbe_get_link_capabilities()
+         * in order to ascertain the speeds which we are capable of supporting,
+         * and in the case of SFP-equipped adapters, which speed we are
+         * advertising. If ixgbe_get_link_capabilities() fails for some reason,
+         * we'll go with a default list of speeds as a last resort.
+         */
+        speeds_supported = hw->phy.speeds_supported;
+
+        if (speeds_supported == 0) {
+                if (ixgbe_get_link_capabilities(hw, &speeds_supported,
+                    &negotiate) != IXGBE_SUCCESS) {
+                        if (hw->mac.type == ixgbe_mac_82598EB) {
+                                speeds_supported =
+                                    IXGBE_LINK_SPEED_82598_AUTONEG;
+                        } else {
+                                speeds_supported =
+                                    IXGBE_LINK_SPEED_82599_AUTONEG;
+                        }
+                }
+        }
+        ixgbe->speeds_supported = speeds_supported;
+
+        /*
+         * By default, all supported speeds are enabled and advertised.
+         */
+        if (speeds_supported & IXGBE_LINK_SPEED_10GB_FULL) {
         ixgbe->param_en_10000fdx_cap = 1;
-        ixgbe->param_en_1000fdx_cap = 1;
-        ixgbe->param_en_100fdx_cap = 1;
         ixgbe->param_adv_10000fdx_cap = 1;
+        } else {
+                ixgbe->param_en_10000fdx_cap = 0;
+                ixgbe->param_adv_10000fdx_cap = 0;
+        }
+
+        if (speeds_supported & IXGBE_LINK_SPEED_5GB_FULL) {
+                ixgbe->param_en_5000fdx_cap = 1;
+                ixgbe->param_adv_5000fdx_cap = 1;
+        } else {
+                ixgbe->param_en_5000fdx_cap = 0;
+                ixgbe->param_adv_5000fdx_cap = 0;
+        }
+
+        if (speeds_supported & IXGBE_LINK_SPEED_2_5GB_FULL) {
+                ixgbe->param_en_2500fdx_cap = 1;
+                ixgbe->param_adv_2500fdx_cap = 1;
+        } else {
+                ixgbe->param_en_2500fdx_cap = 0;
+                ixgbe->param_adv_2500fdx_cap = 0;
+        }
+
+        if (speeds_supported & IXGBE_LINK_SPEED_1GB_FULL) {
+                ixgbe->param_en_1000fdx_cap = 1;
         ixgbe->param_adv_1000fdx_cap = 1;
+        } else {
+                ixgbe->param_en_1000fdx_cap = 0;
+                ixgbe->param_adv_1000fdx_cap = 0;
+        }
+
+        if (speeds_supported & IXGBE_LINK_SPEED_100_FULL) {
+                ixgbe->param_en_100fdx_cap = 1;
         ixgbe->param_adv_100fdx_cap = 1;
+        } else {
+                ixgbe->param_en_100fdx_cap = 0;
+                ixgbe->param_adv_100fdx_cap = 0;
+        }
 
         ixgbe->param_pause_cap = 1;
         ixgbe->param_asym_pause_cap = 1;
         ixgbe->param_rem_fault = 0;
 

@@ -3255,10 +3558,12 @@
         ixgbe->param_adv_pause_cap = 1;
         ixgbe->param_adv_asym_pause_cap = 1;
         ixgbe->param_adv_rem_fault = 0;
 
         ixgbe->param_lp_10000fdx_cap = 0;
+        ixgbe->param_lp_5000fdx_cap = 0;
+        ixgbe->param_lp_2500fdx_cap = 0;
         ixgbe->param_lp_1000fdx_cap = 0;
         ixgbe->param_lp_100fdx_cap = 0;
         ixgbe->param_lp_autoneg_cap = 0;
         ixgbe->param_lp_pause_cap = 0;
         ixgbe->param_lp_asym_pause_cap = 0;

@@ -3302,36 +3607,47 @@
  * ixgbe_driver_setup_link - Using the link properties to setup the link.
  */
 int
 ixgbe_driver_setup_link(ixgbe_t *ixgbe, boolean_t setup_hw)
 {
-        u32 autoneg_advertised = 0;
+        struct ixgbe_hw *hw = &ixgbe->hw;
+        ixgbe_link_speed advertised = 0;
 
         /*
-         * No half duplex support with 10Gb parts
+         * Assemble a list of enabled speeds to auto-negotiate with.
          */
-        if (ixgbe->param_adv_10000fdx_cap == 1)
-                autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL;
+        if (ixgbe->param_en_10000fdx_cap == 1)
+                advertised |= IXGBE_LINK_SPEED_10GB_FULL;
 
-        if (ixgbe->param_adv_1000fdx_cap == 1)
-                autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL;
+        if (ixgbe->param_en_5000fdx_cap == 1)
+                advertised |= IXGBE_LINK_SPEED_5GB_FULL;
 
-        if (ixgbe->param_adv_100fdx_cap == 1)
-                autoneg_advertised |= IXGBE_LINK_SPEED_100_FULL;
+        if (ixgbe->param_en_2500fdx_cap == 1)
+                advertised |= IXGBE_LINK_SPEED_2_5GB_FULL;
 
-        if (ixgbe->param_adv_autoneg_cap == 1 && autoneg_advertised == 0) {
-                ixgbe_notice(ixgbe, "Invalid link settings. Setup link "
-                    "to autonegotiation with full link capabilities.");
+        if (ixgbe->param_en_1000fdx_cap == 1)
+                advertised |= IXGBE_LINK_SPEED_1GB_FULL;
 
-                autoneg_advertised = IXGBE_LINK_SPEED_10GB_FULL |
-                    IXGBE_LINK_SPEED_1GB_FULL |
-                    IXGBE_LINK_SPEED_100_FULL;
+        if (ixgbe->param_en_100fdx_cap == 1)
+                advertised |= IXGBE_LINK_SPEED_100_FULL;
+
+        /*
+         * As a last resort, autoneg with a default list of speeds.
+         */
+        if (ixgbe->param_adv_autoneg_cap == 1 && advertised == 0) {
+                ixgbe_notice(ixgbe, "Invalid link settings. Setting link "
+                    "to autonegotiate with full capabilities.");
+
+                if (hw->mac.type == ixgbe_mac_82598EB)
+                        advertised = IXGBE_LINK_SPEED_82598_AUTONEG;
+                else
+                        advertised = IXGBE_LINK_SPEED_82599_AUTONEG;
         }
 
         if (setup_hw) {
-                if (ixgbe_setup_link(&ixgbe->hw, autoneg_advertised,
-                    ixgbe->param_adv_autoneg_cap, B_TRUE) != IXGBE_SUCCESS) {
+                if (ixgbe_setup_link(&ixgbe->hw, advertised,
+                    ixgbe->param_adv_autoneg_cap) != IXGBE_SUCCESS) {
                         ixgbe_notice(ixgbe, "Setup link failed on this "
                             "device.");
                         return (IXGBE_FAILURE);
                 }
         }

@@ -3352,11 +3668,11 @@
         boolean_t link_up = B_FALSE;
         boolean_t link_changed = B_FALSE;
 
         ASSERT(mutex_owned(&ixgbe->gen_lock));
 
-        (void) ixgbe_check_link(hw, &speed, &link_up, false);
+        (void) ixgbe_check_link(hw, &speed, &link_up, B_FALSE);
         if (link_up) {
                 ixgbe->link_check_complete = B_TRUE;
 
                 /* Link is up, enable flow control settings */
                 (void) ixgbe_fc_enable(hw);

@@ -3367,10 +3683,16 @@
                 if (ixgbe->link_state != LINK_STATE_UP) {
                         switch (speed) {
                         case IXGBE_LINK_SPEED_10GB_FULL:
                                 ixgbe->link_speed = SPEED_10GB;
                                 break;
+                        case IXGBE_LINK_SPEED_5GB_FULL:
+                                ixgbe->link_speed = SPEED_5GB;
+                                break;
+                        case IXGBE_LINK_SPEED_2_5GB_FULL:
+                                ixgbe->link_speed = SPEED_2_5GB;
+                                break;
                         case IXGBE_LINK_SPEED_1GB_FULL:
                                 ixgbe->link_speed = SPEED_1GB;
                                 break;
                         case IXGBE_LINK_SPEED_100_FULL:
                                 ixgbe->link_speed = SPEED_100;

@@ -3420,29 +3742,29 @@
         ixgbe_t *ixgbe = (ixgbe_t *)arg;
         uint32_t eicr = ixgbe->eicr;
         struct ixgbe_hw *hw = &ixgbe->hw;
 
         mutex_enter(&ixgbe->gen_lock);
-        if (eicr & IXGBE_EICR_GPI_SDP1) {
+        if (eicr & IXGBE_EICR_GPI_SDP1_BY_MAC(hw)) {
                 /* clear the interrupt */
-                IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP1);
+                IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP1_BY_MAC(hw));
 
                 /* if link up, do multispeed fiber setup */
                 (void) ixgbe_setup_link(hw, IXGBE_LINK_SPEED_82599_AUTONEG,
-                    B_TRUE, B_TRUE);
+                    B_TRUE);
                 ixgbe_driver_link_check(ixgbe);
                 ixgbe_get_hw_state(ixgbe);
-        } else if (eicr & IXGBE_EICR_GPI_SDP2) {
+        } else if (eicr & IXGBE_EICR_GPI_SDP2_BY_MAC(hw)) {
                 /* clear the interrupt */
-                IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP2);
+                IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP2_BY_MAC(hw));
 
                 /* if link up, do sfp module setup */
                 (void) hw->mac.ops.setup_sfp(hw);
 
                 /* do multispeed fiber setup */
                 (void) ixgbe_setup_link(hw, IXGBE_LINK_SPEED_82599_AUTONEG,
-                    B_TRUE, B_TRUE);
+                    B_TRUE);
                 ixgbe_driver_link_check(ixgbe);
                 ixgbe_get_hw_state(ixgbe);
         }
         mutex_exit(&ixgbe->gen_lock);
 

@@ -3471,14 +3793,14 @@
         boolean_t link_up;
 
         mutex_enter(&ixgbe->gen_lock);
 
         /* make sure we know current state of link */
-        (void) ixgbe_check_link(hw, &speed, &link_up, false);
+        (void) ixgbe_check_link(hw, &speed, &link_up, B_FALSE);
 
         /* check over-temp condition */
-        if (((eicr & IXGBE_EICR_GPI_SDP0) && (!link_up)) ||
+        if (((eicr & IXGBE_EICR_GPI_SDP0_BY_MAC(hw)) && (!link_up)) ||
             (eicr & IXGBE_EICR_LSC)) {
                 if (hw->phy.ops.check_overtemp(hw) == IXGBE_ERR_OVERTEMP) {
                         atomic_or_32(&ixgbe->ixgbe_state, IXGBE_OVERTEMP);
 
                         /*

@@ -3507,10 +3829,69 @@
 
         mutex_exit(&ixgbe->gen_lock);
 }
 
 /*
+ * ixgbe_phy_check - taskq to process interrupts from an external PHY
+ *
+ * This routine will only be called on adapters with external PHYs
+ * (such as X550) that may be trying to raise our attention to some event.
+ * Currently, this is limited to claiming PHY overtemperature and link status
+ * change (LSC) events, however this may expand to include other things in
+ * future adapters.
+ */
+static void
+ixgbe_phy_check(void *arg)
+{
+        ixgbe_t *ixgbe = (ixgbe_t *)arg;
+        struct ixgbe_hw *hw = &ixgbe->hw;
+        int rv;
+
+        mutex_enter(&ixgbe->gen_lock);
+
+        /*
+         * X550 baseT PHY overtemp and LSC events are handled here.
+         *
+         * If an overtemp event occurs, it will be reflected in the
+         * return value of phy.ops.handle_lasi() and the common code will
+         * automatically power off the baseT PHY. This is our cue to trigger
+         * an FMA event.
+         *
+         * If a link status change event occurs, phy.ops.handle_lasi() will
+         * automatically initiate a link setup between the integrated KR PHY
+         * and the external X557 PHY to ensure that the link speed between
+         * them matches the link speed of the baseT link.
+         */
+        rv = ixgbe_handle_lasi(hw);
+
+        if (rv == IXGBE_ERR_OVERTEMP) {
+                atomic_or_32(&ixgbe->ixgbe_state, IXGBE_OVERTEMP);
+
+                /*
+                 * Disable the adapter interrupts
+                 */
+                ixgbe_disable_adapter_interrupts(ixgbe);
+
+                /*
+                 * Disable Rx/Tx units
+                 */
+                (void) ixgbe_stop_adapter(hw);
+
+                ddi_fm_service_impact(ixgbe->dip, DDI_SERVICE_LOST);
+                ixgbe_error(ixgbe,
+                    "Problem: Network adapter has been stopped due to a "
+                    "overtemperature event being detected.");
+                ixgbe_error(ixgbe,
+                    "Action: Shut down or restart the computer. If the issue "
+                    "persists, please take action in accordance with the "
+                    "recommendations from your system vendor.");
+        }
+
+        mutex_exit(&ixgbe->gen_lock);
+}
+
+/*
  * ixgbe_link_timer - timer for link status detection
  */
 static void
 ixgbe_link_timer(void *arg)
 {

@@ -3670,11 +4051,11 @@
 
         /*
          * Finally(!), if there's a valid "mac-address" property (created
          * if we netbooted from this interface), we must use this instead
          * of any of the above to ensure that the NFS/install server doesn't
-         * get confused by the address changing as Solaris takes over!
+         * get confused by the address changing as illumos takes over!
          */
         err = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ixgbe->dip,
             DDI_PROP_DONTPASS, "mac-address", &bytes, &nelts);
         if (err == DDI_PROP_SUCCESS) {
                 if (nelts == ETHERADDRL) {

@@ -3860,12 +4241,12 @@
                 /* disable autoclear, leave gpie at default */
                 eiac = 0;
 
                 /*
                  * General purpose interrupt enable.
-                 * For 82599 or X540, extended interrupt automask enable
-                 * only in MSI or MSI-X mode
+                 * For 82599, X540 and X550, extended interrupt
+                 * automask enable only in MSI or MSI-X mode
                  */
                 if ((hw->mac.type == ixgbe_mac_82598EB) ||
                     (ixgbe->intr_type == DDI_INTR_TYPE_MSI)) {
                         gpie |= IXGBE_GPIE_EIAME;
                 }

@@ -3877,10 +4258,12 @@
                 gpie |= ixgbe->capab->other_gpie;
                 break;
 
         case ixgbe_mac_82599EB:
         case ixgbe_mac_X540:
+        case ixgbe_mac_X550:
+        case ixgbe_mac_X550EM_x:
                 gpie |= ixgbe->capab->other_gpie;
 
                 /* Enable RSC Delay 8us when LRO enabled  */
                 if (ixgbe->lro_enable) {
                         gpie |= (1 << IXGBE_GPIE_RSC_DELAY_SHIFT);

@@ -4071,17 +4454,19 @@
                     atlas);
                 break;
 
         case ixgbe_mac_82599EB:
         case ixgbe_mac_X540:
+        case ixgbe_mac_X550:
+        case ixgbe_mac_X550EM_x:
                 reg = IXGBE_READ_REG(&ixgbe->hw, IXGBE_AUTOC);
                 reg |= (IXGBE_AUTOC_FLU |
                     IXGBE_AUTOC_10G_KX4);
                 IXGBE_WRITE_REG(&ixgbe->hw, IXGBE_AUTOC, reg);
 
                 (void) ixgbe_setup_link(&ixgbe->hw, IXGBE_LINK_SPEED_10GB_FULL,
-                    B_FALSE, B_TRUE);
+                    B_FALSE);
                 break;
 
         default:
                 break;
         }

@@ -4137,10 +4522,12 @@
  * ixgbe_intr_other_work - Process interrupt types other than tx/rx
  */
 static void
 ixgbe_intr_other_work(ixgbe_t *ixgbe, uint32_t eicr)
 {
+        struct ixgbe_hw *hw = &ixgbe->hw;
+
         ASSERT(mutex_owned(&ixgbe->gen_lock));
 
         /*
          * handle link status change
          */

@@ -4179,11 +4566,12 @@
 
         /*
          * Do SFP check for adapters with hot-plug capability
          */
         if ((ixgbe->capab->flags & IXGBE_FLAG_SFP_PLUG_CAPABLE) &&
-            ((eicr & IXGBE_EICR_GPI_SDP1) || (eicr & IXGBE_EICR_GPI_SDP2))) {
+            ((eicr & IXGBE_EICR_GPI_SDP1_BY_MAC(hw)) ||
+            (eicr & IXGBE_EICR_GPI_SDP2_BY_MAC(hw)))) {
                 ixgbe->eicr = eicr;
                 if ((ddi_taskq_dispatch(ixgbe->sfp_taskq,
                     ixgbe_sfp_check, (void *)ixgbe,
                     DDI_NOSLEEP)) != DDI_SUCCESS) {
                         ixgbe_log(ixgbe, "No memory available to dispatch "

@@ -4193,19 +4581,34 @@
 
         /*
          * Do over-temperature check for adapters with temp sensor
          */
         if ((ixgbe->capab->flags & IXGBE_FLAG_TEMP_SENSOR_CAPABLE) &&
-            ((eicr & IXGBE_EICR_GPI_SDP0) || (eicr & IXGBE_EICR_LSC))) {
+            ((eicr & IXGBE_EICR_GPI_SDP0_BY_MAC(hw)) ||
+            (eicr & IXGBE_EICR_LSC))) {
                 ixgbe->eicr = eicr;
                 if ((ddi_taskq_dispatch(ixgbe->overtemp_taskq,
                     ixgbe_overtemp_check, (void *)ixgbe,
                     DDI_NOSLEEP)) != DDI_SUCCESS) {
                         ixgbe_log(ixgbe, "No memory available to dispatch "
                             "taskq for overtemp check");
                 }
         }
+
+        /*
+         * Process an external PHY interrupt
+         */
+        if (hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T &&
+            (eicr & IXGBE_EICR_GPI_SDP0_X540)) {
+                ixgbe->eicr = eicr;
+                if ((ddi_taskq_dispatch(ixgbe->phy_taskq,
+                    ixgbe_phy_check, (void *)ixgbe,
+                    DDI_NOSLEEP)) != DDI_SUCCESS) {
+                        ixgbe_log(ixgbe, "No memory available to dispatch "
+                            "taskq for PHY check");
+                }
+        }
 }
 
 /*
  * ixgbe_intr_legacy - Interrupt handler for legacy interrupts.
  */

@@ -4290,10 +4693,12 @@
                                 ixgbe->eims &= ~(eicr & IXGBE_OTHER_INTR);
                                 break;
 
                         case ixgbe_mac_82599EB:
                         case ixgbe_mac_X540:
+                        case ixgbe_mac_X550:
+                        case ixgbe_mac_X550EM_x:
                                 ixgbe->eimc = IXGBE_82599_OTHER_INTR;
                                 IXGBE_WRITE_REG(hw, IXGBE_EIMC, ixgbe->eimc);
                                 break;
 
                         default:

@@ -4384,10 +4789,12 @@
                         ixgbe->eims &= ~(eicr & IXGBE_OTHER_INTR);
                         break;
 
                 case ixgbe_mac_82599EB:
                 case ixgbe_mac_X540:
+                case ixgbe_mac_X550:
+                case ixgbe_mac_X550EM_x:
                         ixgbe->eimc = IXGBE_82599_OTHER_INTR;
                         IXGBE_WRITE_REG(hw, IXGBE_EIMC, ixgbe->eimc);
                         break;
 
                 default:

@@ -4464,10 +4871,12 @@
                                 ixgbe_intr_other_work(ixgbe, eicr);
                                 break;
 
                         case ixgbe_mac_82599EB:
                         case ixgbe_mac_X540:
+                        case ixgbe_mac_X550:
+                        case ixgbe_mac_X550EM_x:
                                 ixgbe->eims |= IXGBE_EICR_RTX_QUEUE;
                                 ixgbe_intr_other_work(ixgbe, eicr);
                                 break;
 
                         default:

@@ -4553,10 +4962,27 @@
 
         /*
          * Install legacy interrupts
          */
         if (intr_types & DDI_INTR_TYPE_FIXED) {
+                /*
+                 * Disallow legacy interrupts for X550. X550 has a silicon
+                 * bug which prevents Shared Legacy interrupts from working.
+                 * For details, please reference:
+                 *
+                 * Intel Ethernet Controller X550 Specification Update rev. 2.1
+                 * May 2016, erratum 22: PCIe Interrupt Status Bit
+                 */
+                if (ixgbe->hw.mac.type == ixgbe_mac_X550 ||
+                    ixgbe->hw.mac.type == ixgbe_mac_X550EM_x ||
+                    ixgbe->hw.mac.type == ixgbe_mac_X550_vf ||
+                    ixgbe->hw.mac.type == ixgbe_mac_X550EM_x_vf) {
+                        ixgbe_log(ixgbe,
+                            "Legacy interrupts are not supported on this "
+                            "adapter. Please use MSI or MSI-X instead.");
+                        return (IXGBE_FAILURE);
+                }
                 rc = ixgbe_alloc_intr_handles(ixgbe, DDI_INTR_TYPE_FIXED);
                 if (rc == IXGBE_SUCCESS)
                         return (IXGBE_SUCCESS);
 
                 ixgbe_log(ixgbe,

@@ -4865,10 +5291,12 @@
                 IXGBE_WRITE_REG(hw, IXGBE_IVAR(index), ivar);
                 break;
 
         case ixgbe_mac_82599EB:
         case ixgbe_mac_X540:
+        case ixgbe_mac_X550:
+        case ixgbe_mac_X550EM_x:
                 if (cause == -1) {
                         /* other causes */
                         msix_vector |= IXGBE_IVAR_ALLOC_VAL;
                         index = (intr_alloc_entry & 1) * 8;
                         ivar = IXGBE_READ_REG(hw, IXGBE_IVAR_MISC);

@@ -4919,10 +5347,12 @@
                 IXGBE_WRITE_REG(hw, IXGBE_IVAR(index), ivar);
                 break;
 
         case ixgbe_mac_82599EB:
         case ixgbe_mac_X540:
+        case ixgbe_mac_X550:
+        case ixgbe_mac_X550EM_x:
                 if (cause == -1) {
                         /* other causes */
                         index = (intr_alloc_entry & 1) * 8;
                         ivar = IXGBE_READ_REG(hw, IXGBE_IVAR_MISC);
                         ivar |= (IXGBE_IVAR_ALLOC_VAL << index);

@@ -4969,10 +5399,12 @@
                 IXGBE_WRITE_REG(hw, IXGBE_IVAR(index), ivar);
                 break;
 
         case ixgbe_mac_82599EB:
         case ixgbe_mac_X540:
+        case ixgbe_mac_X550:
+        case ixgbe_mac_X550EM_x:
                 if (cause == -1) {
                         /* other causes */
                         index = (intr_alloc_entry & 1) * 8;
                         ivar = IXGBE_READ_REG(hw, IXGBE_IVAR_MISC);
                         ivar &= ~(IXGBE_IVAR_ALLOC_VAL << index);

@@ -5012,10 +5444,12 @@
                 case ixgbe_mac_82598EB:
                         return (sw_rx_index);
 
                 case ixgbe_mac_82599EB:
                 case ixgbe_mac_X540:
+                case ixgbe_mac_X550:
+                case ixgbe_mac_X550EM_x:
                         return (sw_rx_index * 2);
 
                 default:
                         break;
                 }

@@ -5028,10 +5462,12 @@
                             16 + (sw_rx_index % rx_ring_per_group);
                         return (hw_rx_index);
 
                 case ixgbe_mac_82599EB:
                 case ixgbe_mac_X540:
+                case ixgbe_mac_X550:
+                case ixgbe_mac_X550EM_x:
                         if (ixgbe->num_rx_groups > 32) {
                                 hw_rx_index = (sw_rx_index /
                                     rx_ring_per_group) * 2 +
                                     (sw_rx_index % rx_ring_per_group);
                         } else {

@@ -5133,10 +5569,12 @@
                         IXGBE_WRITE_REG(hw, IXGBE_IVAR(v_idx), 0);
                 break;
 
         case ixgbe_mac_82599EB:
         case ixgbe_mac_X540:
+        case ixgbe_mac_X550:
+        case ixgbe_mac_X550EM_x:
                 for (v_idx = 0; v_idx < 64; v_idx++)
                         IXGBE_WRITE_REG(hw, IXGBE_IVAR(v_idx), 0);
                 IXGBE_WRITE_REG(hw, IXGBE_IVAR_MISC, 0);
                 break;
 

@@ -5308,39 +5746,65 @@
  */
 static void
 ixgbe_get_hw_state(ixgbe_t *ixgbe)
 {
         struct ixgbe_hw *hw = &ixgbe->hw;
-        ixgbe_link_speed speed = IXGBE_LINK_SPEED_UNKNOWN;
+        ixgbe_link_speed speed = 0;
         boolean_t link_up = B_FALSE;
         uint32_t pcs1g_anlp = 0;
-        uint32_t pcs1g_ana = 0;
-        boolean_t autoneg = B_FALSE;
 
         ASSERT(mutex_owned(&ixgbe->gen_lock));
         ixgbe->param_lp_1000fdx_cap = 0;
         ixgbe->param_lp_100fdx_cap  = 0;
 
         /* check for link, don't wait */
-        (void) ixgbe_check_link(hw, &speed, &link_up, false);
-        pcs1g_ana = IXGBE_READ_REG(hw, IXGBE_PCS1GANA);
+        (void) ixgbe_check_link(hw, &speed, &link_up, B_FALSE);
 
+        /*
+         * Update the observed Link Partner's capabilities. Not all adapters
+         * can provide full information on the LP's capable speeds, so we
+         * provide what we can.
+         */
         if (link_up) {
                 pcs1g_anlp = IXGBE_READ_REG(hw, IXGBE_PCS1GANLP);
 
                 ixgbe->param_lp_1000fdx_cap =
                     (pcs1g_anlp & IXGBE_PCS1GANLP_LPFD) ? 1 : 0;
                 ixgbe->param_lp_100fdx_cap =
                     (pcs1g_anlp & IXGBE_PCS1GANLP_LPFD) ? 1 : 0;
         }
 
-        (void) ixgbe_get_link_capabilities(hw, &speed, &autoneg);
+        /*
+         * Update GLD's notion of the adapter's currently advertised speeds.
+         * Since the common code doesn't always record the current autonegotiate
+         * settings in the phy struct for all parts (specifically, adapters with
+         * SFPs) we first test to see if it is 0, and if so, we fall back to
+         * using the adapter's speed capabilities which we saved during instance
+         * init in ixgbe_init_params().
+         *
+         * Adapters with SFPs will always be shown as advertising all of their
+         * supported speeds, and adapters with baseT PHYs (where the phy struct
+         * is maintained by the common code) will always have a factual view of
+         * their currently-advertised speeds. In the case of SFPs, this is
+         * acceptable as we default to advertising all speeds that the adapter
+         * claims to support, and those properties are immutable; unlike on
+         * baseT (copper) PHYs, where speeds can be enabled or disabled at will.
+         */
+        speed = hw->phy.autoneg_advertised;
+        if (speed == 0)
+                speed = ixgbe->speeds_supported;
 
-        ixgbe->param_adv_1000fdx_cap = ((pcs1g_ana & IXGBE_PCS1GANA_FDC) &&
-            (speed & IXGBE_LINK_SPEED_1GB_FULL)) ? 1 : 0;
-        ixgbe->param_adv_100fdx_cap = ((pcs1g_ana & IXGBE_PCS1GANA_FDC) &&
-            (speed & IXGBE_LINK_SPEED_100_FULL)) ? 1 : 0;
+        ixgbe->param_adv_10000fdx_cap =
+            (speed & IXGBE_LINK_SPEED_10GB_FULL) ? 1 : 0;
+        ixgbe->param_adv_5000fdx_cap =
+            (speed & IXGBE_LINK_SPEED_5GB_FULL) ? 1 : 0;
+        ixgbe->param_adv_2500fdx_cap =
+            (speed & IXGBE_LINK_SPEED_2_5GB_FULL) ? 1 : 0;
+        ixgbe->param_adv_1000fdx_cap =
+            (speed & IXGBE_LINK_SPEED_1GB_FULL) ? 1 : 0;
+        ixgbe->param_adv_100fdx_cap =
+            (speed & IXGBE_LINK_SPEED_100_FULL) ? 1 : 0;
 }
 
 /*
  * ixgbe_get_driver_control - Notify that driver is in control of device.
  */