Print this page
MFV: illumos-omnios@aea0472ecb9ee91fa70556d6f6a941c10c989f1d
Add support for Emulex Corporation Lancer Gen6: LPe32000 FC Host Adapter
Author: Andy Fiddaman <omnios@citrus-it.co.uk>
NEX-8705 Drivers for ATTO Celerity FC-162E Gen 5 and Celerity FC-162P Gen 6 16GB FC cards support
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
NEX-1878 update emlxs from source provided by Emulex

@@ -20,10 +20,11 @@
  */
 
 /*
  * Copyright (c) 2004-2012 Emulex. All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #include <emlxs.h>
 
 

@@ -119,10 +120,23 @@
 
 static void             emlxs_sli4_timer(emlxs_hba_t *hba);
 
 static void             emlxs_sli4_timer_check_mbox(emlxs_hba_t *hba);
 
+static void             emlxs_sli4_gpio_timer_start(emlxs_hba_t *hba);
+
+static void             emlxs_sli4_gpio_timer_stop(emlxs_hba_t *hba);
+
+static void             emlxs_sli4_gpio_timer(void *arg);
+
+static void             emlxs_sli4_check_gpio(emlxs_hba_t *hba);
+
+static uint32_t emlxs_sli4_fix_gpio(emlxs_hba_t *hba,
+                                        uint8_t *pin, uint8_t *pinval);
+
+static uint32_t emlxs_sli4_fix_gpio_mbcmpl(emlxs_hba_t *hba, MAILBOXQ *mbq);
+
 static void             emlxs_sli4_poll_erratt(emlxs_hba_t *hba);
 
 extern XRIobj_t         *emlxs_sli4_reserve_xri(emlxs_port_t *port,
                                 RPIobj_t *rpip, uint32_t type, uint16_t rx_id);
 static int              emlxs_check_hdw_ready(emlxs_hba_t *);

@@ -330,19 +344,44 @@
                 cfg[CFG_NUM_WQ].current = 1;
                 hba->chan_count = hba->intr_count * cfg[CFG_NUM_WQ].current;
         }
         hba->channel_fcp = 0; /* First channel */
 
+        /* Gen6 chips only support P2P topologies */
+        if ((hba->model_info.flags & EMLXS_FC_GEN6) &&
+            cfg[CFG_TOPOLOGY].current != 2) {
+                EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_msg,
+                    "Loop topologies are not supported by this HBA. "
+                    "Forcing topology to P2P.");
+                cfg[CFG_TOPOLOGY].current = 2;
+        }
+
         /* Default channel for everything else is the last channel */
         hba->channel_ip = hba->chan_count - 1;
         hba->channel_els = hba->chan_count - 1;
         hba->channel_ct = hba->chan_count - 1;
 
         hba->fc_iotag = 1;
         hba->io_count = 0;
         hba->channel_tx_count = 0;
 
+        /* Specific to ATTO G5 boards */
+        if (hba->model_info.flags & EMLXS_GPIO_LEDS) {
+                /* Set hard-coded GPIO pins */
+                if (hba->pci_function_number) {
+                        hba->gpio_pin[EMLXS_GPIO_PIN_LO] = 27;
+                        hba->gpio_pin[EMLXS_GPIO_PIN_HI] = 28;
+                        hba->gpio_pin[EMLXS_GPIO_PIN_ACT] = 29;
+                        hba->gpio_pin[EMLXS_GPIO_PIN_LASER] = 8;
+                } else {
+                        hba->gpio_pin[EMLXS_GPIO_PIN_LO] = 13;
+                        hba->gpio_pin[EMLXS_GPIO_PIN_HI] = 25;
+                        hba->gpio_pin[EMLXS_GPIO_PIN_ACT] = 26;
+                        hba->gpio_pin[EMLXS_GPIO_PIN_LASER] = 12;
+                }
+        }
+
         /* Initialize the local dump region buffer */
         bzero(&hba->sli.sli4.dump_region, sizeof (MBUF_INFO));
         hba->sli.sli4.dump_region.size = EMLXS_DUMP_REGION_SIZE;
         hba->sli.sli4.dump_region.flags = FC_MBUF_DMA | FC_MBUF_SNGLSG
             | FC_MBUF_DMA32;

@@ -1327,16 +1366,18 @@
                 emlxs_build_prog_types(hba, vpd);
         }
 
         /* Create the symbolic names */
         (void) snprintf(hba->snn, (sizeof (hba->snn)-1),
-            "Emulex %s FV%s DV%s %s",
-            hba->model_info.model, hba->vpd.fw_version, emlxs_version,
+            "%s %s FV%s DV%s %s",
+            hba->model_info.manufacturer, hba->model_info.model,
+            hba->vpd.fw_version, emlxs_version,
             (char *)utsname.nodename);
 
         (void) snprintf(hba->spn, (sizeof (hba->spn)-1),
-            "Emulex PPN-%01x%01x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+            "%s PPN-%01x%01x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+            hba->model_info.manufacturer,
             hba->wwpn.nameType, hba->wwpn.IEEEextMsn, hba->wwpn.IEEEextLsb,
             hba->wwpn.IEEE[0], hba->wwpn.IEEE[1], hba->wwpn.IEEE[2],
             hba->wwpn.IEEE[3], hba->wwpn.IEEE[4], hba->wwpn.IEEE[5]);
 
 

