[SCSI] lpfc: NPIV: add SLI-3 interface

NPIV support is only available via new adapter interface extensions,
termed SLI-3. This interface changes some of the basic behaviors such
as command and response ring element sizes and data structures, as
well as a change in buffer posting.  Note: the new firmware extensions
are found only on our mid-range and enterprise 4Gig adapters - so NPIV
support is available only on these newer adapters. The latest firmware
can be downloaded from the Emulex support page.

Signed-off-by: James Smart <James.Smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 1edac15e..6e0b42b 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -65,6 +65,25 @@
 	LPFC_ABORT_IOCB
 } lpfc_iocb_type;
 
+/*
+ * SLI-2/SLI-3 provide different sized iocbs.  Given a pointer to the start of
+ * the ring, and the slot number of the desired iocb entry, calc a pointer to
+ * that entry.
+ */
+static inline IOCB_t *
+lpfc_cmd_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
+{
+	return (IOCB_t *) (((char *) pring->cmdringaddr) +
+			   pring->cmdidx * phba->iocb_cmd_size);
+}
+
+static inline IOCB_t *
+lpfc_resp_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
+{
+	return (IOCB_t *) (((char *) pring->rspringaddr) +
+			   pring->rspidx * phba->iocb_rsp_size);
+}
+
 static struct lpfc_iocbq *
 __lpfc_sli_get_iocbq(struct lpfc_hba *phba)
 {
@@ -180,6 +199,9 @@
 	case CMD_RCV_ELS_REQ_CX:
 	case CMD_RCV_SEQUENCE64_CX:
 	case CMD_RCV_ELS_REQ64_CX:
+	case CMD_IOCB_RCV_SEQ64_CX:
+	case CMD_IOCB_RCV_ELS64_CX:
+	case CMD_IOCB_RCV_CONT64_CX:
 		type = LPFC_UNSOL_IOCB;
 		break;
 	default:
@@ -191,14 +213,19 @@
 }
 
 static int
-lpfc_sli_ring_map(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+lpfc_sli_ring_map(struct lpfc_hba *phba)
 {
 	struct lpfc_sli *psli = &phba->sli;
-	MAILBOX_t *pmbox = &pmb->mb;
-	int i, rc;
+	LPFC_MBOXQ_t *pmb;
+	MAILBOX_t *pmbox;
+	int i, rc, ret = 0;
 
+	pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+	if (!pmb)
+		return -ENOMEM;
+	pmbox = &pmb->mb;
+	phba->link_state = LPFC_INIT_MBX_CMDS;
 	for (i = 0; i < psli->num_rings; i++) {
-		phba->link_state = LPFC_INIT_MBX_CMDS;
 		lpfc_config_ring(phba, i, pmb);
 		rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
 		if (rc != MBX_SUCCESS) {
@@ -213,10 +240,12 @@
 					pmbox->mbxStatus,
 					i);
 			phba->link_state = LPFC_HBA_ERROR;
-			return -ENXIO;
+			ret = -ENXIO;
+			break;
 		}
 	}
-	return 0;
+	mempool_free(pmb, phba->mbox_mem_pool);
+	return ret;
 }
 
 static int
@@ -255,9 +284,10 @@
 static IOCB_t *
 lpfc_sli_next_iocb_slot (struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
 {
-	struct lpfc_pgp *pgp = &phba->slim2p->mbx.us.s2.port[pring->ringno];
+	struct lpfc_pgp *pgp = (phba->sli_rev == 3) ?
+		&phba->slim2p->mbx.us.s3_pgp.port[pring->ringno] :
+		&phba->slim2p->mbx.us.s2.port[pring->ringno];
 	uint32_t  max_cmd_idx = pring->numCiocb;
-	IOCB_t *iocb = NULL;
 
 	if ((pring->next_cmdidx == pring->cmdidx) &&
 	   (++pring->next_cmdidx >= max_cmd_idx))
@@ -291,9 +321,7 @@
 			return NULL;
 	}
 
-	iocb = IOCB_ENTRY(pring->cmdringaddr, pring->cmdidx);
-
-	return iocb;
+	return lpfc_cmd_iocb(phba, pring);
 }
 
 uint16_t
@@ -390,8 +418,7 @@
 	 * driver will put a command into.
 	 */
 	pring->cmdidx = pring->next_cmdidx;
-	writel(pring->cmdidx, phba->MBslimaddr
-	       + (SLIMOFF + (pring->ringno * 2)) * 4);
+	writel(pring->cmdidx, &phba->host_gp[pring->ringno].cmdPutInx);
 }
 
 static void
@@ -462,7 +489,9 @@
 static void
 lpfc_sli_turn_on_ring(struct lpfc_hba *phba, int ringno)
 {
-	struct lpfc_pgp  *pgp = &phba->slim2p->mbx.us.s2.port[ringno];
+	struct lpfc_pgp *pgp = (phba->sli_rev == 3) ?
+		&phba->slim2p->mbx.us.s3_pgp.port[ringno] :
+		&phba->slim2p->mbx.us.s2.port[ringno];
 	unsigned long iflags;
 
 	/* If the ring is active, flag it */
@@ -481,6 +510,168 @@
 	spin_unlock_irqrestore(&phba->hbalock, iflags);
 }
 
