[SCSI] megaraid_sas: Add support for MegaRAID 9360/9380 12GB/s controllers

Signed-off-by: Adam Radford <aradford@gmail.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index bc83eaa..6b35625 100644
--- a/drivers/scsi/megaraid/megaraid_sas.h
+++ b/drivers/scsi/megaraid/megaraid_sas.h
@@ -48,6 +48,7 @@
 #define	PCI_DEVICE_ID_LSI_SAS0073SKINNY		0x0073
 #define	PCI_DEVICE_ID_LSI_SAS0071SKINNY		0x0071
 #define	PCI_DEVICE_ID_LSI_FUSION		0x005b
+#define PCI_DEVICE_ID_LSI_INVADER		0x005d
 
 /*
  * =====================================
@@ -221,6 +222,7 @@
 	MFI_STAT_RESERVATION_IN_PROGRESS = 0x36,
 	MFI_STAT_I2C_ERRORS_DETECTED = 0x37,
 	MFI_STAT_PCI_ERRORS_DETECTED = 0x38,
+	MFI_STAT_CONFIG_SEQ_MISMATCH = 0x67,
 
 	MFI_STAT_INVALID_STATUS = 0xFF
 };
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index 9d5a14a..f2cf768 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -114,6 +114,8 @@
 	/* xscale IOP */
 	{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FUSION)},
 	/* Fusion */
+	{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_INVADER)},
+	/* Invader */
 	{}
 };
 
@@ -1583,7 +1585,8 @@
 {
 	if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
 	    (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) ||
-	    (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION)) {
+	    (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) ||
+	    (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER)) {
 		writel(MFI_STOP_ADP, &instance->reg_set->doorbell);
 	} else {
 		writel(MFI_STOP_ADP, &instance->reg_set->inbound_doorbell);
@@ -1957,7 +1960,8 @@
 	/*
 	 * First wait for all commands to complete
 	 */
-	if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION)
+	if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) ||
+	    (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER))
 		ret = megasas_reset_fusion(scmd->device->host);
 	else
 		ret = megasas_generic_reset(scmd);
@@ -2656,7 +2660,9 @@
 				(instance->pdev->device ==
 				 PCI_DEVICE_ID_LSI_SAS0071SKINNY) ||
 				(instance->pdev->device ==
-				 PCI_DEVICE_ID_LSI_FUSION)) {
+				 PCI_DEVICE_ID_LSI_FUSION) ||
+				(instance->pdev->device ==
+				PCI_DEVICE_ID_LSI_INVADER)) {
 				writel(
 				  MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG,
 				  &instance->reg_set->doorbell);
@@ -2676,7 +2682,9 @@
 				(instance->pdev->device ==
 				 PCI_DEVICE_ID_LSI_SAS0071SKINNY) ||
 			    (instance->pdev->device ==
-			     PCI_DEVICE_ID_LSI_FUSION)) {
+			     PCI_DEVICE_ID_LSI_FUSION) ||
+			    (instance->pdev->device ==
+			     PCI_DEVICE_ID_LSI_INVADER)) {
 				writel(MFI_INIT_HOTPLUG,
 				       &instance->reg_set->doorbell);
 			} else