@@ -1403,10 +1444,14 @@
         if (mbq) {
                 (void) kmem_free((uint8_t *)mbq, sizeof (MAILBOXQ));
                 mbq = NULL;
                 mb = NULL;
         }
+
+        if (hba->model_info.flags & EMLXS_GPIO_LEDS)
+                emlxs_sli4_gpio_timer_start(hba);
+
         return (0);
 
 failed3:
         EMLXS_STATE_CHANGE(hba, FC_ERROR);
 

@@ -1448,10 +1493,13 @@
 static void
 emlxs_sli4_offline(emlxs_hba_t *hba, uint32_t reset_requested)
 {
         /* Reverse emlxs_sli4_online */
 
+        if (hba->model_info.flags & EMLXS_GPIO_LEDS)
+                emlxs_sli4_gpio_timer_stop(hba);
+
         mutex_enter(&EMLXS_PORT_LOCK);
         if (hba->flag & FC_INTERLOCKED) {
                 mutex_exit(&EMLXS_PORT_LOCK);
                 goto killed;
         }

@@ -2361,10 +2409,19 @@
         hba->heartbeat_active = 0;
         hba->discovery_timer = 0;
         hba->linkup_timer = 0;
         hba->loopback_tics = 0;
 
+        /* Specific to ATTO G5 boards */
+        if (hba->model_info.flags & EMLXS_GPIO_LEDS) {
+                /* Assume the boot driver enabled all LEDs */
+                hba->gpio_current =
+                    EMLXS_GPIO_LO | EMLXS_GPIO_HI | EMLXS_GPIO_ACT;
+                hba->gpio_desired = 0;
+                hba->gpio_bit = 0;
+        }
+
         /* Reset the port objects */
         for (i = 0; i < MAX_VPORTS; i++) {
                 vport = &VPORT(i);
 
                 vport->flag &= EMLXS_PORT_RESET_MASK;

@@ -2553,11 +2610,11 @@
 
         iocbq = (IOCBQ *) &sbp->iocbq;
         wqe = &iocbq->wqe;
         pkt = PRIV2PKT(sbp);
         xrip = sbp->xrip;
-        sge = xrip->SGList.virt;
+        sge = xrip->SGList->virt;
 
 #if (EMLXS_MODREV >= EMLXS_MODREV3)
         cp_cmd = pkt->pkt_cmd_cookie;
         cp_data = pkt->pkt_data_cookie;
 #else

@@ -2703,11 +2760,11 @@
                     "fct_bde_setup: Only 1 sglist entry supported: %d",
                     sbp->fct_buf->db_sglist_length);
                 return (1);
         }
 
-        sge = xrip->SGList.virt;
+        sge = xrip->SGList->virt;
 
         if (iocb->ULPCOMMAND == CMD_FCP_TRECEIVE64_CX) {
 
                 mp = emlxs_mem_buf_alloc(hba, EMLXS_XFER_RDY_SIZE);
                 if (!mp || !mp->virt || !mp->phys) {

@@ -3986,11 +4043,11 @@
                 /* Make size a multiple of 4 */
                 if (sge_size & 3) {
                         sge_size = (sge_size + 3) & 0xfffffffc;
                 }
                 sge_addr = cp_cmd->dmac_laddress;
-                sge = xrip->SGList.virt;
+                sge = xrip->SGList->virt;
 
                 stage_sge.addrHigh = PADDR_HI(sge_addr);
                 stage_sge.addrLow = PADDR_LO(sge_addr);
                 stage_sge.length = sge_size;
                 stage_sge.offset = 0;

@@ -4178,11 +4235,10 @@
         IOCBQ *iocbq;
         IOCB *iocb;
         NODELIST *node;
         uint16_t iotag;
         uint32_t did;
-        off_t offset;
 
         pkt = PRIV2PKT(sbp);
         did = LE_SWAP24_LO(pkt->pkt_cmd_fhdr.d_id);
         cp = &hba->chan[channel];
 

@@ -4240,27 +4296,23 @@
         }
 
         /* DEBUG */
 #ifdef DEBUG_FCP
         EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
-            "FCP: SGLaddr virt %p phys %p size %d", xrip->SGList.virt,
-            xrip->SGList.phys, pkt->pkt_datalen);
-        emlxs_data_dump(port, "FCP: SGL", (uint32_t *)xrip->SGList.virt, 20, 0);
+            "FCP: SGLaddr virt %p phys %p size %d", xrip->SGList->virt,
+            xrip->SGList->phys, pkt->pkt_datalen);
+        emlxs_data_dump(port, "FCP: SGL",
+            (uint32_t *)xrip->SGList->virt, 20, 0);
         EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
             "FCP: CMD virt %p len %d:%d:%d",
             pkt->pkt_cmd, pkt->pkt_cmdlen, pkt->pkt_rsplen, pkt->pkt_datalen);
         emlxs_data_dump(port, "FCP: CMD", (uint32_t *)pkt->pkt_cmd, 10, 0);
 #endif /* DEBUG_FCP */
 
