From a1f6e0211b71a6c3ff401ad1d345ab024d0c6f01 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?nickcheng=28=E9=84=AD=E5=AE=88=E8=AC=99?=
 <nick.cheng@areca.com.tw>
Date: Fri, 15 Jun 2007 11:43:32 +0800
Subject: [SCSI] areca: improve driver stability and compatibility

Description:

    1. Implement PCI-Express error recovery function and AER
       capability, especially thanks to Yanmin Zhang's openhanded help
       about AER

    2. Implement the selection of ARCMSR_MAX_XFER_SECTORS_B=4096 if
       firmware version is latter than 1.42

    3. Add arcmsr_done4_abort_postqueue in arcmsr_iop_reset function
       to improve the stability as hot-unplug/plug

    4. Modify the ISR, arcmsr_interrupt routine, to prevent the
       inconsistency with sg_mod driver if application directly calls
       the arcmsr driver w/o passing through scsi midlayer

Signed-off-by: Nick Cheng <nick.cheng@areca.com.tw>
[jejb: unused variable removal]
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
---
 drivers/scsi/arcmsr/arcmsr.h     |   4 +-
 drivers/scsi/arcmsr/arcmsr_hba.c | 456 ++++++++++++++++++++++++++++++++++++---
 2 files changed, 425 insertions(+), 35 deletions(-)

(limited to 'drivers/scsi/arcmsr')

diff --git a/drivers/scsi/arcmsr/arcmsr.h b/drivers/scsi/arcmsr/arcmsr.h
index aff96db..f0b8bf4 100644
--- a/drivers/scsi/arcmsr/arcmsr.h
+++ b/drivers/scsi/arcmsr/arcmsr.h
@@ -48,9 +48,10 @@ struct class_device_attribute;
 
 #define ARCMSR_MAX_OUTSTANDING_CMD 						256
 #define ARCMSR_MAX_FREECCB_NUM							288
-#define ARCMSR_DRIVER_VERSION				"Driver Version 1.20.00.13"
+#define ARCMSR_DRIVER_VERSION				"Driver Version 1.20.00.14"
 #define ARCMSR_SCSI_INITIATOR_ID						255
 #define ARCMSR_MAX_XFER_SECTORS							512
+#define ARCMSR_MAX_XFER_SECTORS_B                                              4096
 #define ARCMSR_MAX_TARGETID							 17
 #define ARCMSR_MAX_TARGETLUN							  8
 #define ARCMSR_MAX_CMD_PERLUN				 ARCMSR_MAX_OUTSTANDING_CMD
@@ -469,4 +470,3 @@ extern void arcmsr_post_Qbuffer(struct AdapterControlBlock *acb);
 extern struct class_device_attribute *arcmsr_host_attrs[];
 extern int arcmsr_alloc_sysfs_attr(struct AdapterControlBlock *acb);
 void arcmsr_free_sysfs_attr(struct AdapterControlBlock *acb);
-
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index 672df79..0ddfc21 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -57,6 +57,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/timer.h>
 #include <linux/pci.h>
+#include <linux/aer.h>
 #include <asm/dma.h>
 #include <asm/io.h>
 #include <asm/system.h>
@@ -71,7 +72,7 @@
 #include "arcmsr.h"
 
 MODULE_AUTHOR("Erich Chen <erich@areca.com.tw>");
-MODULE_DESCRIPTION("ARECA (ARC11xx/12xx) SATA RAID HOST Adapter");
+MODULE_DESCRIPTION("ARECA (ARC11xx/12xx/13xx/16xx) SATA/SAS RAID HOST Adapter");
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_VERSION(ARCMSR_DRIVER_VERSION);
 
@@ -93,7 +94,9 @@ static void arcmsr_flush_adapter_cache(struct AdapterControlBlock *acb);
 static uint8_t arcmsr_wait_msgint_ready(struct AdapterControlBlock *acb);
 static const char *arcmsr_info(struct Scsi_Host *);
 static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb);