+struct lpfc_hbq_entry *
+lpfc_sli_next_hbq_slot(struct lpfc_hba *phba, uint32_t hbqno)
+{
+	struct hbq_s *hbqp = &phba->hbqs[hbqno];
+
+	if (hbqp->next_hbqPutIdx == hbqp->hbqPutIdx &&
+	    ++hbqp->next_hbqPutIdx >= hbqp->entry_count)
+		hbqp->next_hbqPutIdx = 0;
+
+	if (unlikely(hbqp->local_hbqGetIdx == hbqp->next_hbqPutIdx)) {
+		uint32_t raw_index = readl(&phba->hbq_get[hbqno]);
+		uint32_t getidx = le32_to_cpu(raw_index);
+
+		hbqp->local_hbqGetIdx = getidx;
+
+		if (unlikely(hbqp->local_hbqGetIdx >= hbqp->entry_count)) {
+			lpfc_printf_log(phba, KERN_ERR,
+					LOG_SLI,
+					"%d:1802 HBQ %d: local_hbqGetIdx "
+					"%u is > than hbqp->entry_count %u\n",
+					phba->brd_no, hbqno,
+					hbqp->local_hbqGetIdx,
+					hbqp->entry_count);
+
+			phba->link_state = LPFC_HBA_ERROR;
+			return NULL;
+		}
+
+		if (hbqp->local_hbqGetIdx == hbqp->next_hbqPutIdx)
+			return NULL;
+	}
+
+	return (struct lpfc_hbq_entry *) phba->hbqslimp.virt + hbqp->hbqPutIdx;
+}
+
+void
+lpfc_sli_hbqbuf_free_all(struct lpfc_hba *phba)
+{
+	uint32_t  i;
+
+	if (!phba->hbq_buffer_pool)
+		return;
+	/* Return all memory used by all HBQs */
+	for (i = 0; i < phba->hbq_buffer_count; i++) {
+		lpfc_hbq_free(phba, phba->hbq_buffer_pool[i].dbuf.virt,
+			      phba->hbq_buffer_pool[i].dbuf.phys);
+	}
+	kfree(phba->hbq_buffer_pool);
+	phba->hbq_buffer_pool = NULL;
+}
+
+static void
+lpfc_sli_hbq_to_firmware(struct lpfc_hba *phba, uint32_t hbqno,
+			 struct hbq_dmabuf *hbq_buf_desc)
+{
+	struct lpfc_hbq_entry *hbqe;
+
+	/* Get next HBQ entry slot to use */
+	hbqe = lpfc_sli_next_hbq_slot(phba, hbqno);
+	if (hbqe) {
+		struct hbq_s *hbqp = &phba->hbqs[hbqno];
+
+		hbqe->bde.addrHigh = putPaddrHigh(hbq_buf_desc->dbuf.phys);
+		hbqe->bde.addrLow  = putPaddrLow(hbq_buf_desc->dbuf.phys);
+		hbqe->bde.tus.f.bdeSize = FCELSSIZE;
+		hbqe->bde.tus.f.bdeFlags = 0;
+		hbqe->buffer_tag = hbq_buf_desc->tag;
+		/* Sync SLIM */
+		hbqp->hbqPutIdx = hbqp->next_hbqPutIdx;
+		writel(hbqp->hbqPutIdx, phba->hbq_put + hbqno);
+		/* flush */
+		readl(phba->hbq_put + hbqno);
+		phba->hbq_buff_count++;
+	}
+}
+
+static void
+lpfc_sli_fill_hbq(struct lpfc_hba *phba, uint32_t hbqno, uint32_t buffer_index)
+{
+	struct hbq_dmabuf *hbq_buf_desc;
+	uint32_t i;
+
+	for (i = 0; i < phba->hbqs[hbqno].entry_count; i++) {
+		/* Search hbqbufq, from the begining,
+		 * looking for an unused entry
+		 */
+		phba->hbq_buffer_pool[buffer_index + i].tag |= hbqno << 16;
+		hbq_buf_desc = phba->hbq_buffer_pool + buffer_index + i;
+		lpfc_sli_hbq_to_firmware(phba, hbqno, hbq_buf_desc);
+	}
+}
+
+int
+lpfc_sli_hbqbuf_fill_hbq(struct lpfc_hba *phba)
+{
+	return 0;
+}
+
+static int
+lpfc_sli_hbqbuf_fill_hbqs(struct lpfc_hba *phba)
+{
+	uint32_t buffer_index = 0;
+	uint32_t hbqno;
+
+	/* Populate HBQ entries */
+	for (hbqno = 0; hbqno < phba->hbq_count; ++hbqno) {
+		/* Find ring associated with HBQ */
+
+		lpfc_sli_fill_hbq(phba, hbqno, buffer_index);
+		buffer_index += phba->hbqs[hbqno].entry_count;
+	}
+	return 0;
+}
+
+struct hbq_dmabuf *
+lpfc_sli_hbqbuf_find(struct lpfc_hba *phba, uint32_t tag)
+{
+	if ((tag & 0xffff) < phba->hbq_buffer_count)
+		return phba->hbq_buffer_pool + (tag & 0xffff);
+
+	lpfc_printf_log(phba, KERN_ERR,
+			LOG_SLI,
+			"%d:1803 Bad hbq tag. Data: x%x x%x\n",
+			phba->brd_no, tag,
+			phba->hbq_buffer_count);
+	return NULL;
+}
+
+void
+lpfc_sli_hbqbuf_free(struct lpfc_hba *phba, void *virt, dma_addr_t phys)
+{
+	uint32_t i, hbqno;
+
+	for (i = 0; i < phba->hbq_buffer_count; i++) {
+		/* Search hbqbufq, from the begining, looking for a match on
+		   phys */
+		if (phba->hbq_buffer_pool[i].dbuf.phys == phys) {
+			hbqno = phba->hbq_buffer_pool[i].tag >> 16;
+			lpfc_sli_hbq_to_firmware(phba, hbqno,
+						 phba->hbq_buffer_pool + i);
+			return;
+		}
+	}
+
+	lpfc_printf_log(phba, KERN_ERR,
+			LOG_SLI,
+			"%d:1804 Cannot find virtual addr for "
+			"mapped buf. Data x%llx\n",
+			phba->brd_no, (unsigned long long) phys);
+}
+
+void
+lpfc_sli_free_hbq(struct lpfc_hba *phba, struct hbq_dmabuf *sp)
+{
+	uint32_t hbqno;
+
+	if (sp) {
+		hbqno = sp->tag >> 16;
+		lpfc_sli_hbq_to_firmware(phba, hbqno, sp);
+	}
+}
+
 static int
 lpfc_sli_chk_mbx_command(uint8_t mbxCommand)
 {
@@ -757,7 +948,9 @@
 	match = 0;
 	irsp = &(saveq->iocb);
 	if ((irsp->ulpCommand == CMD_RCV_ELS_REQ64_CX)
-	    || (irsp->ulpCommand == CMD_RCV_ELS_REQ_CX)) {
+	    || (irsp->ulpCommand == CMD_RCV_ELS_REQ_CX)
+	    || (irsp->ulpCommand == CMD_IOCB_RCV_ELS64_CX)
+	    || (irsp->ulpCommand == CMD_IOCB_RCV_CONT64_CX)) {
 		Rctl = FC_ELS_REQ;
 		Type = FC_ELS_DATA;
 	} else {
@@ -769,7 +962,8 @@
 
 		/* Firmware Workaround */
 		if ((Rctl == 0) && (pring->ringno == LPFC_ELS_RING) &&
-			(irsp->ulpCommand == CMD_RCV_SEQUENCE64_CX)) {
+		    (irsp->ulpCommand == CMD_RCV_SEQUENCE64_CX ||
+		     irsp->ulpCommand == CMD_IOCB_RCV_SEQ64_CX)) {
 			Rctl = FC_ELS_REQ;
 			Type = FC_ELS_DATA;
 			w5p->hcsw.Rctl = Rctl;
@@ -906,7 +1100,10 @@
 static void
 lpfc_sli_rsp_pointers_error(struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
 {
-	struct lpfc_pgp *pgp = &phba->slim2p->mbx.us.s2.port[pring->ringno];
+	struct lpfc_pgp *pgp = (phba->sli_rev == 3) ?
+		&phba->slim2p->mbx.us.s3_pgp.port[pring->ringno] :
+		&phba->slim2p->mbx.us.s2.port[pring->ringno];
+
 	/*
 	 * Ring <ringno> handler: portRspPut <portRspPut> is bigger then
 	 * rsp ring <portRspMax>
@@ -945,14 +1142,15 @@
 	uint32_t portRspPut, portRspMax;
 	int type;
 	uint32_t rsp_cmpl = 0;
-	void __iomem *to_slim;
 	uint32_t ha_copy;
 	unsigned long iflags;
 
 	pring->stats.iocb_event++;
 
-	/* The driver assumes SLI-2 mode */
-	pgp =  &phba->slim2p->mbx.us.s2.port[pring->ringno];
+	pgp = (phba->sli_rev == 3) ?
+		&phba->slim2p->mbx.us.s3_pgp.port[pring->ringno] :
+		&phba->slim2p->mbx.us.s2.port[pring->ringno];
+
 
 	/*
 	 * The next available response entry should never exceed the maximum
@@ -967,9 +1165,7 @@
 
 	rmb();
 	while (pring->rspidx != portRspPut) {
-
-		entry = IOCB_ENTRY(pring->rspringaddr, pring->rspidx);
-
+		entry = lpfc_resp_iocb(phba, pring);
 		if (++pring->rspidx >= portRspMax)
 			pring->rspidx = 0;
 
@@ -1050,9 +1246,7 @@
 		 * been updated, sync the pgp->rspPutInx and fetch the new port
 		 * response put pointer.
 		 */
-		to_slim = phba->MBslimaddr +
-			(SLIMOFF + (pring->ringno * 2) + 1) * 4;
-		writeb(pring->rspidx, to_slim);
+		writel(pring->rspidx, &phba->host_gp[pring->ringno].rspGetInx);
 
 		if (pring->rspidx == portRspPut)
 			portRspPut = le32_to_cpu(pgp->rspPutInx);
@@ -1096,7 +1290,9 @@
 lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
 				struct lpfc_sli_ring *pring, uint32_t mask)
 {
-	struct lpfc_pgp  *pgp = &phba->slim2p->mbx.us.s2.port[pring->ringno];
+	struct lpfc_pgp *pgp = (phba->sli_rev == 3) ?
+		&phba->slim2p->mbx.us.s3_pgp.port[pring->ringno] :
+		&phba->slim2p->mbx.us.s2.port[pring->ringno];
 	IOCB_t *irsp = NULL;
 	IOCB_t *entry = NULL;
 	struct lpfc_iocbq *cmdiocbq = NULL;
@@ -1107,7 +1303,6 @@
 	lpfc_iocb_type type;
 	unsigned long iflag;
 	uint32_t rsp_cmpl = 0;
-	void __iomem *to_slim;
 
 	spin_lock_irqsave(&phba->hbalock, iflag);
 	pring->stats.iocb_event++;
@@ -1131,14 +1326,14 @@
 		 * structure.  The copy involves a byte-swap since the
 		 * network byte order and pci byte orders are different.
 		 */
-		entry = IOCB_ENTRY(pring->rspringaddr, pring->rspidx);
+		entry = lpfc_resp_iocb(phba, pring);
 
 		if (++pring->rspidx >= portRspMax)
 			pring->rspidx = 0;
 
 		lpfc_sli_pcimem_bcopy((uint32_t *) entry,
 				      (uint32_t *) &rspiocbq.iocb,
-				      sizeof(IOCB_t));
+				      phba->iocb_rsp_size);
 		INIT_LIST_HEAD(&(rspiocbq.list));
 		irsp = &rspiocbq.iocb;
 
@@ -1222,9 +1417,7 @@
 		 * been updated, sync the pgp->rspPutInx and fetch the new port
 		 * response put pointer.
 		 */
-		to_slim = phba->MBslimaddr +
-			(SLIMOFF + (pring->ringno * 2) + 1) * 4;
-		writel(pring->rspidx, to_slim);
+		writel(pring->rspidx, &phba->host_gp[pring->ringno].rspGetInx);
 
 		if (pring->rspidx == portRspPut)
 			portRspPut = le32_to_cpu(pgp->rspPutInx);
@@ -1258,7 +1451,9 @@
 lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
 				struct lpfc_sli_ring *pring, uint32_t mask)
 {
-	struct lpfc_pgp  *pgp = &phba->slim2p->mbx.us.s2.port[pring->ringno];
+	struct lpfc_pgp *pgp = (phba->sli_rev == 3) ?
+		&phba->slim2p->mbx.us.s3_pgp.port[pring->ringno] :
+		&phba->slim2p->mbx.us.s2.port[pring->ringno];
 	IOCB_t *entry;
 	IOCB_t *irsp = NULL;
 	struct lpfc_iocbq *rspiocbp = NULL;
@@ -1271,7 +1466,6 @@
 	uint32_t portRspPut, portRspMax;
 	int rc = 1;
 	unsigned long iflag;
-	void __iomem *to_slim;
 
 	spin_lock_irqsave(&phba->hbalock, iflag);
 	pring->stats.iocb_event++;
@@ -1287,9 +1481,7 @@
 		 * Ring <ringno> handler: portRspPut <portRspPut> is bigger then
 		 * rsp ring <portRspMax>
 		 */
-		lpfc_printf_log(phba,
-				KERN_ERR,
-				LOG_SLI,
+		lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
 				"%d:0303 Ring %d handler: portRspPut %d "
 				"is bigger then rsp ring %d\n",
 				phba->brd_no,
@@ -1319,7 +1511,8 @@
 		 * the ulpLe field is set, the entire Command has been
 		 * received.
 		 */
-		entry = IOCB_ENTRY(pring->rspringaddr, pring->rspidx);
+		entry = lpfc_resp_iocb(phba, pring);
+
 		rspiocbp = __lpfc_sli_get_iocbq(phba);
 		if (rspiocbp == NULL) {
 			printk(KERN_ERR "%s: out of buffers! Failing "
@@ -1327,15 +1520,14 @@
 			break;
 		}
 
-		lpfc_sli_pcimem_bcopy(entry, &rspiocbp->iocb, sizeof(IOCB_t));
+		lpfc_sli_pcimem_bcopy(entry, &rspiocbp->iocb,
+				      phba->iocb_rsp_size);
 		irsp = &rspiocbp->iocb;
 
 		if (++pring->rspidx >= portRspMax)
 			pring->rspidx = 0;
 
-		to_slim = phba->MBslimaddr + (SLIMOFF + (pring->ringno * 2)
-					      + 1) * 4;
-		writel(pring->rspidx, to_slim);
+		writel(pring->rspidx, &phba->host_gp[pring->ringno].rspGetInx);
 
 		if (list_empty(&(pring->iocb_continueq))) {
 			list_add(&rspiocbp->list, &(pring->iocb_continueq));
@@ -1361,21 +1553,31 @@
 
 			if (irsp->ulpStatus) {
 				/* Rsp ring <ringno> error: IOCB */
-				lpfc_printf_log(phba,
-					KERN_WARNING,
-					LOG_SLI,
-					"%d:0328 Rsp Ring %d error: IOCB Data: "
-					"x%x x%x x%x x%x x%x x%x x%x x%x\n",
-					phba->brd_no,
-					pring->ringno,
-					irsp->un.ulpWord[0],
-					irsp->un.ulpWord[1],
-					irsp->un.ulpWord[2],
-					irsp->un.ulpWord[3],
-					irsp->un.ulpWord[4],
-					irsp->un.ulpWord[5],
-					*(((uint32_t *) irsp) + 6),
-					*(((uint32_t *) irsp) + 7));
+				lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+						"%d:0328 Rsp Ring %d error: "
+						"IOCB Data: "
+						"x%x x%x x%x x%x "
+						"x%x x%x x%x x%x "
+						"x%x x%x x%x x%x "
+						"x%x x%x x%x x%x\n",
+						phba->brd_no,
+						pring->ringno,
+						irsp->un.ulpWord[0],
+						irsp->un.ulpWord[1],
+						irsp->un.ulpWord[2],
+						irsp->un.ulpWord[3],
+						irsp->un.ulpWord[4],
+						irsp->un.ulpWord[5],
+						*(((uint32_t *) irsp) + 6),
+						*(((uint32_t *) irsp) + 7),
+						*(((uint32_t *) irsp) + 8),
+						*(((uint32_t *) irsp) + 9),
+						*(((uint32_t *) irsp) + 10),
+						*(((uint32_t *) irsp) + 11),
+						*(((uint32_t *) irsp) + 12),
+						*(((uint32_t *) irsp) + 13),
+						*(((uint32_t *) irsp) + 14),
+						*(((uint32_t *) irsp) + 15));
 			}
 
 			/*
@@ -1659,13 +1861,9 @@
 	psli = &phba->sli;
 
 	/* Kill HBA */
-	lpfc_printf_log(phba,
-		KERN_INFO,
-		LOG_SLI,
+	lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
 		"%d:0329 Kill HBA Data: x%x x%x\n",
-		phba->brd_no,
-		phba->pport->port_state,
-		psli->sli_flag);
+		phba->brd_no, phba->pport->port_state, psli->sli_flag);
 
 	if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool,
 						  GFP_KERNEL)) == 0)
@@ -1857,13 +2055,10 @@
 		if (i++ >= 20) {
 			/* Adapter failed to init, timeout, status reg
 			   <status> */
-			lpfc_printf_log(phba,
-					KERN_ERR,
-					LOG_INIT,
+			lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
 					"%d:0436 Adapter failed to init, "
 					"timeout, status reg x%x\n",
-					phba->brd_no,
-					status);
+					phba->brd_no, status);
 			phba->link_state = LPFC_HBA_ERROR;
 			return -ETIMEDOUT;
 		}
@@ -1873,9 +2068,7 @@
 			/* ERROR: During chipset initialization */
 			/* Adapter failed to init, chipset, status reg
 			   <status> */
-			lpfc_printf_log(phba,
-					KERN_ERR,
-					LOG_INIT,
+			lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
 					"%d:0437 Adapter failed to init, "
 					"chipset, status reg x%x\n",
 					phba->brd_no,
@@ -1905,9 +2098,7 @@
 	if (status & HS_FFERM) {
 		/* ERROR: During chipset initialization */
 		/* Adapter failed to init, chipset, status reg <status> */
-		lpfc_printf_log(phba,
-				KERN_ERR,
-				LOG_INIT,
+		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
 				"%d:0438 Adapter failed to init, chipset, "
 				"status reg x%x\n",
 				phba->brd_no,
@@ -1926,8 +2117,145 @@
 	return 0;
 }
 
+static struct hbq_dmabuf *
+lpfc_alloc_hbq_buffers(struct lpfc_hba *phba, int count)
+{
+	struct hbq_dmabuf *hbq_buffer_pool;
+	int  i;
+
+	hbq_buffer_pool = kmalloc(count * sizeof(struct hbq_dmabuf),
+				  GFP_KERNEL);
+	if (!hbq_buffer_pool)
+		goto out;
+
+	for (i = 0; i < count; ++i) {
+		hbq_buffer_pool[i].dbuf.virt =
+			lpfc_hbq_alloc(phba, MEM_PRI,
+				       &hbq_buffer_pool[i].dbuf.phys);
+		if (hbq_buffer_pool[i].dbuf.virt == NULL)
+			goto alloc_failed;
+		hbq_buffer_pool[i].tag = i;
+	}
+	goto out;
+
+alloc_failed:
+	while (--i >= 0)
+		lpfc_hbq_free(phba, hbq_buffer_pool[i].dbuf.virt,
+			      hbq_buffer_pool[i].dbuf.phys);
+	kfree(hbq_buffer_pool);
+	hbq_buffer_pool = NULL;
+
+out:
+	phba->hbq_buffer_pool = hbq_buffer_pool;
+	return hbq_buffer_pool;
+}
+
+static struct lpfc_hbq_init lpfc_els_hbq = {
+	.rn = 1,
+	.entry_count = 1200,
+	.mask_count = 0,
+	.profile = 0,
+	.ring_mask = 1 << LPFC_ELS_RING,
+};
+
+static struct lpfc_hbq_init *lpfc_hbq_definitions[] = {
+	&lpfc_els_hbq,
+};
+
+static int
+lpfc_sli_hbq_count(void)
+{
+	return ARRAY_SIZE(lpfc_hbq_definitions);
+}
+
+static int
+lpfc_sli_hbq_entry_count(void)
+{
+	int  hbq_count = lpfc_sli_hbq_count();
+	int  count = 0;
+	int  i;
+
+	for (i = 0; i < hbq_count; ++i)
+		count += lpfc_hbq_definitions[i]->entry_count;
+	return count;
+}
+
 int
-lpfc_sli_hba_setup(struct lpfc_hba *phba)
+lpfc_sli_hbq_size(void)
+{
+	return lpfc_sli_hbq_entry_count() * sizeof(struct lpfc_hbq_entry);
+}
+
+static int
+lpfc_sli_hbq_setup(struct lpfc_hba *phba)
+{
+	int  hbq_count = lpfc_sli_hbq_count();
+	LPFC_MBOXQ_t *pmb;
+	MAILBOX_t *pmbox;
+	uint32_t hbqno;
+	uint32_t hbq_entry_index;
+	uint32_t hbq_buffer_count;
+
+	/* count hbq buffers */
+	hbq_buffer_count = lpfc_sli_hbq_entry_count();
+	if (!lpfc_alloc_hbq_buffers(phba, hbq_buffer_count))
+		return -ENOMEM;
+
+	phba->hbq_buffer_count = hbq_buffer_count;
+
+	/* Get a Mailbox buffer to setup mailbox
+	 * commands for HBA initialization
+	 */
+	pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+
+	if (!pmb)
+		return -ENOMEM;
+
+	pmbox = &pmb->mb;
+
+	/* Initialize the struct lpfc_sli_hbq structure for each hbq */
+	phba->link_state = LPFC_INIT_MBX_CMDS;
+
+	hbq_entry_index = 0;
+	for (hbqno = 0; hbqno < hbq_count; ++hbqno) {
+		phba->hbqs[hbqno].next_hbqPutIdx = 0;
+		phba->hbqs[hbqno].hbqPutIdx      = 0;
+		phba->hbqs[hbqno].local_hbqGetIdx   = 0;
+		phba->hbqs[hbqno].entry_count =
+			lpfc_hbq_definitions[hbqno]->entry_count;
+		lpfc_config_hbq(phba, lpfc_hbq_definitions[hbqno],
+				hbq_entry_index, pmb);
+		hbq_entry_index += phba->hbqs[hbqno].entry_count;
+
+		if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
+			/* Adapter failed to init, mbxCmd <cmd> CFG_RING,
+			   mbxStatus <status>, ring <num> */
+
+			lpfc_printf_log(phba, KERN_ERR,
+					LOG_SLI,
+					"%d:1805 Adapter failed to init. "
+					"Data: x%x x%x x%x\n",
+					phba->brd_no, pmbox->mbxCommand,
+					pmbox->mbxStatus, hbqno);
+
+			phba->link_state = LPFC_HBA_ERROR;
+			mempool_free(pmb, phba->mbox_mem_pool);
+			/* Free all HBQ memory */
+			lpfc_sli_hbqbuf_free_all(phba);
+			return ENXIO;
+		}
+	}
+	phba->hbq_count = hbq_count;
+
+	/* Initially populate or replenish the HBQs */
+	lpfc_sli_hbqbuf_fill_hbqs(phba);
+	mempool_free(pmb, phba->mbox_mem_pool);
+
+	return 0;
+}
+
+static int
+lpfc_do_config_port(struct lpfc_hba *phba, int sli_mode)
 {
 	LPFC_MBOXQ_t *pmb;
 	uint32_t resetcount = 0, rc = 0, done = 0;
@@ -1938,6 +2266,7 @@
 		return -ENOMEM;
 	}
 
+	phba->sli_rev = sli_mode;
 	while (resetcount < 2 && !done) {
 		spin_lock_irq(&phba->hbalock);
 		phba->sli.sli_flag |= LPFC_SLI_MBOX_ACTIVE;
@@ -1954,14 +2283,14 @@
 		spin_unlock_irq(&phba->hbalock);
 		resetcount++;
 
-	/* Call pre CONFIG_PORT mailbox command initialization.  A value of 0
-	 * means the call was successful.  Any other nonzero value is a failure,
-	 * but if ERESTART is returned, the driver may reset the HBA and try
-	 * again.
-	 */
+		/* Call pre CONFIG_PORT mailbox command initialization.  A
+		 * value of 0 means the call was successful.  Any other
+		 * nonzero value is a failure, but if ERESTART is returned,
+		 * the driver may reset the HBA and try again.
+		 */
 		rc = lpfc_config_port_prep(phba);
 		if (rc == -ERESTART) {
-			phba->pport->port_state = 0;
+			phba->link_state = LPFC_LINK_UNKNOWN;
 			continue;
 		} else if (rc) {
 			break;
@@ -1970,39 +2299,116 @@
 		phba->link_state = LPFC_INIT_MBX_CMDS;
 		lpfc_config_port(phba, pmb);
 		rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
-		if (rc == MBX_SUCCESS)
-			done = 1;
-		else {
+		if (rc != MBX_SUCCESS) {
 			lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
-				"%d:0442 Adapter failed to init, mbxCmd x%x "
-				"CONFIG_PORT, mbxStatus x%x Data: x%x\n",
-				phba->brd_no, pmb->mb.mbxCommand,
-				pmb->mb.mbxStatus, 0);
+					"%d:0442 Adapter failed to init, "
+					"mbxCmd x%x CONFIG_PORT, mbxStatus "
+					"x%x Data: x%x\n",
+					phba->brd_no, pmb->mb.mbxCommand,
+					pmb->mb.mbxStatus, 0);
 			spin_lock_irq(&phba->hbalock);
 			phba->sli.sli_flag &= ~LPFC_SLI2_ACTIVE;
 			spin_unlock_irq(&phba->hbalock);
 			rc = -ENXIO;
+		} else {
+			done = 1;
+			/* DBG: Do we need max_vpi, reg_vpi for that matter
+			   phba->max_vpi = 0;
+			*/
 		}
 	}
-	if (!done)
+
+	if (!done) {
+		rc = -EINVAL;
+		goto do_prep_failed;
+	}
+
+	if ((pmb->mb.un.varCfgPort.sli_mode == 3) &&
+	    (!pmb->mb.un.varCfgPort.cMA)) {
+		rc = -ENXIO;
+		goto do_prep_failed;
+	}
+	return rc;
+
+ do_prep_failed:
+	mempool_free(pmb, phba->mbox_mem_pool);
+	return rc;
+}
+
+int
+lpfc_sli_hba_setup(struct lpfc_hba *phba)
+{
+	uint32_t rc;
+	int mode = 3;
+
+	switch (lpfc_sli_mode) {
+	case 2:
+		mode = 2;
+		break;
+	case 0:
+	case 3:
+		break;
+	default:
+		lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+				"%d:1819 Unrecognized lpfc_sli_mode "
+				"parameter: %d.\n",
+				phba->brd_no, lpfc_sli_mode);
+
+		break;
+	}
+
+	rc = lpfc_do_config_port(phba, mode);
+	if (rc && lpfc_sli_mode == 3)
+		lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+				"%d:1820 Unable to select SLI-3.  "
+				"Not supported by adapter.\n",
+				phba->brd_no);
+	if (rc && mode != 2)
+		rc = lpfc_do_config_port(phba, 2);
+	if (rc)
 		goto lpfc_sli_hba_setup_error;
 
