Print this page
6064 ixgbe needs X550 support
Reviewed by: Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Dan McDonald <danmcd@omniti.com>
        
*** 27,42 ****
   * 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.
   */
  
  #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 *);
--- 27,42 ----
   * 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";
  
  /*
   * Local function protoypes
   */
  static int ixgbe_register_mac(ixgbe_t *);
*** 63,72 ****
--- 63,73 ----
  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,83 ****
--- 75,85 ----
  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,333 ****
          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_SDP1_GPIEN
!         | IXGBE_SDP2_GPIEN), /* "other" interrupt types enable mask */
  
          (IXGBE_FLAG_DCA_CAPABLE
          | IXGBE_FLAG_RSS_CAPABLE
          | IXGBE_FLAG_VMDQ_CAPABLE
          | IXGBE_FLAG_RSC_CAPABLE) /* capability flags */
  };
  
  /*
   * Module Initialization Functions.
   */
  
  int
--- 314,359 ----
          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_X540
!         | IXGBE_EICR_GPI_SDP2_X540), /* "other" interrupt types handled */
  
!         (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,434 ****
  
          /* Attach the instance pointer to the dev_info data structure */
          ddi_set_driver_private(devinfo, ixgbe);
  
          /*
!          * 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);
--- 450,460 ----
  
          /* Attach the instance pointer to the dev_info data structure */
          ddi_set_driver_private(devinfo, ixgbe);
  
          /*
!          * 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,542 ****
--- 559,579 ----
                  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,576 ****
--- 604,618 ----
                  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,613 ****
                  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);
          atomic_or_32(&ixgbe->ixgbe_state, IXGBE_INITIALIZED);
  
          return (DDI_SUCCESS);
  
  attach_fail:
--- 645,655 ----
                  ixgbe_error(ixgbe, "Failed to enable DDI interrupts");
                  goto attach_fail;
          }
          ixgbe->attach_progress |= ATTACH_PROGRESS_ENABLE_INTR;
  
!         ixgbe_log(ixgbe, "%s", ixgbe_ident);
          atomic_or_32(&ixgbe->ixgbe_state, IXGBE_INITIALIZED);
  
          return (DDI_SUCCESS);
  
  attach_fail:
*** 786,795 ****
--- 828,844 ----
          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,964 ****
--- 1004,1032 ----
                   * 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,1020 ****
          /*
           * 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.
   *
--- 1077,1086 ----
*** 1245,1265 ****
  static int
  ixgbe_init(ixgbe_t *ixgbe)
  {
          struct ixgbe_hw *hw = &ixgbe->hw;
          u8 pbanum[IXGBE_PBANUM_LENGTH];
  
          mutex_enter(&ixgbe->gen_lock);
  
          /*
!          * Reset chipset to put the hardware in a known state
!          * before we try to do anything with the eeprom.
           */
!         if (ixgbe_reset_hw(hw) != IXGBE_SUCCESS) {
                  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) {
--- 1311,1367 ----
  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);
  
          /*
!          * Configure/Initialize hardware
           */
!         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,1304 ****
--- 1397,1411 ----
          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,1352 ****
   */
  static int
  ixgbe_chip_start(ixgbe_t *ixgbe)
  {
          struct ixgbe_hw *hw = &ixgbe->hw;
!         int ret_val, i;
  
          ASSERT(mutex_owned(&ixgbe->gen_lock));
  
          /*
           * Get the mac address
--- 1449,1459 ----
   */
  static int
  ixgbe_chip_start(ixgbe_t *ixgbe)
  {
          struct ixgbe_hw *hw = &ixgbe->hw;
!         int i;
  
          ASSERT(mutex_owned(&ixgbe->gen_lock));
  
          /*
           * Get the mac address
*** 1365,1390 ****
                  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);
--- 1472,1481 ----
*** 1410,1421 ****
          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
           */
          ixgbe_get_hw_state(ixgbe);
  
          /*
           * Make sure driver has control
           */
--- 1501,1538 ----
          for (i = 0; i < ixgbe->intr_cnt; i++) {
                  IXGBE_WRITE_REG(hw, IXGBE_EITR(i), ixgbe->intr_throttling[i]);
          }
  
          /*
!          * 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,1445 ****
   */
  static void
  ixgbe_chip_stop(ixgbe_t *ixgbe)
  {
          struct ixgbe_hw *hw = &ixgbe->hw;
  
          ASSERT(mutex_owned(&ixgbe->gen_lock));
  
          /*
!          * Tell firmware driver is no longer in control
           */
!         ixgbe_release_driver_control(hw);
  
          /*
           * Reset the chipset
           */
          (void) ixgbe_reset_hw(hw);
--- 1546,1564 ----
   */
  static void
  ixgbe_chip_stop(ixgbe_t *ixgbe)
  {
          struct ixgbe_hw *hw = &ixgbe->hw;
+         int rv;
  
          ASSERT(mutex_owned(&ixgbe->gen_lock));
  
          /*
!          * Stop interupt generation and disable Tx unit
           */
!         hw->adapter_stopped = B_FALSE;
!         (void) ixgbe_stop_adapter(hw);
  
          /*
           * Reset the chipset
           */
          (void) ixgbe_reset_hw(hw);
*** 1446,1455 ****
--- 1565,1600 ----
  
          /*
           * 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,1656 ****
--- 1792,1802 ----
   * 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,1691 ****
--- 1828,1855 ----
          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,2206 ****
           * 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 */
          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) {
                  reg_val = IXGBE_READ_REG(hw, IXGBE_RDRXCTL);
                  reg_val |= (IXGBE_RDRXCTL_CRCSTRIP | IXGBE_RDRXCTL_AGGDIS);
                  IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, reg_val);
          }
  
--- 2353,2372 ----
           * 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, 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_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,2231 ****
--- 2388,2403 ----
          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,2269 ****
                          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.
           */
          reg_val = IXGBE_READ_REG(hw, IXGBE_FCTRL);
!         reg_val |= IXGBE_FCTRL_BAM;     /* broadcast accept mode */
!         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 */
                  IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, reg_val);
          }
  
          /*
           * Setup VMDq and RSS for multiple receive queues
--- 2418,2445 ----
                          IXGBE_WRITE_REG(hw, IXGBE_PSRTYPE(i), reg_val);
                  }
          }
  
          /*
!          * 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 */
!         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_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,2311 ****
                  break;
          }
  
          /*
           * Enable the receive unit.  This must be done after filter
!          * control is set in FCTRL.
           */