-        offset = (off_t)((uint64_t)((unsigned long)
-            xrip->SGList.virt) -
-            (uint64_t)((unsigned long)
-            hba->sli.sli4.slim2.virt));
+        EMLXS_MPDATA_SYNC(xrip->SGList->dma_handle, 0,
+            xrip->SGList->size, DDI_DMA_SYNC_FORDEV);
 
-        EMLXS_MPDATA_SYNC(xrip->SGList.dma_handle, offset,
-            xrip->SGList.size, DDI_DMA_SYNC_FORDEV);
-
         /* if device is FCP-2 device, set the following bit */
         /* that says to run the FC-TAPE protocol. */
         if (node->nlp_fcp_info & NLP_FCP_2_DEVICE) {
                 wqe->ERP = 1;
         }

@@ -4340,11 +4392,10 @@
         ULP_SGE64 stage_sge;
         ULP_SGE64 *sge;
         ddi_dma_cookie_t *cp_cmd;
         ddi_dma_cookie_t *cp_resp;
         emlxs_node_t *node;
-        off_t offset;
 
         pkt = PRIV2PKT(sbp);
         did = LE_SWAP24_LO(pkt->pkt_cmd_fhdr.d_id);
 
         iocbq = &sbp->iocbq;

@@ -4434,11 +4485,11 @@
                 wqe->OXId = xrip->rx_id;
 
                 sge->last = 1;
                 /* Now sge is fully staged */
 
-                sge = xrip->SGList.virt;
+                sge = xrip->SGList->virt;
                 BE_SWAP32_BCOPY((uint8_t *)&stage_sge, (uint8_t *)sge,
                     sizeof (ULP_SGE64));
 
                 if (rpip->RPI == FABRIC_RPI) {
                         wqe->ContextTag = port->vpip->VPI;

@@ -4499,11 +4550,11 @@
                 iocb->un.elsreq64.remoteID = (did == BCAST_DID) ? 0 : did;
                 iocb->ULPPU = 1;        /* Wd4 is relative offset */
 
                 sge->last = 0;
 
-                sge = xrip->SGList.virt;
+                sge = xrip->SGList->virt;
                 BE_SWAP32_BCOPY((uint8_t *)&stage_sge, (uint8_t *)sge,
                     sizeof (ULP_SGE64));
 
                 wqe->un.ElsCmd.PayloadLength =
                     pkt->pkt_cmdlen; /* Byte offset of rsp data */

@@ -4515,23 +4566,23 @@
                 sge->length = pkt->pkt_rsplen;
                 sge->offset = 0;
                 sge->last = 1;
                 /* Now sge is fully staged */
 
-                sge = xrip->SGList.virt;
+                sge = xrip->SGList->virt;
                 sge++;
                 BE_SWAP32_BCOPY((uint8_t *)&stage_sge, (uint8_t *)sge,
                     sizeof (ULP_SGE64));
 #ifdef DEBUG_ELS
                 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
                     "ELS: SGLaddr virt %p phys %p",
-                    xrip->SGList.virt, xrip->SGList.phys);
+                    xrip->SGList->virt, xrip->SGList->phys);
                 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
                     "ELS: PAYLOAD virt %p phys %p",
                     pkt->pkt_cmd, cp_cmd->dmac_laddress);
-                emlxs_data_dump(port, "ELS: SGL", (uint32_t *)xrip->SGList.virt,
-                    12, 0);
+                emlxs_data_dump(port, "ELS: SGL",
+                    (uint32_t *)xrip->SGList->virt, 12, 0);
 #endif /* DEBUG_ELS */
 
                 switch (cmd) {
                 case ELS_CMD_FLOGI:
                         wqe->un.ElsCmd.SP = 1;

@@ -4633,18 +4684,13 @@
                 } else {
                         wqe->CmdSpecific = reserved_rpip->RPI;
                 }
         }
 
-        offset = (off_t)((uint64_t)((unsigned long)
-            xrip->SGList.virt) -
-            (uint64_t)((unsigned long)
-            hba->sli.sli4.slim2.virt));
+        EMLXS_MPDATA_SYNC(xrip->SGList->dma_handle, 0,
+            xrip->SGList->size, DDI_DMA_SYNC_FORDEV);
 