-	rc = lpfc_sli_ring_map(phba, pmb);
+	if (phba->sli_rev == 3) {
+		phba->iocb_cmd_size = SLI3_IOCB_CMD_SIZE;
+		phba->iocb_rsp_size = SLI3_IOCB_RSP_SIZE;
+		phba->sli3_options |= LPFC_SLI3_ENABLED;
+		phba->sli3_options |= LPFC_SLI3_HBQ_ENABLED;
+
+	} else {
+		phba->iocb_cmd_size = SLI2_IOCB_CMD_SIZE;
+		phba->iocb_rsp_size = SLI2_IOCB_RSP_SIZE;
+		phba->sli3_options = 0x0;
+	}
+
+	lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+			"%d:0444 Firmware in SLI %x mode.\n",
+			phba->brd_no, phba->sli_rev);
+	rc = lpfc_sli_ring_map(phba);
 
 	if (rc)
 		goto lpfc_sli_hba_setup_error;
 
+	/* Init HBQs */
+
+	if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
+		rc = lpfc_sli_hbq_setup(phba);
+		if (rc)
+			goto lpfc_sli_hba_setup_error;
+	}
+
 	phba->sli.sli_flag |= LPFC_PROCESS_LA;
 
 	rc = lpfc_config_port_post(phba);
 	if (rc)
 		goto lpfc_sli_hba_setup_error;
 