!         reg_val = (IXGBE_RXCTRL_RXEN    /* Enable Receive Unit */
!             | IXGBE_RXCTRL_DMBYPS);     /* descriptor monitor bypass */
!         IXGBE_WRITE_REG(hw, IXGBE_RXCTRL, reg_val);
  
          /*
           * ixgbe_setup_rx_ring must be called after configuring RXCTRL
           */
          for (i = 0; i < ixgbe->num_rx_rings; i++) {
--- 2473,2490 ----
                  break;
          }
  
          /*
           * Enable the receive unit.  This must be done after filter
!          * 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_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,2349 ****
           * 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)
              + 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);
                  reg_val |= IXGBE_HLREG0_JUMBOEN;
                  IXGBE_WRITE_REG(hw, IXGBE_HLREG0, reg_val);
-         }
  
          /*
           * Setup RSC for multiple receive queues.
           */
          if (ixgbe->lro_enable) {
--- 2507,2531 ----
           * 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_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
           */
          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,2503 ****
--- 2676,2687 ----
                                      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,2522 ****
--- 2697,2708 ----
                          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,2542 ****
          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
           */
          if (hw->mac.type == ixgbe_mac_82599EB ||
!             hw->mac.type == ixgbe_mac_X540) {
                  /* 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);
  
--- 2715,2730 ----
          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, X540 and X550 parts
           */
          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) {
                  /* 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,2603 ****
   */
  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;
  
          /*
!          * Fill out redirection 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);
-         }
- 
          /*
-          * 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 |
--- 2754,2771 ----
   */
  static void
  ixgbe_setup_rss(ixgbe_t *ixgbe)
  {
          struct ixgbe_hw *hw = &ixgbe->hw;
!         uint32_t mrqc;
  
          /*
!          * Initialize RETA/ERETA table
           */
!         ixgbe_setup_rss_table(ixgbe);
  
          /*
           * Enable RSS & perform hash on these packet types
           */
          mrqc = IXGBE_MRQC_RSSEN |
              IXGBE_MRQC_RSS_FIELD_IPV4 |
              IXGBE_MRQC_RSS_FIELD_IPV4_TCP |
*** 2607,2626 ****
              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
   */
--- 2775,2784 ----
*** 2645,2654 ****
--- 2803,2814 ----
                  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,2718 ****
   */
  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;
  
          /*
!          * Fill out redirection 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);
!         }
  
          /*
-          * 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:
                  /*
--- 2841,2859 ----
   */
  static void
  ixgbe_setup_vmdq_rss(ixgbe_t *ixgbe)
  {
          struct ixgbe_hw *hw = &ixgbe->hw;
!         uint32_t i, mrqc;
!         uint32_t vtctl, vmdctl;
  
          /*
!          * Initialize RETA/ERETA table
           */
!         ixgbe_setup_rss_table(ixgbe);
  
          /*
           * Enable and setup RSS and VMDq
           */
          switch (hw->mac.type) {
          case ixgbe_mac_82598EB:
                  /*
*** 2739,2748 ****
--- 2880,2891 ----
                  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,2795 ****
          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) {
                  /*
                   * Enable Virtualization and Replication.
                   */
                  vtctl = IXGBE_VT_CTL_VT_ENABLE | IXGBE_VT_CTL_REPLEN;
                  IXGBE_WRITE_REG(hw, IXGBE_VT_CTL, vtctl);
--- 2917,2930 ----
          default:
                  break;
  
          }
  
          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) {
                  /*
                   * Enable Virtualization and Replication.
                   */
                  vtctl = IXGBE_VT_CTL_VT_ENABLE | IXGBE_VT_CTL_REPLEN;
                  IXGBE_WRITE_REG(hw, IXGBE_VT_CTL, vtctl);
*** 2801,2810 ****
--- 2936,3038 ----
                  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,3016 ****
  
                  break;
  
          case ixgbe_mac_82599EB:
          case ixgbe_mac_X540:
                  /*
                   * 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.
                   */
                  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) {
--- 3225,3246 ----
  
                  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 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,3181 ****
          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 */
          if (hw->mac.type == ixgbe_mac_82599EB ||
!             hw->mac.type == ixgbe_mac_X540) {
                  ixgbe->tx_head_wb_enable = B_FALSE;
          }
  
          /*
           * ixgbe LSO needs the tx h/w checksum support.
--- 3399,3413 ----
          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, X540 and X550 */
          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->tx_head_wb_enable = B_FALSE;
          }
  
          /*
           * ixgbe LSO needs the tx h/w checksum support.
*** 3194,3204 ****
          if (ixgbe->rx_hcksum_enable == B_FALSE) {
                  ixgbe->lro_enable = B_FALSE;
          }
  
          /*
!          * ixgbe LRO only been supported by 82599 and X540 now
           */
          if (hw->mac.type == ixgbe_mac_82598EB) {
                  ixgbe->lro_enable = B_FALSE;
          }
          ixgbe->tx_copy_thresh = ixgbe_get_prop(ixgbe, PROP_TX_COPY_THRESHOLD,
--- 3426,3436 ----
          if (ixgbe->rx_hcksum_enable == B_FALSE) {
                  ixgbe->lro_enable = B_FALSE;
          }
  
          /*
!          * 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,3253 ****
          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.
           */
!         if (hw->mac.type == ixgbe_mac_82599EB || hw->mac.type == ixgbe_mac_X540)
                  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)
  {
          ixgbe->param_en_10000fdx_cap = 1;
-         ixgbe->param_en_1000fdx_cap = 1;
-         ixgbe->param_en_100fdx_cap = 1;
          ixgbe->param_adv_10000fdx_cap = 1;
          ixgbe->param_adv_1000fdx_cap = 1;
          ixgbe->param_adv_100fdx_cap = 1;
  
          ixgbe->param_pause_cap = 1;
          ixgbe->param_asym_pause_cap = 1;
          ixgbe->param_rem_fault = 0;
  
--- 3456,3556 ----
          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, 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 ||
!             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_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,3264 ****
--- 3558,3569 ----
          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,3337 ****
   * 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;
  
          /*
!          * No half duplex support with 10Gb parts
           */
!         if (ixgbe->param_adv_10000fdx_cap == 1)
!                 autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL;
  
!         if (ixgbe->param_adv_1000fdx_cap == 1)
!                 autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL;
  
!         if (ixgbe->param_adv_100fdx_cap == 1)
!                 autoneg_advertised |= IXGBE_LINK_SPEED_100_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.");
  
!                 autoneg_advertised = IXGBE_LINK_SPEED_10GB_FULL |
!                     IXGBE_LINK_SPEED_1GB_FULL |
!                     IXGBE_LINK_SPEED_100_FULL;
          }
  
          if (setup_hw) {
!                 if (ixgbe_setup_link(&ixgbe->hw, autoneg_advertised,
!                     ixgbe->param_adv_autoneg_cap, B_TRUE) != IXGBE_SUCCESS) {
                          ixgbe_notice(ixgbe, "Setup link failed on this "
                              "device.");
                          return (IXGBE_FAILURE);
                  }
          }
--- 3607,3653 ----
   * ixgbe_driver_setup_link - Using the link properties to setup the link.
   */
  int
  ixgbe_driver_setup_link(ixgbe_t *ixgbe, boolean_t setup_hw)
  {
!         struct ixgbe_hw *hw = &ixgbe->hw;
!         ixgbe_link_speed advertised = 0;
  
          /*
!          * Assemble a list of enabled speeds to auto-negotiate with.
           */
!         if (ixgbe->param_en_10000fdx_cap == 1)
!                 advertised |= IXGBE_LINK_SPEED_10GB_FULL;
  
!         if (ixgbe->param_en_5000fdx_cap == 1)
!                 advertised |= IXGBE_LINK_SPEED_5GB_FULL;
  
!         if (ixgbe->param_en_2500fdx_cap == 1)
!                 advertised |= IXGBE_LINK_SPEED_2_5GB_FULL;
  
!         if (ixgbe->param_en_1000fdx_cap == 1)
!                 advertised |= IXGBE_LINK_SPEED_1GB_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, advertised,
!                     ixgbe->param_adv_autoneg_cap) != IXGBE_SUCCESS) {
                          ixgbe_notice(ixgbe, "Setup link failed on this "
                              "device.");
                          return (IXGBE_FAILURE);
                  }
          }
*** 3352,3362 ****
          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);
          if (link_up) {
                  ixgbe->link_check_complete = B_TRUE;
  
                  /* Link is up, enable flow control settings */
                  (void) ixgbe_fc_enable(hw);
--- 3668,3678 ----
          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, B_FALSE);
          if (link_up) {
                  ixgbe->link_check_complete = B_TRUE;
  
                  /* Link is up, enable flow control settings */
                  (void) ixgbe_fc_enable(hw);
*** 3367,3376 ****
--- 3683,3698 ----
                  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,3448 ****
          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) {
                  /* clear the interrupt */
!                 IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP1);
  
                  /* if link up, do multispeed fiber setup */
                  (void) ixgbe_setup_link(hw, IXGBE_LINK_SPEED_82599_AUTONEG,
!                     B_TRUE, B_TRUE);
                  ixgbe_driver_link_check(ixgbe);
                  ixgbe_get_hw_state(ixgbe);