-        EMLXS_MPDATA_SYNC(xrip->SGList.dma_handle, offset,
-            xrip->SGList.size, DDI_DMA_SYNC_FORDEV);
-
         if (pkt->pkt_cmd_fhdr.f_ctl & F_CTL_CHAINED_SEQ) {
                 wqe->CCPE = 1;
                 wqe->CCP = pkt->pkt_cmd_fhdr.rsvd;
         }
 

@@ -4678,11 +4724,10 @@
         NODELIST *node = NULL;
         CHANNEL *cp;
         RPIobj_t *rpip;
         XRIobj_t *xrip;
         uint32_t did;
-        off_t offset;
 
         pkt = PRIV2PKT(sbp);
         did = LE_SWAP24_LO(pkt->pkt_cmd_fhdr.d_id);
 
         iocbq = &sbp->iocbq;

@@ -4830,13 +4875,13 @@
                 wqe->un.GenReq.Rctl  = pkt->pkt_cmd_fhdr.r_ctl;
                 wqe->un.GenReq.Type  = pkt->pkt_cmd_fhdr.type;
 
 #ifdef DEBUG_CT
                 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
-                    "CT: SGLaddr virt %p phys %p", xrip->SGList.virt,
-                    xrip->SGList.phys);
-                emlxs_data_dump(port, "CT: SGL", (uint32_t *)xrip->SGList.virt,
+                    "CT: SGLaddr virt %p phys %p", xrip->SGList->virt,
+                    xrip->SGList->phys);
+                emlxs_data_dump(port, "CT: SGL", (uint32_t *)xrip->SGList->virt,
                     12, 0);
                 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
                     "CT: CMD virt %p len %d:%d",
                     pkt->pkt_cmd, pkt->pkt_cmdlen, pkt->pkt_rsplen);
                 emlxs_data_dump(port, "CT: DATA", (uint32_t *)pkt->pkt_cmd,

@@ -4856,18 +4901,13 @@
         iocb->un.genreq64.w5.hcsw.Rctl  = pkt->pkt_cmd_fhdr.r_ctl;
         iocb->un.genreq64.w5.hcsw.Type  = pkt->pkt_cmd_fhdr.type;
         iocb->un.genreq64.w5.hcsw.Dfctl  = pkt->pkt_cmd_fhdr.df_ctl;
         iocb->ULPPU = 1;        /* Wd4 is relative offset */
 
-        offset = (off_t)((uint64_t)((unsigned long)
-            xrip->SGList.virt) -
-            (uint64_t)((unsigned long)
-            hba->sli.sli4.slim2.virt));
+        EMLXS_MPDATA_SYNC(xrip->SGList->dma_handle, 0,
+            xrip->SGList->size, DDI_DMA_SYNC_FORDEV);
 
-        EMLXS_MPDATA_SYNC(xrip->SGList.dma_handle, offset,
-            xrip->SGList.size, DDI_DMA_SYNC_FORDEV);
-
         wqe->ContextTag = rpip->RPI;
         wqe->ContextType = WQE_RPI_CONTEXT;
         wqe->XRITag = xrip->XRI;
         wqe->Timer = ((pkt->pkt_timeout > 0xff) ? 0 : pkt->pkt_timeout);
 

@@ -5106,11 +5146,32 @@
                 }
                 break;
         case ASYNC_EVENT_CODE_PORT:
                 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
                     "SLI Port Async Event: type=%d", cqe->event_type);
-                if (cqe->event_type == ASYNC_EVENT_MISCONFIG_PORT) {
+
+                switch (cqe->event_type) {
+                case ASYNC_EVENT_PORT_OTEMP:
+                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_err_msg,
+                            "SLI Port Async Event: Temperature limit exceeded");
+                        cmn_err(CE_WARN,
+                            "^%s%d: Temperature limit exceeded. Fibre channel "
+                            "controller temperature %u degrees C",
+                            DRIVER_NAME, hba->ddiinst,
+                            BE_SWAP32(*(uint32_t *)cqe->un.port.link_status));
+                        break;
+
+                case ASYNC_EVENT_PORT_NTEMP:
+                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_err_msg,
+                            "SLI Port Async Event: Temperature returned to "
+                            "normal");
+                        cmn_err(CE_WARN,
+                            "^%s%d: Temperature returned to normal",
+                            DRIVER_NAME, hba->ddiinst);
+                        break;
+
+                case ASYNC_EVENT_MISCONFIG_PORT:
                         *((uint32_t *)cqe->un.port.link_status) =
                             BE_SWAP32(*((uint32_t *)cqe->un.port.link_status));
                         status =
                             cqe->un.port.link_status[hba->sli.sli4.link_number];
 