-	goto lpfc_sli_hba_setup_exit;
-lpfc_sli_hba_setup_error:
+	return rc;
+
+ lpfc_sli_hba_setup_error:
 	phba->link_state = LPFC_HBA_ERROR;
-lpfc_sli_hba_setup_exit:
-	mempool_free(pmb, phba->mbox_mem_pool);
+	lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+			"%d:0445 Firmware initialization failed\n",
+			phba->brd_no);
 	return rc;
 }
 
@@ -2027,7 +2433,7 @@
 	uint32_t tmo_posted;
 
 	spin_lock_irqsave(&phba->pport->work_port_lock, iflag);
-	tmo_posted = (phba->pport->work_port_events & WORKER_MBOX_TMO) == 0;
+	tmo_posted = (phba->pport->work_port_events & WORKER_MBOX_TMO);
 	if (!tmo_posted)
 		phba->pport->work_port_events |= WORKER_MBOX_TMO;
 	spin_unlock_irqrestore(&phba->pport->work_port_lock, iflag);
@@ -2051,9 +2457,7 @@
 	}
 
 	/* Mbox cmd <mbxCommand> timeout */
-	lpfc_printf_log(phba,
-		KERN_ERR,
-		LOG_MBOX | LOG_SLI,
+	lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
 		"%d:0310 Mailbox command x%x timeout Data: x%x x%x x%p\n",
 		phba->brd_no,
 		mb->mbxCommand,
@@ -2105,13 +2509,25 @@
 	volatile uint32_t word0, ldata;
 	void __iomem *to_slim;
 
+	if (pmbox->mbox_cmpl && pmbox->mbox_cmpl != lpfc_sli_def_mbox_cmpl &&
+	    pmbox->mbox_cmpl != lpfc_sli_wake_mbox_wait) {
+		if(!pmbox->vport) {
+			lpfc_printf_log(phba, KERN_ERR,
+					LOG_MBOX,
+					"%d:1806 Mbox x%x failed. No vport\n",
+					phba->brd_no,
+					pmbox->mb.mbxCommand);
+			dump_stack();
+			return MBXERR_ERROR;
+		}
+	}
+
 	/* If the PCI channel is in offline state, do not post mbox. */
 	if (unlikely(pci_channel_offline(phba->pcidev)))
 		return MBX_NOT_FINISHED;
 
 	spin_lock_irqsave(&phba->hbalock, drvr_flag);
 	psli = &phba->sli;
-
 	mb = &pmbox->mb;
 	status = MBX_SUCCESS;
 
@@ -2172,15 +2588,11 @@
 		lpfc_mbox_put(phba, pmbox);
 
 		/* Mbox cmd issue - BUSY */
-		lpfc_printf_log(phba,
-			KERN_INFO,
-			LOG_MBOX | LOG_SLI,
+		lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
 			"%d:0308 Mbox cmd issue - BUSY Data: x%x x%x x%x x%x\n",
 			phba->brd_no,
-			mb->mbxCommand,
-			phba->pport->port_state,
-			psli->sli_flag,
-			flag);
+			mb->mbxCommand, phba->pport->port_state,
+			psli->sli_flag, flag);
 
 		psli->slistat.mbox_busy++;
 		spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