!         } else if (eicr & IXGBE_EICR_GPI_SDP2) {
                  /* clear the interrupt */
!                 IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP2);
  
                  /* 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);
                  ixgbe_driver_link_check(ixgbe);
                  ixgbe_get_hw_state(ixgbe);
          }
          mutex_exit(&ixgbe->gen_lock);
  
--- 3742,3770 ----
          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_BY_MAC(hw)) {
                  /* clear the interrupt */
!                 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);
                  ixgbe_driver_link_check(ixgbe);
                  ixgbe_get_hw_state(ixgbe);
!         } else if (eicr & IXGBE_EICR_GPI_SDP2_BY_MAC(hw)) {
                  /* clear the interrupt */
!                 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);
                  ixgbe_driver_link_check(ixgbe);
                  ixgbe_get_hw_state(ixgbe);
          }
          mutex_exit(&ixgbe->gen_lock);
  
*** 3471,3484 ****
          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);
  
          /* check over-temp condition */
!         if (((eicr & IXGBE_EICR_GPI_SDP0) && (!link_up)) ||
              (eicr & IXGBE_EICR_LSC)) {
                  if (hw->phy.ops.check_overtemp(hw) == IXGBE_ERR_OVERTEMP) {
                          atomic_or_32(&ixgbe->ixgbe_state, IXGBE_OVERTEMP);
  
                          /*
--- 3793,3806 ----
          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, B_FALSE);
  
          /* check over-temp condition */
!         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,3516 ****
--- 3829,3897 ----
  
          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,3680 ****
  
          /*
           * 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!
           */
          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) {
--- 4051,4061 ----
  
          /*
           * 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 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,3871 ****
                  /* 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
                   */
                  if ((hw->mac.type == ixgbe_mac_82598EB) ||
                      (ixgbe->intr_type == DDI_INTR_TYPE_MSI)) {
                          gpie |= IXGBE_GPIE_EIAME;
                  }
--- 4241,4252 ----
                  /* disable autoclear, leave gpie at default */
                  eiac = 0;
  
                  /*
                   * General purpose interrupt enable.
!                  * 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,3886 ****
--- 4258,4269 ----
                  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,4087 ****
                      atlas);
                  break;
  
          case ixgbe_mac_82599EB:
          case ixgbe_mac_X540:
                  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);
                  break;
  
          default:
                  break;
          }
--- 4454,4472 ----
                      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);
                  break;
  
          default:
                  break;
          }
*** 4137,4146 ****
--- 4522,4533 ----
   * 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,4189 ****
  
          /*
           * 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))) {
                  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 "
--- 4566,4577 ----
  
          /*
           * Do SFP check for adapters with hot-plug capability
           */
          if ((ixgbe->capab->flags & IXGBE_FLAG_SFP_PLUG_CAPABLE) &&
!             ((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,4211 ****
  
          /*
           * 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))) {
                  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");
                  }
          }
  }
  
  /*
   * ixgbe_intr_legacy - Interrupt handler for legacy interrupts.
   */
--- 4581,4614 ----
  
          /*
           * Do over-temperature check for adapters with temp sensor
           */
          if ((ixgbe->capab->flags & IXGBE_FLAG_TEMP_SENSOR_CAPABLE) &&
!             ((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,4299 ****
--- 4693,4704 ----
                                  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,4393 ****
--- 4789,4800 ----
                          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,4473 ****
--- 4871,4882 ----
                                  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,4562 ****
--- 4962,4988 ----
  
          /*
           * 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,4874 ****
--- 5291,5302 ----
                  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,4928 ****
--- 5347,5358 ----
                  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,4978 ****
--- 5399,5410 ----
                  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,5021 ****
--- 5444,5455 ----
                  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,5037 ****
--- 5462,5473 ----
                              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,5142 ****
--- 5569,5580 ----
                          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,5346 ****
   */
  static void
  ixgbe_get_hw_state(ixgbe_t *ixgbe)
  {
          struct ixgbe_hw *hw = &ixgbe->hw;
!         ixgbe_link_speed speed = IXGBE_LINK_SPEED_UNKNOWN;
          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);
  
          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);
  
!         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_get_driver_control - Notify that driver is in control of device.
   */
--- 5746,5810 ----
   */
  static void
  ixgbe_get_hw_state(ixgbe_t *ixgbe)
  {
          struct ixgbe_hw *hw = &ixgbe->hw;
!         ixgbe_link_speed speed = 0;
          boolean_t link_up = B_FALSE;
          uint32_t pcs1g_anlp = 0;
  
          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, 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;
          }
  
!         /*
!          * 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_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.
   */