@@ -5159,11 +5220,13 @@
                                     "^%s%d: Misconfigured port: status=0x%x - "
                                     "Check optics on card.",
                                     DRIVER_NAME, hba->ddiinst, status);
                                 break;
                         }
+                        break;
                 }
+
                 break;
         case ASYNC_EVENT_CODE_VF:
                 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
                     "VF Async Event: type=%d",
                     cqe->event_type);

@@ -6597,23 +6660,10 @@
                 }
 
                 /* pass xrip to FCT in the iocbq */
                 iocbq->sbp = xrip;
 
-#define EMLXS_FIX_CISCO_BUG1
-#ifdef EMLXS_FIX_CISCO_BUG1
-{
-uint8_t *ptr;
-ptr = ((uint8_t *)seq_mp->virt);
-if (((*ptr+12) != 0xa0) && (*(ptr+20) == 0x8) && (*(ptr+21) == 0x8)) {
-        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_err_msg,
-            "RQ ENTRY: Bad CDB fixed");
-        *ptr++ = 0;
-        *ptr = 0;
-}
-}
-#endif
                 (void) emlxs_fct_handle_unsol_req(port, cp, iocbq,
                         seq_mp, seq_len);
                 break;
 #endif /* SFCT_SUPPORT */
 

@@ -7194,11 +7244,10 @@
         hba->sli.sli4.flag &= ~EMLXS_SLI4_INTR_ENABLED;
 
         /* Short of reset, we cannot disable interrupts */
 } /* emlxs_sli4_disable_intr() */
 
-
 static void
 emlxs_sli4_resource_free(emlxs_hba_t *hba)
 {
         emlxs_port_t    *port = &PPORT;
         MBUF_INFO       *buf_info;

@@ -7216,10 +7265,12 @@
         if (buf_info->virt) {
                 bzero(buf_info, sizeof (MBUF_INFO));
         }
 
         if (hba->sli.sli4.XRIp) {
+                XRIobj_t        *xrip;
+
                 if ((hba->sli.sli4.XRIinuse_f !=
                     (XRIobj_t *)&hba->sli.sli4.XRIinuse_f) ||
                     (hba->sli.sli4.XRIinuse_b !=
                     (XRIobj_t *)&hba->sli.sli4.XRIinuse_f)) {
                         EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_debug_msg,

@@ -7226,10 +7277,21 @@
                             "XRIs in use during free!: %p %p != %p\n",
                             hba->sli.sli4.XRIinuse_f,
                             hba->sli.sli4.XRIinuse_b,
                             &hba->sli.sli4.XRIinuse_f);
                 }
+
+                xrip = hba->sli.sli4.XRIp;
+                for (i = 0; i < hba->sli.sli4.XRICount; i++) {
+                        xrip->XRI = emlxs_sli4_index_to_xri(hba, i);
+
+                        if (xrip->XRI != 0)
+                                emlxs_mem_put(hba, xrip->SGSeg, xrip->SGList);
+
+                        xrip++;
+                }
+
                 kmem_free(hba->sli.sli4.XRIp,
                     (sizeof (XRIobj_t) * hba->sli.sli4.XRICount));
                 hba->sli.sli4.XRIp = NULL;
 
                 hba->sli.sli4.XRIfree_f =

@@ -7270,13 +7332,16 @@
                 buf_info->flags = FC_MBUF_DMA;
                 emlxs_mem_free(hba, buf_info);
                 bzero(buf_info, sizeof (MBUF_INFO));
         }
 
+        /* GPIO lock */
+        if (hba->model_info.flags & EMLXS_GPIO_LEDS)
+                mutex_destroy(&hba->gpio_lock);
+
 } /* emlxs_sli4_resource_free() */
 