@@ -2223,15 +2635,11 @@
 	}
 
 	/* Mailbox cmd <cmd> issue */
-	lpfc_printf_log(phba,
-		KERN_INFO,
-		LOG_MBOX | LOG_SLI,
+	lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_SLI,
 		"%d:0309 Mailbox cmd x%x issue Data: x%x x%x x%x\n",
 		phba->brd_no,
-		mb->mbxCommand,
-		phba->pport->port_state,
-		psli->sli_flag,
-			flag);
+		mb->mbxCommand, phba->pport->port_state,
+		psli->sli_flag, flag);
 
 	psli->slistat.mbox_cmd++;
 	evtctr = psli->slistat.mbox_event;
@@ -2526,7 +2934,7 @@
 int
 lpfc_sli_setup(struct lpfc_hba *phba)
 {
-	int i, totiocb = 0;
+	int i, totiocbsize = 0;
 	struct lpfc_sli *psli = &phba->sli;
 	struct lpfc_sli_ring *pring;
 
@@ -2551,9 +2959,15 @@
 			pring->numRiocb += SLI2_IOCB_RSP_R1XTRA_ENTRIES;
 			pring->numCiocb += SLI2_IOCB_CMD_R3XTRA_ENTRIES;
 			pring->numRiocb += SLI2_IOCB_RSP_R3XTRA_ENTRIES;
+			pring->sizeCiocb = (phba->sli_rev == 3) ?
+				SLI3_IOCB_CMD_SIZE :
+				SLI2_IOCB_CMD_SIZE;
+			pring->sizeRiocb = (phba->sli_rev == 3) ?
+				SLI3_IOCB_RSP_SIZE :
+				SLI2_IOCB_RSP_SIZE;
 			pring->iotag_ctr = 0;
 			pring->iotag_max =
-			    (phba->cfg_hba_queue_depth * 2);
+				(phba->cfg_hba_queue_depth * 2);
 			pring->fast_iotag = pring->iotag_max;
 			pring->num_mask = 0;
 			break;
@@ -2561,6 +2975,12 @@
 			/* numCiocb and numRiocb are used in config_port */
 			pring->numCiocb = SLI2_IOCB_CMD_R1_ENTRIES;
 			pring->numRiocb = SLI2_IOCB_RSP_R1_ENTRIES;
+			pring->sizeCiocb = (phba->sli_rev == 3) ?
+				SLI3_IOCB_CMD_SIZE :
+				SLI2_IOCB_CMD_SIZE;
+			pring->sizeRiocb = (phba->sli_rev == 3) ?
+				SLI3_IOCB_RSP_SIZE :
+				SLI2_IOCB_RSP_SIZE;
 			pring->iotag_max = phba->cfg_hba_queue_depth;
 			pring->num_mask = 0;
 			break;
@@ -2568,6 +2988,12 @@
 			/* numCiocb and numRiocb are used in config_port */
 			pring->numCiocb = SLI2_IOCB_CMD_R2_ENTRIES;
 			pring->numRiocb = SLI2_IOCB_RSP_R2_ENTRIES;
+			pring->sizeCiocb = (phba->sli_rev == 3) ?
+				SLI3_IOCB_CMD_SIZE :
+				SLI2_IOCB_CMD_SIZE;
+			pring->sizeRiocb = (phba->sli_rev == 3) ?
+				SLI3_IOCB_RSP_SIZE :
+				SLI2_IOCB_RSP_SIZE;
 			pring->fast_iotag = 0;
 			pring->iotag_ctr = 0;
 			pring->iotag_max = 4096;
@@ -2576,36 +3002,38 @@
 			pring->prt[0].rctl = FC_ELS_REQ;
 			pring->prt[0].type = FC_ELS_DATA;
 			pring->prt[0].lpfc_sli_rcv_unsol_event =
-			    lpfc_els_unsol_event;
+				lpfc_els_unsol_event;
 			pring->prt[1].profile = 0;	/* Mask 1 */
 			pring->prt[1].rctl = FC_ELS_RSP;
 			pring->prt[1].type = FC_ELS_DATA;
 			pring->prt[1].lpfc_sli_rcv_unsol_event =
-			    lpfc_els_unsol_event;
+				lpfc_els_unsol_event;
 			pring->prt[2].profile = 0;	/* Mask 2 */
 			/* NameServer Inquiry */
 			pring->prt[2].rctl = FC_UNSOL_CTL;
 			/* NameServer */
 			pring->prt[2].type = FC_COMMON_TRANSPORT_ULP;
 			pring->prt[2].lpfc_sli_rcv_unsol_event =
-			    lpfc_ct_unsol_event;
+				lpfc_ct_unsol_event;
 			pring->prt[3].profile = 0;	/* Mask 3 */
 			/* NameServer response */
 			pring->prt[3].rctl = FC_SOL_CTL;
 			/* NameServer */
 			pring->prt[3].type = FC_COMMON_TRANSPORT_ULP;
 			pring->prt[3].lpfc_sli_rcv_unsol_event =
-			    lpfc_ct_unsol_event;
+				lpfc_ct_unsol_event;
 			break;
 		}