-
+static pci_ers_result_t arcmsr_pci_error_detected(struct pci_dev *pdev,
+						pci_channel_state_t state);
+static pci_ers_result_t arcmsr_pci_slot_reset(struct pci_dev *pdev);
 static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev, int queue_depth)
 {
 	if (queue_depth > ARCMSR_MAX_CMD_PERLUN)
@@ -104,7 +107,8 @@ static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev, int queue_de
 
 static struct scsi_host_template arcmsr_scsi_host_template = {
 	.module			= THIS_MODULE,
-	.name			= "ARCMSR ARECA SATA RAID HOST Adapter" ARCMSR_DRIVER_VERSION,
+	.name			= "ARCMSR ARECA SATA/SAS RAID HOST Adapter"
+							ARCMSR_DRIVER_VERSION,
 	.info			= arcmsr_info,
 	.queuecommand		= arcmsr_queue_command,
 	.eh_abort_handler	= arcmsr_abort,
@@ -119,6 +123,10 @@ static struct scsi_host_template arcmsr_scsi_host_template = {
 	.use_clustering		= ENABLE_CLUSTERING,
 	.shost_attrs		= arcmsr_host_attrs,
 };
+static struct pci_error_handlers arcmsr_pci_error_handlers = {
+	.error_detected		= arcmsr_pci_error_detected,
+	.slot_reset		= arcmsr_pci_slot_reset,
+};
 
 static struct pci_device_id arcmsr_device_id_table[] = {
 	{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1110)},
@@ -144,7 +152,8 @@ static struct pci_driver arcmsr_pci_driver = {
 	.id_table		= arcmsr_device_id_table,
 	.probe			= arcmsr_probe,
 	.remove			= arcmsr_remove,
-	.shutdown		= arcmsr_shutdown
+	.shutdown		= arcmsr_shutdown,
+	.err_handler		= &arcmsr_pci_error_handlers,
 };
 
 static irqreturn_t arcmsr_do_interrupt(int irq, void *dev_id)
@@ -328,6 +337,8 @@ static int arcmsr_probe(struct pci_dev *pdev,
 
 	arcmsr_iop_init(acb);
 	pci_set_drvdata(pdev, host);
+	if (strncmp(acb->firm_version, "V1.42", 5) >= 0)
+		host->max_sectors= ARCMSR_MAX_XFER_SECTORS_B;
 
 	error = scsi_add_host(host, &pdev->dev);
 	if (error)
@@ -338,6 +349,7 @@ static int arcmsr_probe(struct pci_dev *pdev,
 		goto out_free_sysfs;
 
 	scsi_scan_host(host);
+	pci_enable_pcie_error_reporting(pdev);
 	return 0;
  out_free_sysfs:
  out_free_irq:
@@ -488,7 +500,7 @@ static void arcmsr_enable_outbound_ints(struct AdapterControlBlock *acb,
 
 static void arcmsr_flush_adapter_cache(struct AdapterControlBlock *acb)
 {
-	struct MessageUnit __iomem *reg=acb->pmu;
+	struct MessageUnit __iomem *reg = acb->pmu;
 
 	writel(ARCMSR_INBOUND_MESG0_FLUSH_CACHE, &reg->inbound_msgaddr0);
 	if (arcmsr_wait_msgint_ready(acb))
@@ -718,7 +730,7 @@ static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb)
 		int id, lun;
 		/*
 		****************************************************************
-		**               areca cdb command done
+		**	      areca cdb command done
 		****************************************************************
 		*/
 		while (1) {
@@ -729,20 +741,20 @@ static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb)
 				(flag_ccb << 5));
 			if ((ccb->acb != acb) || (ccb->startdone != ARCMSR_CCB_START)) {
 				if (ccb->startdone == ARCMSR_CCB_ABORTED) {
-					struct scsi_cmnd *abortcmd=ccb->pcmd;
+					struct scsi_cmnd *abortcmd = ccb->pcmd;
 					if (abortcmd) {
 					abortcmd->result |= DID_ABORT >> 16;
 					arcmsr_ccb_complete(ccb, 1);
 					printk(KERN_NOTICE
-						"arcmsr%d: ccb='0x%p' isr got aborted command \n"
+						"arcmsr%d: ccb ='0x%p' isr got aborted command \n"
 						, acb->host->host_no, ccb);
 					}
 					continue;
 				}
 				printk(KERN_NOTICE
-					"arcmsr%d: isr get an illegal ccb command done acb='0x%p'"
-					"ccb='0x%p' ccbacb='0x%p' startdone = 0x%x"
-					" ccboutstandingcount=%d \n"
+					"arcmsr%d: isr get an illegal ccb command done acb = '0x%p'"
+					"ccb = '0x%p' ccbacb = '0x%p' startdone = 0x%x"
+					" ccboutstandingcount = %d \n"
 					, acb->host->host_no
 					, acb
 					, ccb
@@ -762,7 +774,7 @@ static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb)
 				switch(ccb->arcmsr_cdb.DeviceStatus) {
 				case ARCMSR_DEV_SELECT_TIMEOUT: {
 						acb->devstate[id][lun] = ARECA_RAID_GONE;
-						ccb->pcmd->result = DID_TIME_OUT << 16;
+						ccb->pcmd->result = DID_NO_CONNECT << 16;
 						arcmsr_ccb_complete(ccb, 1);
 					}
 					break;
@@ -781,8 +793,8 @@ static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb)
 					break;
 				default:
 					printk(KERN_NOTICE
-						"arcmsr%d: scsi id=%d lun=%d"
-						" isr get command error done,"
+						"arcmsr%d: scsi id = %d lun = %d"
+						" isr get command error done, "
 						"but got unknown DeviceStatus = 0x%x \n"
 						, acb->host->host_no
 						, id
@@ -1062,7 +1074,7 @@ static void arcmsr_handle_virtual_command(struct AdapterControlBlock *acb,
 		inqdata[1] = 0;
 		/* rem media bit & Dev Type Modifier */
 		inqdata[2] = 0;
-		/* ISO,ECMA,& ANSI versions */
+		/* ISO, ECMA, & ANSI versions */
 		inqdata[4] = 31;
 		/* length of additional data */
 		strncpy(&inqdata[8], "Areca   ", 8);
@@ -1112,7 +1124,7 @@ static int arcmsr_queue_command(struct scsi_cmnd *cmd,
 			, acb->host->host_no);
 		return SCSI_MLQUEUE_HOST_BUSY;
 	}
-	if(target == 16) {
+	if (target == 16) {
 		/* virtual device for iop message transfer */
 		arcmsr_handle_virtual_command(acb, cmd);
 		return 0;
@@ -1125,7 +1137,7 @@ static int arcmsr_queue_command(struct scsi_cmnd *cmd,
 			printk(KERN_NOTICE
 				"arcmsr%d: block 'read/write'"
 				"command with gone raid volume"
-				" Cmd=%2x, TargetId=%d, Lun=%d \n"
+				" Cmd = %2x, TargetId = %d, Lun = %d \n"
 				, acb->host->host_no
 				, cmd->cmnd[0]
 				, target, lun);
@@ -1216,7 +1228,7 @@ static void arcmsr_polling_ccbdone(struct AdapterControlBlock *acb,
 			if ((ccb->startdone == ARCMSR_CCB_ABORTED) ||
 				(ccb == poll_ccb)) {
 				printk(KERN_NOTICE
-					"arcmsr%d: scsi id=%d lun=%d ccb='0x%p'"
+					"arcmsr%d: scsi id = %d lun = %d ccb = '0x%p'"
 					" poll command abort successfully \n"
 					, acb->host->host_no
 					, ccb->pcmd->device->id
@@ -1229,8 +1241,8 @@ static void arcmsr_polling_ccbdone(struct AdapterControlBlock *acb,
 			}
 			printk(KERN_NOTICE
 				"arcmsr%d: polling get an illegal ccb"
-				" command done ccb='0x%p'"
-				"ccboutstandingcount=%d \n"
+				" command done ccb ='0x%p'"
+				"ccboutstandingcount = %d \n"
 				, acb->host->host_no
 				, ccb
 				, atomic_read(&acb->ccboutstandingcount));
@@ -1247,7 +1259,7 @@ static void arcmsr_polling_ccbdone(struct AdapterControlBlock *acb,
 			switch(ccb->arcmsr_cdb.DeviceStatus) {
 			case ARCMSR_DEV_SELECT_TIMEOUT: {
 					acb->devstate[id][lun] = ARECA_RAID_GONE;
-					ccb->pcmd->result = DID_TIME_OUT << 16;
+					ccb->pcmd->result = DID_NO_CONNECT << 16;
 					arcmsr_ccb_complete(ccb, 1);
 				}
 				break;
@@ -1266,7 +1278,7 @@ static void arcmsr_polling_ccbdone(struct AdapterControlBlock *acb,
 				break;
 			default:
 				printk(KERN_NOTICE
-					"arcmsr%d: scsi id=%d lun=%d"
+					"arcmsr%d: scsi id = %d lun = %d"
 					" polling and getting command error done"
 					"but got unknown DeviceStatus = 0x%x \n"
 					, acb->host->host_no
@@ -1281,6 +1293,94 @@ static void arcmsr_polling_ccbdone(struct AdapterControlBlock *acb,
 		}
 	}
 }
+static void arcmsr_done4_abort_postqueue(struct AdapterControlBlock *acb)
+{
+	int i = 0, found = 0;
+	int id, lun;
+	uint32_t flag_ccb, outbound_intstatus;
+	struct MessageUnit __iomem *reg = acb->pmu;
+	struct CommandControlBlock *ccb;
+	/*clear and abort all outbound posted Q*/
+
+	while (((flag_ccb = readl(&reg->outbound_queueport)) != 0xFFFFFFFF) &&
+(i++ < 256)){
+		ccb = (struct CommandControlBlock *)(acb->vir2phy_offset +
+(flag_ccb << 5));
+	if (ccb){
+		if ((ccb->acb != acb)||(ccb->startdone != \
+ARCMSR_CCB_START)){
+				printk(KERN_NOTICE "arcmsr%d: polling get \
+an illegal ccb" "command done ccb = '0x%p'""ccboutstandingcount = %d \n",
+					acb->host->host_no, ccb,
+					atomic_read(&acb->ccboutstandingcount));
+				continue;
+			}
+
+			id = ccb->pcmd->device->id;
+			lun = ccb->pcmd->device->lun;
+			if (!(flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR)){
+				if (acb->devstate[id][lun] == ARECA_RAID_GONE)
+					acb->devstate[id][lun] = ARECA_RAID_GOOD;
+				ccb->pcmd->result = DID_OK << 16;
+				arcmsr_ccb_complete(ccb, 1);
+			}
+			else {
+				switch(ccb->arcmsr_cdb.DeviceStatus) {
+				case ARCMSR_DEV_SELECT_TIMEOUT: {
+						acb->devstate[id][lun] = ARECA_RAID_GONE;
+						ccb->pcmd->result = DID_NO_CONNECT << 16;
+						arcmsr_ccb_complete(ccb, 1);
+				}
+				break;
+
+				case ARCMSR_DEV_ABORTED:
+
+				case ARCMSR_DEV_INIT_FAIL: {
+						acb->devstate[id][lun] =
+							ARECA_RAID_GONE;
+						ccb->pcmd->result =
+							DID_BAD_TARGET << 16;
+				arcmsr_ccb_complete(ccb, 1);
+				}
+				break;
+
+				case ARCMSR_DEV_CHECK_CONDITION: {
+						acb->devstate[id][lun] =
+							ARECA_RAID_GOOD;
+						arcmsr_report_sense_info(ccb);
+						arcmsr_ccb_complete(ccb, 1);
+				}
+				break;
+
+				default:
+						printk(KERN_NOTICE
+						      "arcmsr%d: scsi id = %d \
+							lun = %d""polling and \
+							getting command error \
+							done""but got unknown \
+							DeviceStatus = 0x%x \n",
+							acb->host->host_no, id,
+					   lun, ccb->arcmsr_cdb.DeviceStatus);
+						acb->devstate[id][lun] =
+								ARECA_RAID_GONE;
+						ccb->pcmd->result =
+							DID_BAD_TARGET << 16;
+						arcmsr_ccb_complete(ccb, 1);
+				break;
+			       }
+	}
+		       found = 1;
+	       }
+	}
+	if (found){
+		outbound_intstatus = readl(&reg->outbound_intstatus) & \
+			acb->outbound_int_enable;
+		writel(outbound_intstatus, &reg->outbound_intstatus);
+		/*clear interrupt*/
+	}
+	return;
+}
+
 
 static void arcmsr_iop_init(struct AdapterControlBlock *acb)
 {
@@ -1314,7 +1414,6 @@ static void arcmsr_iop_init(struct AdapterControlBlock *acb)
 
 static void arcmsr_iop_reset(struct AdapterControlBlock *acb)
 {
-	struct MessageUnit __iomem *reg = acb->pmu;
 	struct CommandControlBlock *ccb;
 	uint32_t intmask_org;
 	int i = 0;
@@ -1327,21 +1426,17 @@ static void arcmsr_iop_reset(struct AdapterControlBlock *acb)
 		/* disable all outbound interrupt */
 		intmask_org = arcmsr_disable_outbound_ints(acb);
 		/* clear all outbound posted Q */
-		for (i = 0; i < ARCMSR_MAX_OUTSTANDING_CMD; i++)
-			readl(&reg->outbound_queueport);
+		arcmsr_done4_abort_postqueue(acb);
 		for (i = 0; i < ARCMSR_MAX_FREECCB_NUM; i++) {
 			ccb = acb->pccb_pool[i];
-			if ((ccb->startdone == ARCMSR_CCB_START) ||
-				(ccb->startdone == ARCMSR_CCB_ABORTED)) {
+			if (ccb->startdone == ARCMSR_CCB_START) {
 				ccb->startdone = ARCMSR_CCB_ABORTED;
-				ccb->pcmd->result = DID_ABORT << 16;
-				arcmsr_ccb_complete(ccb, 1);
 			}
 		}
 		/* enable all outbound interrupt */
 		arcmsr_enable_outbound_ints(acb, intmask_org);
 	}
-	atomic_set(&acb->ccboutstandingcount, 0);
+
 }
 
 static int arcmsr_bus_reset(struct scsi_cmnd *cmd)
@@ -1387,10 +1482,9 @@ static int arcmsr_abort(struct scsi_cmnd *cmd)
 	int i = 0;
 
 	printk(KERN_NOTICE
-		"arcmsr%d: abort device command of scsi id=%d lun=%d \n",
+		"arcmsr%d: abort device command of scsi id = %d lun = %d \n",
 		acb->host->host_no, cmd->device->id, cmd->device->lun);
 	acb->num_aborts++;
-
 	/*
 	************************************************
 	** the all interrupt service routine is locked
@@ -1445,10 +1539,306 @@ static const char *arcmsr_info(struct Scsi_Host *host)
 		type = "X-TYPE";
 		break;
 	}
-	sprintf(buf, "Areca %s Host Adapter RAID Controller%s\n        %s",
+	sprintf(buf, "Areca %s Host Adapter RAID Controller%s\n %s",
 			type, raid6 ? "( RAID6 capable)" : "",
 			ARCMSR_DRIVER_VERSION);
 	return buf;
 }
 
+static pci_ers_result_t arcmsr_pci_slot_reset(struct pci_dev *pdev)
+{
+	struct Scsi_Host *host;
+	struct AdapterControlBlock *acb;
+	uint8_t bus, dev_fun;
+	int error;
+
+	error = pci_enable_device(pdev);
+	if (error)
+		return PCI_ERS_RESULT_DISCONNECT;
+	pci_set_master(pdev);
+
+	host = scsi_host_alloc(&arcmsr_scsi_host_template, sizeof \
+(struct AdapterControlBlock));
+	if (!host)
+		return PCI_ERS_RESULT_DISCONNECT;
+	acb = (struct AdapterControlBlock *)host->hostdata;
+	memset(acb, 0, sizeof (struct AdapterControlBlock));
+
+	error = pci_set_dma_mask(pdev, DMA_64BIT_MASK);
+	if (error) {
+		error = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+		if (error) {
+			printk(KERN_WARNING
+			       "scsi%d: No suitable DMA mask available\n",
+			       host->host_no);
+			return PCI_ERS_RESULT_DISCONNECT;
+		}
+	}
+	bus = pdev->bus->number;
+	dev_fun = pdev->devfn;
+	acb = (struct AdapterControlBlock *) host->hostdata;
+	memset(acb, 0, sizeof(struct AdapterControlBlock));
+	acb->pdev = pdev;
+	acb->host = host;
+	host->max_sectors = ARCMSR_MAX_XFER_SECTORS;
+	host->max_lun = ARCMSR_MAX_TARGETLUN;
+	host->max_id = ARCMSR_MAX_TARGETID;/*16:8*/
+	host->max_cmd_len = 16;    /*this is issue of 64bit LBA, over 2T byte*/
+	host->sg_tablesize = ARCMSR_MAX_SG_ENTRIES;
+	host->can_queue = ARCMSR_MAX_FREECCB_NUM; /* max simultaneous cmds */
+	host->cmd_per_lun = ARCMSR_MAX_CMD_PERLUN;
+	host->this_id = ARCMSR_SCSI_INITIATOR_ID;
+	host->unique_id = (bus << 8) | dev_fun;
+	host->irq = pdev->irq;
+	error = pci_request_regions(pdev, "arcmsr");
+	if (error)
+		return PCI_ERS_RESULT_DISCONNECT;
+
+	acb->pmu = ioremap(pci_resource_start(pdev, 0),
+			   pci_resource_len(pdev, 0));
+	if (!acb->pmu) {
+		printk(KERN_NOTICE "arcmsr%d: memory"
+			" mapping region fail \n", acb->host->host_no);
+		return PCI_ERS_RESULT_DISCONNECT;
+	}
+	acb->acb_flags |= (ACB_F_MESSAGE_WQBUFFER_CLEARED |
+			   ACB_F_MESSAGE_RQBUFFER_CLEARED |
+			   ACB_F_MESSAGE_WQBUFFER_READED);
+	acb->acb_flags &= ~ACB_F_SCSISTOPADAPTER;
+	INIT_LIST_HEAD(&acb->ccb_free_list);
+
+	error = arcmsr_alloc_ccb_pool(acb);
+	if (error)
+		return PCI_ERS_RESULT_DISCONNECT;
+
+	error = request_irq(pdev->irq, arcmsr_do_interrupt,
+			IRQF_DISABLED | IRQF_SHARED, "arcmsr", acb);
+	if (error)
+		return PCI_ERS_RESULT_DISCONNECT;
+
+	arcmsr_iop_init(acb);
+	if (strncmp(acb->firm_version, "V1.42", 5) >= 0)
+	      host->max_sectors = ARCMSR_MAX_XFER_SECTORS_B;
+
+	pci_set_drvdata(pdev, host);
+
+	error = scsi_add_host(host, &pdev->dev);
+	if (error)
+		return PCI_ERS_RESULT_DISCONNECT;
+
+	error = arcmsr_alloc_sysfs_attr(acb);
+	if (error)
+		return PCI_ERS_RESULT_DISCONNECT;
+
+	scsi_scan_host(host);
+	return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void arcmsr_pci_ers_need_reset_forepart(struct pci_dev *pdev)
+{
+	struct Scsi_Host *host = pci_get_drvdata(pdev);
+	struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
+	struct MessageUnit __iomem *reg = acb->pmu;
+	struct CommandControlBlock *ccb;
+	/*clear and abort all outbound posted Q*/
+	int i = 0, found = 0;
+	int id, lun;
+	uint32_t flag_ccb, outbound_intstatus;
+
+	while (((flag_ccb = readl(&reg->outbound_queueport)) != 0xFFFFFFFF) &&
+								(i++ < 256)){
+			ccb = (struct CommandControlBlock *)(acb->vir2phy_offset
+							 + (flag_ccb << 5));
+			if (ccb){
+				if ((ccb->acb != acb)||(ccb->startdone !=
+							ARCMSR_CCB_START)){
+					printk(KERN_NOTICE "arcmsr%d: polling \
+					get an illegal ccb"" command done ccb = '0x%p'"
+					"ccboutstandingcount = %d \n",
+					acb->host->host_no, ccb,
+					atomic_read(&acb->ccboutstandingcount));
+					continue;
+				}
+
+				id = ccb->pcmd->device->id;
+				lun = ccb->pcmd->device->lun;
+				if (!(flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR)) {
+					if (acb->devstate[id][lun] ==
+								ARECA_RAID_GONE)
+						acb->devstate[id][lun] =
+								ARECA_RAID_GOOD;
+					ccb->pcmd->result = DID_OK << 16;
+					arcmsr_ccb_complete(ccb, 1);
+				}
+				else {
+					switch(ccb->arcmsr_cdb.DeviceStatus) {
+					case ARCMSR_DEV_SELECT_TIMEOUT: {
+							acb->devstate[id][lun] =
+							ARECA_RAID_GONE;
+							ccb->pcmd->result =
+							DID_NO_CONNECT << 16;
+							arcmsr_ccb_complete(ccb, 1);
+					}
+					break;
+
+					case ARCMSR_DEV_ABORTED:
+
+					case ARCMSR_DEV_INIT_FAIL: {
+							acb->devstate[id][lun] =
+							 ARECA_RAID_GONE;
+							ccb->pcmd->result =
+							DID_BAD_TARGET << 16;
+							arcmsr_ccb_complete(ccb, 1);
+					}
+					break;
+
+					case ARCMSR_DEV_CHECK_CONDITION: {
+							acb->devstate[id][lun] =
+							 ARECA_RAID_GOOD;
+							arcmsr_report_sense_info(ccb);
+							arcmsr_ccb_complete(ccb, 1);
+					}
+					break;
+
+					default:
+							printk(KERN_NOTICE
+								"arcmsr%d: scsi \
+								id = %d lun = %d"
+								" polling and \
+								getting command \
+								error done"
+								"but got unknown \
+							DeviceStatus = 0x%x \n"
+							, acb->host->host_no,
+								id, lun,
+						ccb->arcmsr_cdb.DeviceStatus);
+							acb->devstate[id][lun] =
+								ARECA_RAID_GONE;
+							ccb->pcmd->result =
+							DID_BAD_TARGET << 16;
+							arcmsr_ccb_complete(ccb, 1);
+					break;
+					}
+				}
+				found = 1;
+			}
+		}
+	if (found){
+		outbound_intstatus = readl(&reg->outbound_intstatus) &
+							acb->outbound_int_enable;
+		writel(outbound_intstatus, &reg->outbound_intstatus);
+		/*clear interrupt*/
+		    }
+	return;
+}
+
 
+static void arcmsr_pci_ers_disconnect_forepart(struct pci_dev *pdev)
+{
+	struct Scsi_Host *host = pci_get_drvdata(pdev);
+	struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
+	struct MessageUnit __iomem *reg = acb->pmu;
+	struct CommandControlBlock *ccb;
+	/*clear and abort all outbound posted Q*/
+	int i = 0, found = 0;
+	int id, lun;
+	uint32_t flag_ccb, outbound_intstatus;
+
+	while (((flag_ccb = readl(&reg->outbound_queueport)) != 0xFFFFFFFF) &&
+								(i++ < 256)){
+			ccb = (struct CommandControlBlock *)(acb->vir2phy_offset +
+							(flag_ccb << 5));
+			if (ccb){
+				if ((ccb->acb != acb)||(ccb->startdone !=
+							ARCMSR_CCB_START)){
+					printk(KERN_NOTICE
+						"arcmsr%d: polling get an illegal ccb"
+						" command done ccb = '0x%p'"
+						"ccboutstandingcount = %d \n",
+						acb->host->host_no, ccb,
+						atomic_read(&acb->ccboutstandingcount));
+					continue;
+			}
+
+			id = ccb->pcmd->device->id;
+			lun = ccb->pcmd->device->lun;
+			if (!(flag_ccb & ARCMSR_CCBREPLY_FLAG_ERROR))	{
+				if (acb->devstate[id][lun] == ARECA_RAID_GONE)
+					acb->devstate[id][lun] = ARECA_RAID_GOOD;
+				ccb->pcmd->result = DID_OK << 16;
+				arcmsr_ccb_complete(ccb, 1);
+			}
+			else {
+				switch(ccb->arcmsr_cdb.DeviceStatus) {
+				case ARCMSR_DEV_SELECT_TIMEOUT: {
+						acb->devstate[id][lun] =
+								ARECA_RAID_GONE;
+						ccb->pcmd->result =
+							DID_NO_CONNECT << 16;
+						arcmsr_ccb_complete(ccb, 1);
+				}
+				break;
+
+				case ARCMSR_DEV_ABORTED:
+
+				case ARCMSR_DEV_INIT_FAIL: {
+						acb->devstate[id][lun] =
+								ARECA_RAID_GONE;
+						ccb->pcmd->result =
+							DID_BAD_TARGET << 16;
+						arcmsr_ccb_complete(ccb, 1);
+				}
+				break;
+
+				case ARCMSR_DEV_CHECK_CONDITION: {
+						acb->devstate[id][lun] =
+								ARECA_RAID_GOOD;
+						arcmsr_report_sense_info(ccb);
+						arcmsr_ccb_complete(ccb, 1);
+				}
+				break;
+
+				default:
+						printk(KERN_NOTICE "arcmsr%d: \
+							scsi id = %d lun = %d"
+								" polling and \
+						getting command error done"
+								"but got unknown \
+						 DeviceStatus = 0x%x \n"
+								, acb->host->host_no,
+					id, lun, ccb->arcmsr_cdb.DeviceStatus);
+							acb->devstate[id][lun] =
+								ARECA_RAID_GONE;
+							ccb->pcmd->result =
+							DID_BAD_TARGET << 16;
+							arcmsr_ccb_complete(ccb, 1);
+				break;
+				}
+			}
+			found = 1;
+		}
+	}
+	if (found){
+		outbound_intstatus = readl(&reg->outbound_intstatus) &
+						acb->outbound_int_enable;
+		writel(outbound_intstatus, &reg->outbound_intstatus);
+		/*clear interrupt*/
+	}
+	return;
+}
+
+static pci_ers_result_t arcmsr_pci_error_detected(struct pci_dev *pdev,
+						pci_channel_state_t state)
+{
+	switch (state) {
+	case pci_channel_io_frozen:
+			arcmsr_pci_ers_need_reset_forepart(pdev);
+			return PCI_ERS_RESULT_NEED_RESET;
+	case pci_channel_io_perm_failure:
+			arcmsr_pci_ers_disconnect_forepart(pdev);
+			return PCI_ERS_RESULT_DISCONNECT;
+			break;
+	default:
+			return PCI_ERS_RESULT_NEED_RESET;
+	}
+}
-- 
cgit v1.1