-
 static int
 emlxs_sli4_resource_alloc(emlxs_hba_t *hba)
 {
         emlxs_port_t    *port = &PPORT;
         emlxs_config_t  *cfg = &CFG;

@@ -7350,14 +7415,10 @@
 
         /* RQB/E */
         count += RQB_COUNT * (RQB_DATA_SIZE + RQB_HEADER_SIZE);
         count += (4096 - (count%4096)); /* Ensure 4K alignment */
 
-        /* SGL */
-        count += hba->sli.sli4.XRIExtSize * hba->sli.sli4.mem_sgl_size;
-        count += (4096 - (count%4096)); /* Ensure 4K alignment */
-
         /* RPI Header Templates */
         if (hba->sli.sli4.param.HDRR) {
                 /* Bytes per extent */
                 j = hba->sli.sli4.RPIExtSize * sizeof (RPIHdrTmplate_t);
 

@@ -7374,10 +7435,13 @@
         buf_info = &hba->sli.sli4.slim2;
         buf_info->size = count;
         buf_info->flags = FC_MBUF_DMA | FC_MBUF_SNGLSG | FC_MBUF_DMA32;
         buf_info->align = ddi_ptob(hba->dip, 1L);
 
+        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
+            "Allocating memory for slim2: %d", count);
+
         (void) emlxs_mem_alloc(hba, buf_info);
 
         if (buf_info->virt == NULL) {
                 EMLXS_MSGF(EMLXS_CONTEXT,
                     &emlxs_init_failed_msg,

@@ -7387,11 +7451,11 @@
         }
         bzero(buf_info->virt, buf_info->size);
         EMLXS_MPDATA_SYNC(buf_info->dma_handle, 0,
             buf_info->size, DDI_DMA_SYNC_FORDEV);
 
-        /* Assign memory to SGL, Head Template, EQ, CQ, WQ, RQ and MQ */
+        /* Assign memory to Head Template, EQ, CQ, WQ, RQ and MQ */
         data_handle = buf_info->data_handle;
         dma_handle = buf_info->dma_handle;
         phys = buf_info->phys;
         virt = (char *)buf_info->virt;
 

@@ -7577,11 +7641,29 @@
         /* 4K Alignment */
         align = (4096 - (phys%4096));
         phys += align;
         virt += align;
 
+        /* RPI Header Templates */
+        if (hba->sli.sli4.param.HDRR) {
+                buf_info = &hba->sli.sli4.HeaderTmplate;
+                bzero(buf_info, sizeof (MBUF_INFO));
+                buf_info->size = hddr_size;
+                buf_info->flags = FC_MBUF_DMA | FC_MBUF_DMA32;
+                buf_info->align = ddi_ptob(hba->dip, 1L);
+                buf_info->phys = phys;
+                buf_info->virt = (void *)virt;
+                buf_info->data_handle = data_handle;
+                buf_info->dma_handle = dma_handle;
+        }
+
         /* SGL */
+
+        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
+            "Allocating memory for %d SGLs: %d/%d",
+            hba->sli.sli4.XRICount, sizeof (XRIobj_t), size);
+
         /* Initialize double linked lists */
         hba->sli.sli4.XRIinuse_f =
             (XRIobj_t *)&hba->sli.sli4.XRIinuse_f;
         hba->sli.sli4.XRIinuse_b =
             (XRIobj_t *)&hba->sli.sli4.XRIinuse_f;

@@ -7589,18 +7671,37 @@
 
         hba->sli.sli4.XRIfree_f =
             (XRIobj_t *)&hba->sli.sli4.XRIfree_f;
         hba->sli.sli4.XRIfree_b =
             (XRIobj_t *)&hba->sli.sli4.XRIfree_f;
-        hba->sli.sli4.xria_count = 0;
+        hba->sli.sli4.xrif_count = 0;
 
+        uint32_t mseg;
+
+        switch (hba->sli.sli4.mem_sgl_size) {
+        case 1024:
+                mseg = MEM_SGL1K;
+                break;
+        case 2048:
+                mseg = MEM_SGL2K;
+                break;
+        case 4096:
+                mseg = MEM_SGL4K;
+                break;
+        default:
+                EMLXS_MSGF(EMLXS_CONTEXT,
+                    &emlxs_init_failed_msg,
+                    "Unsupported SGL Size: %d", hba->sli.sli4.mem_sgl_size);
+                goto failed;
+        }
+
         hba->sli.sli4.XRIp = (XRIobj_t *)kmem_zalloc(
             (sizeof (XRIobj_t) * hba->sli.sli4.XRICount), KM_SLEEP);
 
         xrip = hba->sli.sli4.XRIp;
-        size = hba->sli.sli4.mem_sgl_size;
         iotag = 1;
+
         for (i = 0; i < hba->sli.sli4.XRICount; i++) {
                 xrip->XRI = emlxs_sli4_index_to_xri(hba, i);
 
                 /* We don't use XRI==0, since it also represents an */
                 /* uninitialized exchange */

@@ -7619,44 +7720,29 @@
                 xrip->_f = (XRIobj_t *)&hba->sli.sli4.XRIfree_f;
                 hba->sli.sli4.XRIfree_b = xrip;
                 hba->sli.sli4.xrif_count++;
 
                 /* Allocate SGL for this xrip */
-                buf_info = &xrip->SGList;
-                buf_info->size = size;
-                buf_info->flags =
-                    FC_MBUF_DMA | FC_MBUF_SNGLSG | FC_MBUF_DMA32;
-                buf_info->align = size;
-                buf_info->phys = phys;
-                buf_info->virt = (void *)virt;
-                buf_info->data_handle = data_handle;
-                buf_info->dma_handle = dma_handle;
+                xrip->SGSeg = mseg;
+                xrip->SGList = emlxs_mem_get(hba, xrip->SGSeg);
 
-                phys += size;
-                virt += size;
-
-                xrip++;
+                if (xrip->SGList == NULL) {
+                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_failed_msg,
+                            "Unable to allocate memory for SGL %d", i);
+                        goto failed;
         }
 
-        /* 4K Alignment */
-        align = (4096 - (phys%4096));
-        phys += align;
-        virt += align;
+                EMLXS_MPDATA_SYNC(xrip->SGList->dma_handle, 0,
+                    xrip->SGList->size, DDI_DMA_SYNC_FORDEV);
 
-        /* RPI Header Templates */
-        if (hba->sli.sli4.param.HDRR) {
-                buf_info = &hba->sli.sli4.HeaderTmplate;
-                bzero(buf_info, sizeof (MBUF_INFO));
-                buf_info->size = hddr_size;
-                buf_info->flags = FC_MBUF_DMA | FC_MBUF_DMA32;
-                buf_info->align = ddi_ptob(hba->dip, 1L);
-                buf_info->phys = phys;
-                buf_info->virt = (void *)virt;
-                buf_info->data_handle = data_handle;
-                buf_info->dma_handle = dma_handle;
+                xrip++;
         }
 
+        /* GPIO lock */
+        if (hba->model_info.flags & EMLXS_GPIO_LEDS)
+                mutex_init(&hba->gpio_lock, NULL, MUTEX_DRIVER, NULL);
+
 #ifdef FMA_SUPPORT
         if (hba->sli.sli4.slim2.dma_handle) {
                 if (emlxs_fm_check_dma_handle(hba,
                     hba->sli.sli4.slim2.dma_handle)
                     != DDI_FM_OK) {

@@ -8249,14 +8335,14 @@
 
                         for (i = 0; (i < xri_cnt) && cnt; i++) {
                                 post_sgl->params.request.xri_count++;
                                 post_sgl->params.request.pages[i].\
                                     sgl_page0.addrLow =
-                                    PADDR_LO(xrip->SGList.phys);
+                                    PADDR_LO(xrip->SGList->phys);
                                 post_sgl->params.request.pages[i].\
                                     sgl_page0.addrHigh =
-                                    PADDR_HI(xrip->SGList.phys);
+                                    PADDR_HI(xrip->SGList->phys);
 
                                 cnt--;
                                 xrip++;
                         }
 

@@ -8670,11 +8756,204 @@
 
         return;
 
 } /* emlxs_sli4_timer_check_mbox() */
 
+static void
+emlxs_sli4_gpio_timer_start(emlxs_hba_t *hba)
+{
+        mutex_enter(&hba->gpio_lock);
 
+        if (!hba->gpio_timer) {
+                hba->gpio_timer = timeout(emlxs_sli4_gpio_timer, (void *)hba,
+                    drv_usectohz(100000));
+        }
+
+        mutex_exit(&hba->gpio_lock);
+
+} /* emlxs_sli4_gpio_timer_start() */
+
+static void
+emlxs_sli4_gpio_timer_stop(emlxs_hba_t *hba)
+{
+        mutex_enter(&hba->gpio_lock);
+
+        if (hba->gpio_timer) {
+                (void) untimeout(hba->gpio_timer);
+                hba->gpio_timer = 0;
+        }
+
+        mutex_exit(&hba->gpio_lock);
+
+        delay(drv_usectohz(300000));
+} /* emlxs_sli4_gpio_timer_stop() */
+
+static void
+emlxs_sli4_gpio_timer(void *arg)
+{
+        emlxs_hba_t *hba = (emlxs_hba_t *)arg;
+
+        mutex_enter(&hba->gpio_lock);
+
+        if (hba->gpio_timer) {
+                emlxs_sli4_check_gpio(hba);
+                hba->gpio_timer = timeout(emlxs_sli4_gpio_timer, (void *)hba,
+                    drv_usectohz(100000));
+        }
+
+        mutex_exit(&hba->gpio_lock);
+} /* emlxs_sli4_gpio_timer() */
+
+static void
+emlxs_sli4_check_gpio(emlxs_hba_t *hba)
+{
+        hba->gpio_desired = 0;
+
+        if (hba->flag & FC_GPIO_LINK_UP) {
+                if (hba->io_active)
+                        hba->gpio_desired |= EMLXS_GPIO_ACT;
+
+                /* This is model specific to ATTO gen5 lancer cards */
+
+                switch (hba->linkspeed) {
+                        case LA_4GHZ_LINK:
+                                hba->gpio_desired |= EMLXS_GPIO_LO;
+                                break;
+
+                        case LA_8GHZ_LINK:
+                                hba->gpio_desired |= EMLXS_GPIO_HI;
+                                break;
+
+                        case LA_16GHZ_LINK:
+                                hba->gpio_desired |=
+                                    EMLXS_GPIO_LO | EMLXS_GPIO_HI;
+                                break;
+                }
+        }
+
+        if (hba->gpio_current != hba->gpio_desired) {
+                emlxs_port_t *port = &PPORT;
+                uint8_t pin;
+                uint8_t pinval;
+                MAILBOXQ *mbq;
+                uint32_t rval;
+
+                if (!emlxs_sli4_fix_gpio(hba, &pin, &pinval))
+                        return;
+
+                if ((mbq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX)) == NULL) {
+                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
+                            "Unable to allocate GPIO mailbox.");
+
+                        hba->gpio_bit = 0;
+                        return;
+                }
+
+                emlxs_mb_gpio_write(hba, mbq, pin, pinval);
+                mbq->mbox_cmpl = emlxs_sli4_fix_gpio_mbcmpl;
+
+                rval = emlxs_sli4_issue_mbox_cmd(hba, mbq, MBX_NOWAIT, 0);
+
+                if ((rval != MBX_BUSY) && (rval != MBX_SUCCESS)) {
+                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
+                            "Unable to start GPIO mailbox.");
+
+                        hba->gpio_bit = 0;
+                        emlxs_mem_put(hba, MEM_MBOX, mbq);
+                        return;
+                }
+        }
+} /* emlxs_sli4_check_gpio */
+
+static uint32_t
+emlxs_sli4_fix_gpio(emlxs_hba_t *hba, uint8_t *pin, uint8_t *pinval)
+{
+        uint8_t dif = hba->gpio_desired ^ hba->gpio_current;
+        uint8_t bit;
+        uint8_t i;
+
+        /* Get out if no pins to set a GPIO request is pending */
+
+        if (dif == 0 || hba->gpio_bit)
+                return (0);
+
+        /* Fix one pin at a time */
+
+        bit = dif & -dif;
+        hba->gpio_bit = bit;
+        dif = hba->gpio_current ^ bit;
+
+        for (i = EMLXS_GPIO_PIN_LO; bit > 1; ++i) {
+                dif >>= 1;
+                bit >>= 1;
+        }
+
+        /* Pins are active low so invert the bit value */
+
+        *pin = hba->gpio_pin[i];
+        *pinval = ~dif & bit;
+
+        return (1);
+} /* emlxs_sli4_fix_gpio */
+
+static uint32_t
+emlxs_sli4_fix_gpio_mbcmpl(emlxs_hba_t *hba, MAILBOXQ *mbq)
+{
+        MAILBOX *mb;
+        uint8_t pin;
+        uint8_t pinval;
+
+        mb = (MAILBOX *)mbq;
+
+        mutex_enter(&hba->gpio_lock);
+
+        if (mb->mbxStatus == 0)
+                hba->gpio_current ^= hba->gpio_bit;
+
+        hba->gpio_bit = 0;
+
+        if (emlxs_sli4_fix_gpio(hba, &pin, &pinval)) {
+                emlxs_port_t *port = &PPORT;
+                MAILBOXQ *mbq;
+                uint32_t rval;
+
+                /*
+                 * We're not using the mb_retry routine here because for some
+                 * reason it doesn't preserve the completion routine. Just let
+                 * this mbox cmd fail to start here and run when the mailbox
+                 * is no longer busy.
+                 */
+
+                if ((mbq = (MAILBOXQ *)emlxs_mem_get(hba, MEM_MBOX)) == NULL) {
+                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
+                            "Unable to allocate GPIO mailbox.");
+
+                        hba->gpio_bit = 0;
+                        goto done;
+                }
+
+                emlxs_mb_gpio_write(hba, mbq, pin, pinval);
+                mbq->mbox_cmpl = emlxs_sli4_fix_gpio_mbcmpl;
+
+                rval = emlxs_sli4_issue_mbox_cmd(hba, mbq, MBX_NOWAIT, 0);
+
+                if ((rval != MBX_BUSY) && (rval != MBX_SUCCESS)) {
+                        EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_init_debug_msg,
+                            "Unable to start GPIO mailbox.");
+
+                        hba->gpio_bit = 0;
+                        emlxs_mem_put(hba, MEM_MBOX, mbq);
+                        goto done;
+                }
+        }
+
+done:
+        mutex_exit(&hba->gpio_lock);
+
+        return (0);
+}
+
 extern void
 emlxs_data_dump(emlxs_port_t *port, char *str, uint32_t *iptr, int cnt, int err)
 {
         void *msg;
 

@@ -9111,10 +9390,13 @@
                 hba->linkspeed = LA_10GHZ_LINK;
                 break;
         case 16:
                 hba->linkspeed = LA_16GHZ_LINK;
                 break;
+        case 32:
+                hba->linkspeed = LA_32GHZ_LINK;
+                break;
         default:
                 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_sli_detail_msg,
                     "sli4_handle_fc_link_att: Unknown link speed=%x.",
                     cqe->un.fc.port_speed);
                 hba->linkspeed = 0;