@@ -2697,11 +2705,15 @@
 				(instance->pdev->device ==
 				PCI_DEVICE_ID_LSI_SAS0071SKINNY)  ||
 				(instance->pdev->device
-					== PCI_DEVICE_ID_LSI_FUSION)) {
+					== PCI_DEVICE_ID_LSI_FUSION) ||
+				(instance->pdev->device
+					== PCI_DEVICE_ID_LSI_INVADER)) {
 				writel(MFI_RESET_FLAGS,
 					&instance->reg_set->doorbell);
-				if (instance->pdev->device ==
-				    PCI_DEVICE_ID_LSI_FUSION) {
+				if ((instance->pdev->device ==
+				    PCI_DEVICE_ID_LSI_FUSION) ||
+				    (instance->pdev->device ==
+				     PCI_DEVICE_ID_LSI_INVADER)) {
 					for (i = 0; i < (10 * 1000); i += 20) {
 						if (readl(
 							    &instance->
@@ -3498,6 +3510,7 @@
 
 	switch (instance->pdev->device) {
 	case PCI_DEVICE_ID_LSI_FUSION:
+	case PCI_DEVICE_ID_LSI_INVADER:
 		instance->instancet = &megasas_instance_template_fusion;
 		break;
 	case PCI_DEVICE_ID_LSI_SAS1078R:
@@ -3894,7 +3907,8 @@
 	host->max_cmd_len = 16;
 
 	/* Fusion only supports host reset */
-	if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) {
+	if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) ||
+	    (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER)) {
 		host->hostt->eh_device_reset_handler = NULL;
 		host->hostt->eh_bus_reset_handler = NULL;
 	}
@@ -4004,6 +4018,7 @@
 
 	switch (instance->pdev->device) {
 	case PCI_DEVICE_ID_LSI_FUSION:
+	case PCI_DEVICE_ID_LSI_INVADER:
 	{
 		struct fusion_context *fusion;
 
@@ -4096,7 +4111,8 @@
 	instance->last_time = 0;
 	instance->disableOnlineCtrlReset = 1;
 
-	if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION)
+	if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) ||
+	    (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER))
 		INIT_WORK(&instance->work_init, megasas_fusion_ocr_wq);
 	else
 		INIT_WORK(&instance->work_init, process_fw_state_change_wq);
@@ -4161,7 +4177,8 @@
 	free_irq(instance->msi_flag ? instance->msixentry.vector :
 		 instance->pdev->irq, instance);
 fail_irq:
-	if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION)
+	if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) ||
+	    (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER))
 		megasas_release_fusion(instance);
 	else
 		megasas_release_mfi(instance);
@@ -4368,6 +4385,7 @@
 
 	switch (instance->pdev->device) {
 	case PCI_DEVICE_ID_LSI_FUSION:
+	case PCI_DEVICE_ID_LSI_INVADER:
 	{
 		megasas_reset_reply_desc(instance);
 		if (megasas_ioc_init_fusion(instance)) {
@@ -4501,6 +4519,7 @@
 
 	switch (instance->pdev->device) {
 	case PCI_DEVICE_ID_LSI_FUSION:
+	case PCI_DEVICE_ID_LSI_INVADER:
 		megasas_release_fusion(instance);
 		for (i = 0; i < 2 ; i++)
 			if (fusion->ld_map[i])
diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c
index 5a5af1f..5255dd6 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fp.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fp.c
@@ -52,6 +52,7 @@
 #include <scsi/scsi_host.h>
 
 #include "megaraid_sas_fusion.h"
+#include "megaraid_sas.h"
 #include <asm/div64.h>
 
 #define ABS_DIFF(a, b)   (((a) > (b)) ? ((a) - (b)) : ((b) - (a)))
@@ -226,8 +227,9 @@
 *    span          - Span number
 *    block         - Absolute Block number in the physical disk
 */
-u8 MR_GetPhyParams(u32 ld, u64 stripRow, u16 stripRef, u64 *pdBlock,
-		   u16 *pDevHandle, struct RAID_CONTEXT *pRAID_Context,
+u8 MR_GetPhyParams(struct megasas_instance *instance, u32 ld, u64 stripRow,
+		   u16 stripRef, u64 *pdBlock, u16 *pDevHandle,
+		   struct RAID_CONTEXT *pRAID_Context,
 		   struct MR_FW_RAID_MAP_ALL *map)
 {
 	struct MR_LD_RAID  *raid = MR_LdRaidGet(ld, map);
@@ -279,7 +281,8 @@
 		*pDevHandle = MR_PdDevHandleGet(pd, map);
 	else {
 		*pDevHandle = MR_PD_INVALID; /* set dev handle as invalid. */
-		if (raid->level >= 5)
+		if ((raid->level >= 5) &&
+		    (instance->pdev->device != PCI_DEVICE_ID_LSI_INVADER))
 			pRAID_Context->regLockFlags = REGION_TYPE_EXCLUSIVE;
 		else if (raid->level == 1) {
 			/* Get alternate Pd. */
@@ -306,7 +309,8 @@
 * This function will return 0 if region lock was acquired OR return num strips
 */
 u8
-MR_BuildRaidContext(struct IO_REQUEST_INFO *io_info,
+MR_BuildRaidContext(struct megasas_instance *instance,
+		    struct IO_REQUEST_INFO *io_info,
 		    struct RAID_CONTEXT *pRAID_Context,
 		    struct MR_FW_RAID_MAP_ALL *map)
 {
@@ -394,8 +398,12 @@
 	}
 
 	pRAID_Context->timeoutValue     = map->raidMap.fpPdIoTimeoutSec;
-	pRAID_Context->regLockFlags     = (isRead) ? REGION_TYPE_SHARED_READ :
-		raid->regTypeReqOnWrite;
+	if (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER)
+		pRAID_Context->regLockFlags = (isRead) ?
+			raid->regTypeReqOnRead : raid->regTypeReqOnWrite;
+	else
+		pRAID_Context->regLockFlags = (isRead) ?
+			REGION_TYPE_SHARED_READ : raid->regTypeReqOnWrite;
 	pRAID_Context->VirtualDiskTgtId = raid->targetId;
 	pRAID_Context->regLockRowLBA    = regStart;
 	pRAID_Context->regLockLength    = regSize;
@@ -404,7 +412,8 @@
 	/*Get Phy Params only if FP capable, or else leave it to MR firmware
 	  to do the calculation.*/
 	if (io_info->fpOkForIo) {
-		retval = MR_GetPhyParams(ld, start_strip, ref_in_start_stripe,
+		retval = MR_GetPhyParams(instance, ld, start_strip,
+					 ref_in_start_stripe,
 					 &io_info->pdBlock,
 					 &io_info->devHandle, pRAID_Context,
 					 map);
@@ -415,7 +424,8 @@
 	} else if (isRead) {
 		uint stripIdx;
 		for (stripIdx = 0; stripIdx < num_strips; stripIdx++) {
-			if (!MR_GetPhyParams(ld, start_strip + stripIdx,
+			if (!MR_GetPhyParams(instance, ld,
+					     start_strip + stripIdx,
 					     ref_in_start_stripe,
 					     &io_info->pdBlock,
 					     &io_info->devHandle,
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c
index 120499a..ad6bd06 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c
@@ -74,7 +74,8 @@
 		     struct megasas_cmd *cmd);
 
 u8
-MR_BuildRaidContext(struct IO_REQUEST_INFO *io_info,
+MR_BuildRaidContext(struct megasas_instance *instance,
+		    struct IO_REQUEST_INFO *io_info,
 		    struct RAID_CONTEXT *pRAID_Context,
 		    struct MR_FW_RAID_MAP_ALL *map);
 u16 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_FW_RAID_MAP_ALL *map);
@@ -1038,7 +1039,9 @@
 	case MFI_STAT_DEVICE_NOT_FOUND:
 		cmd->scmd->result = DID_BAD_TARGET << 16;
 		break;
-
+	case MFI_STAT_CONFIG_SEQ_MISMATCH:
+		cmd->scmd->result = DID_IMM_RETRY << 16;
+		break;
 	default:
 		printk(KERN_DEBUG "megasas: FW status %#x\n", status);
 		cmd->scmd->result = DID_ERROR << 16;
@@ -1061,14 +1064,17 @@
 			struct MPI25_IEEE_SGE_CHAIN64 *sgl_ptr,
 			struct megasas_cmd_fusion *cmd)
 {
-	int i, sg_processed;
-	int sge_count, sge_idx;
+	int i, sg_processed, sge_count;
 	struct scatterlist *os_sgl;
 	struct fusion_context *fusion;
 
 	fusion = instance->ctrl_context;
 
-	cmd->io_request->ChainOffset = 0;
+	if (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) {
+		struct MPI25_IEEE_SGE_CHAIN64 *sgl_ptr_end = sgl_ptr;
+		sgl_ptr_end += fusion->max_sge_in_main_msg - 1;
+		sgl_ptr_end->Flags = 0;
+	}
 
 	sge_count = scsi_dma_map(scp);
 
@@ -1077,16 +1083,14 @@
 	if (sge_count > instance->max_num_sge || !sge_count)
 		return sge_count;
 
-	if (sge_count > fusion->max_sge_in_main_msg) {
-		/* One element to store the chain info */
-		sge_idx = fusion->max_sge_in_main_msg - 1;
-	} else
-		sge_idx = sge_count;
-
 	scsi_for_each_sg(scp, os_sgl, sge_count, i) {
 		sgl_ptr->Length = sg_dma_len(os_sgl);
 		sgl_ptr->Address = sg_dma_address(os_sgl);
 		sgl_ptr->Flags = 0;
+		if (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) {
+			if (i == sge_count - 1)
+				sgl_ptr->Flags = IEEE_SGE_FLAGS_END_OF_LIST;
+		}
 		sgl_ptr++;
 
 		sg_processed = i + 1;
@@ -1095,13 +1099,30 @@
 		    (sge_count > fusion->max_sge_in_main_msg)) {
 
 			struct MPI25_IEEE_SGE_CHAIN64 *sg_chain;
-			cmd->io_request->ChainOffset =
-				fusion->chain_offset_io_request;
+			if (instance->pdev->device ==
+			    PCI_DEVICE_ID_LSI_INVADER) {
+				if ((cmd->io_request->IoFlags &
+				MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH) !=
+				MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH)
+					cmd->io_request->ChainOffset =
+						fusion->
+						chain_offset_io_request;
+				else
+					cmd->io_request->ChainOffset = 0;
+			} else
+				cmd->io_request->ChainOffset =
+					fusion->chain_offset_io_request;
+
 			sg_chain = sgl_ptr;
 			/* Prepare chain element */
 			sg_chain->NextChainOffset = 0;
-			sg_chain->Flags = (IEEE_SGE_FLAGS_CHAIN_ELEMENT |
-					   MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR);
+			if (instance->pdev->device ==
+			    PCI_DEVICE_ID_LSI_INVADER)
+				sg_chain->Flags = IEEE_SGE_FLAGS_CHAIN_ELEMENT;
+			else
+				sg_chain->Flags =
+					(IEEE_SGE_FLAGS_CHAIN_ELEMENT |
+					 MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR);
 			sg_chain->Length =  (sizeof(union MPI2_SGE_IO_UNION)
 					     *(sge_count - sg_processed));
 			sg_chain->Address = cmd->sg_frame_phys_addr;
@@ -1394,7 +1415,8 @@
 		io_request->RaidContext.regLockFlags  = 0;
 		fp_possible = 0;
 	} else {
-		if (MR_BuildRaidContext(&io_info, &io_request->RaidContext,
+		if (MR_BuildRaidContext(instance, &io_info,
+					&io_request->RaidContext,
 					local_map_ptr))
 			fp_possible = io_info.fpOkForIo;
 	}
@@ -1407,6 +1429,20 @@
 		cmd->request_desc->SCSIIO.RequestFlags =
 			(MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY
 			 << MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+		if (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) {
+			if (io_request->RaidContext.regLockFlags ==
+			    REGION_TYPE_UNUSED)
+				cmd->request_desc->SCSIIO.RequestFlags =
+					(MEGASAS_REQ_DESCRIPT_FLAGS_NO_LOCK <<
+					MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+			io_request->RaidContext.Type = MPI2_TYPE_CUDA;
+			io_request->RaidContext.nseg = 0x1;
+			io_request->IoFlags |=
+			  MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH;
+			io_request->RaidContext.regLockFlags |=
+			  (MR_RL_FLAGS_GRANT_DESTINATION_CUDA |
+			   MR_RL_FLAGS_SEQ_NUM_ENABLE);
+		}
 		if ((fusion->load_balance_info[device_id].loadBalanceFlag) &&
 		    (io_info.isRead)) {
 			io_info.devHandle =
@@ -1421,11 +1457,23 @@
 	} else {
 		io_request->RaidContext.timeoutValue =
 			local_map_ptr->raidMap.fpPdIoTimeoutSec;
-		io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
-		io_request->DevHandle = device_id;
 		cmd->request_desc->SCSIIO.RequestFlags =
 			(MEGASAS_REQ_DESCRIPT_FLAGS_LD_IO
 			 << MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+		if (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) {
+			if (io_request->RaidContext.regLockFlags ==
+			    REGION_TYPE_UNUSED)
+				cmd->request_desc->SCSIIO.RequestFlags =
+					(MEGASAS_REQ_DESCRIPT_FLAGS_NO_LOCK <<
+					MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+			io_request->RaidContext.Type = MPI2_TYPE_CUDA;
+			io_request->RaidContext.regLockFlags |=
+				(MR_RL_FLAGS_GRANT_DESTINATION_CPU0 |
+				 MR_RL_FLAGS_SEQ_NUM_ENABLE);
+			io_request->RaidContext.nseg = 0x1;
+		}
+		io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
+		io_request->DevHandle = device_id;
 	} /* Not FP */
 }
 
@@ -1508,8 +1556,10 @@
 	io_request->EEDPFlags = 0;
 	io_request->Control = 0;
 	io_request->EEDPBlockSize = 0;
-	io_request->IoFlags = 0;
+	io_request->ChainOffset = 0;
 	io_request->RaidContext.RAIDFlags = 0;
+	io_request->RaidContext.Type = 0;
+	io_request->RaidContext.nseg = 0;
 
 	memcpy(io_request->CDB.CDB32, scp->cmnd, scp->cmd_len);
 	/*
@@ -1863,6 +1913,14 @@
 
 	fusion = instance->ctrl_context;
 	io_req = cmd->io_request;
+
+	if (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) {
+		struct MPI25_IEEE_SGE_CHAIN64 *sgl_ptr_end =
+			(struct MPI25_IEEE_SGE_CHAIN64 *)&io_req->SGL;
+		sgl_ptr_end += fusion->max_sge_in_main_msg - 1;
+		sgl_ptr_end->Flags = 0;
+	}
+
 	mpi25_ieee_chain =
 	  (struct MPI25_IEEE_SGE_CHAIN64 *)&io_req->SGL.IeeeChain;
 
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h
index 82b577a..9556c08 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.h
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h
@@ -44,6 +44,14 @@
 #define HOST_DIAG_RESET_ADAPTER			    0x4
 #define MEGASAS_FUSION_MAX_RESET_TRIES		    3
 
+/* Invader defines */
+#define MPI2_TYPE_CUDA				    0x2
+#define MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH   0x4000
+#define	MR_RL_FLAGS_GRANT_DESTINATION_CPU0	    0x00
+#define	MR_RL_FLAGS_GRANT_DESTINATION_CPU1	    0x10
+#define	MR_RL_FLAGS_GRANT_DESTINATION_CUDA	    0x80
+#define MR_RL_FLAGS_SEQ_NUM_ENABLE		    0x8
+
 /* T10 PI defines */
 #define MR_PROT_INFO_TYPE_CONTROLLER                0x8
 #define MEGASAS_SCSI_VARIABLE_LENGTH_CMD            0x7f
@@ -70,7 +78,7 @@
  */
 #define MEGASAS_REQ_DESCRIPT_FLAGS_LD_IO           0x7
 #define MEGASAS_REQ_DESCRIPT_FLAGS_MFA             0x1
-
+#define MEGASAS_REQ_DESCRIPT_FLAGS_NO_LOCK	   0x2
 #define MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT      1
 
 #define MEGASAS_FP_CMD_LEN	16
@@ -82,7 +90,9 @@
  */
 
 struct RAID_CONTEXT {
-	u16     resvd0;
+	u8	Type:4;
+	u8	nseg:4;
+	u8	resvd0;
 	u16     timeoutValue;
 	u8      regLockFlags;
 	u8      resvd1;
@@ -527,7 +537,7 @@
 	u8      ldState;
 	u8      regTypeReqOnWrite;
 	u8      modFactor;
-	u8      reserved2[1];
+	u8	regTypeReqOnRead;
 	u16     seqNum;
 
 	struct {