[SCSI] lpfc 8.1.4 : Introduce lpfc_reset_barrier() function for resets on dual channel adapters

Introduce lpfc_reset_barrier() function for resets on dual channel adapters

Workaround for a hardware errata on dual channel asics. There is a
potential for the chip to lock up on a reset if a shared dma engine is in
use. The (ugly) work around requires a reset process which uses a mailbox
command to synchronize the independent channels prior to the reset to
avoid the issue. Unfortunately, the timing windows required to ensure this
workaround succeeds are very specific, meaning we can't release the cpu
during the barrier.

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 764aadb..bb69a7a 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -1566,6 +1566,79 @@
 	return retval;
 }
 
+#define BARRIER_TEST_PATTERN (0xdeadbeef)
+
+void lpfc_reset_barrier(struct lpfc_hba * phba)
+{
+	uint32_t * resp_buf;
+	uint32_t * mbox_buf;
+	volatile uint32_t mbox;
+	uint32_t hc_copy;
+	int  i;
+	uint8_t hdrtype;
+
+	pci_read_config_byte(phba->pcidev, PCI_HEADER_TYPE, &hdrtype);
+	if (hdrtype != 0x80 ||
+	    (FC_JEDEC_ID(phba->vpd.rev.biuRev) != HELIOS_JEDEC_ID &&
+	     FC_JEDEC_ID(phba->vpd.rev.biuRev) != THOR_JEDEC_ID))
+		return;
+
+	/*
+	 * Tell the other part of the chip to suspend temporarily all
+	 * its DMA activity.
+	 */
+	resp_buf =  (uint32_t *)phba->MBslimaddr;
+
+	/* Disable the error attention */
+	hc_copy = readl(phba->HCregaddr);
+	writel((hc_copy & ~HC_ERINT_ENA), phba->HCregaddr);
+	readl(phba->HCregaddr); /* flush */
+
+	if (readl(phba->HAregaddr) & HA_ERATT) {
+		/* Clear Chip error bit */
+		writel(HA_ERATT, phba->HAregaddr);
+		phba->stopped = 1;
+	}
+
+	mbox = 0;
+	((MAILBOX_t *)&mbox)->mbxCommand = MBX_KILL_BOARD;
+	((MAILBOX_t *)&mbox)->mbxOwner = OWN_CHIP;
+
+	writel(BARRIER_TEST_PATTERN, (resp_buf + 1));
+	mbox_buf = (uint32_t *)phba->MBslimaddr;
+	writel(mbox, mbox_buf);
+
+	for (i = 0;
+	     readl(resp_buf + 1) != ~(BARRIER_TEST_PATTERN) && i < 50; i++)
+		mdelay(1);
+
+	if (readl(resp_buf + 1) != ~(BARRIER_TEST_PATTERN)) {
+		if (phba->sli.sli_flag & LPFC_SLI2_ACTIVE ||
+		    phba->stopped)
+			goto restore_hc;
+		else
+			goto clear_errat;
+	}
+
+	((MAILBOX_t *)&mbox)->mbxOwner = OWN_HOST;
+	for (i = 0; readl(resp_buf) != mbox &&  i < 500; i++)
+		mdelay(1);
+
+clear_errat:
+
+	while (!(readl(phba->HAregaddr) & HA_ERATT) && ++i < 500)
+		mdelay(1);
+
+	if (readl(phba->HAregaddr) & HA_ERATT) {
+		writel(HA_ERATT, phba->HAregaddr);
+		phba->stopped = 1;
+	}
+
+restore_hc:
+	writel(hc_copy, phba->HCregaddr);
+	readl(phba->HCregaddr); /* flush */
+}
+
 int
 lpfc_sli_brdkill(struct lpfc_hba * phba)
 {
@@ -1588,9 +1661,8 @@
 		psli->sli_flag);
 
 	if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool,
-						  GFP_ATOMIC)) == 0) {
+						  GFP_KERNEL)) == 0)
 		return 1;
-	}
 
 	/* Disable the error attention */
 	spin_lock_irq(phba->host->host_lock);
@@ -1610,6 +1682,8 @@
 		return 1;
 	}
 
+	psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
+
 	mempool_free(pmb, phba->mbox_mem_pool);
 
 	/* There is no completion for a KILL_BOARD mbox cmd. Check for an error
@@ -1625,7 +1699,10 @@
 	}
 
 	del_timer_sync(&psli->mbox_tmo);
-
+	if (ha_copy & HA_ERATT) {
+		writel(HA_ERATT, phba->HAregaddr);
+		phba->stopped = 1;
+	}
 	spin_lock_irq(phba->host->host_lock);
 	psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
 	spin_unlock_irq(phba->host->host_lock);
@@ -1665,6 +1742,7 @@
 			      (cfg_value &
 			       ~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR)));
 
+	psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
 	/* Now toggle INITFF bit in the Host Control Register */
 	writel(HC_INITFF, phba->HCregaddr);
 	mdelay(1);
@@ -1713,6 +1791,8 @@
 	mb->mbxCommand = MBX_RESTART;
 	mb->mbxHc = 1;
 
+	lpfc_reset_barrier(phba);
+
 	to_slim = phba->MBslimaddr;
 	writel(*(uint32_t *) mb, to_slim);
 	readl(to_slim); /* flush */
@@ -1730,7 +1810,7 @@
 	readl(to_slim); /* flush */
 
 	lpfc_sli_brdreset(phba);
-
+	phba->stopped = 0;
 	phba->hba_state = LPFC_INIT_START;
 
 	spin_unlock_irq(phba->host->host_lock);
@@ -2038,6 +2118,13 @@
 		return (MBX_NOT_FINISHED);
 	}
 
+	if (mb->mbxCommand != MBX_KILL_BOARD && flag & MBX_NOWAIT &&
+	    !(readl(phba->HCregaddr) & HC_MBINT_ENA)) {
+		spin_unlock_irqrestore(phba->host->host_lock, drvr_flag);
+		LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag)
+		return (MBX_NOT_FINISHED);
+	}
+
 	if (psli->sli_flag & LPFC_SLI_MBOX_ACTIVE) {
 		/* Polling for a mbox command when another one is already active
 		 * is not allowed in SLI. Also, the driver must have established
@@ -2154,8 +2241,7 @@
 		/* First copy command data to host SLIM area */
 		lpfc_sli_pcimem_bcopy(mb, &phba->slim2p->mbx, MAILBOX_CMD_SIZE);
 	} else {
-		if (mb->mbxCommand == MBX_CONFIG_PORT ||
-		    mb->mbxCommand == MBX_KILL_BOARD) {
+		if (mb->mbxCommand == MBX_CONFIG_PORT) {
 			/* copy command data into host mbox for cmpl */
 			lpfc_sli_pcimem_bcopy(mb, &phba->slim2p->mbx,
 					MAILBOX_CMD_SIZE);
@@ -3121,6 +3207,7 @@
 			/* Clear Chip error bit */
 			writel(HA_ERATT, phba->HAregaddr);
 			readl(phba->HAregaddr); /* flush */
+			phba->stopped = 1;
 		}
 
 		spin_lock(phba->host->host_lock);