-		totiocb += (pring->numCiocb + pring->numRiocb);
+		totiocbsize += (pring->numCiocb * pring->sizeCiocb) +
+			(pring->numRiocb * pring->sizeRiocb);
 	}
-	if (totiocb > MAX_SLI2_IOCB) {
+	if (totiocbsize > MAX_SLIM_IOCB_SIZE) {
 		/* Too many cmd / rsp ring entries in SLI2 SLIM */
 		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
 				"%d:0462 Too many cmd / rsp ring entries in "
-				"SLI2 SLIM Data: x%x x%x\n",
-				phba->brd_no, totiocb, MAX_SLI2_IOCB);
+				"SLI2 SLIM Data: x%x x%lx\n",
+				phba->brd_no, totiocbsize,
+				(unsigned long) MAX_SLIM_IOCB_SIZE);
 	}
 	if (phba->cfg_multi_ring_support == 2)
 		lpfc_extra_ring_setup(phba);
@@ -2689,6 +3117,7 @@
 	phba->pport->work_port_events &= ~WORKER_MBOX_TMO;
 	spin_unlock_irqrestore(&phba->pport->work_port_lock, flags);
 
+	spin_lock_irqsave(&phba->hbalock, flags);
 	pmb = psli->mbox_active;
 	if (pmb) {
 		psli->mbox_active = NULL;
@@ -2708,6 +3137,9 @@
 	}
 	INIT_LIST_HEAD(&psli->mboxq);
 
+	/* Free all HBQ memory */
+	lpfc_sli_hbqbuf_free_all(phba);
+
 	return 1;
 }
 
@@ -3075,11 +3507,9 @@
 	retval = lpfc_sli_issue_iocb(phba, pring, piocb, 0);
 	if (retval == IOCB_SUCCESS) {
 		timeout_req = timeout * HZ;
-		spin_unlock_irq(&phba->hbalock);
 		timeleft = wait_event_timeout(done_q,
 				piocb->iocb_flag & LPFC_IO_WAKE,
 				timeout_req);
-		spin_lock_irq(&phba->hbalock);
 
 		if (piocb->iocb_flag & LPFC_IO_WAKE) {
 			lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
@@ -3164,13 +3594,25 @@
 {
 	struct lpfc_vport *vport = phba->pport;
 	int i = 0;
+	uint32_t ha_copy;
 
 	while (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE && !vport->stopped) {
 		if (i++ > LPFC_MBOX_TMO * 1000)
 			return 1;
 
-		if (lpfc_sli_handle_mb_event(phba) == 0)
-			i = 0;
+		/*
+		 * Call lpfc_sli_handle_mb_event only if a mailbox cmd
+		 * did finish. This way we won't get the misleading
+		 * "Stray Mailbox Interrupt" message.
+		 */
+		spin_lock_irq(&phba->hbalock);
+		ha_copy = phba->work_ha;
+		phba->work_ha &= ~HA_MBATT;
+		spin_unlock_irq(&phba->hbalock);
+
+		if (ha_copy & HA_MBATT)
+			if (lpfc_sli_handle_mb_event(phba) == 0)
+				i = 0;
 
 		msleep(1);
 	}