diff --git a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
index b441f0d26081..d03295806657 100644
--- a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
+++ b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
@@ -18,6 +18,7 @@ select:
           - qcom,eliza-ufshc
           - qcom,hawi-ufshc
           - qcom,kaanapali-ufshc
+          - qcom,maili-ufshc
           - qcom,nord-ufshc
           - qcom,sm8650-ufshc
           - qcom,sm8750-ufshc
@@ -31,6 +32,7 @@ properties:
           - qcom,eliza-ufshc
           - qcom,hawi-ufshc
           - qcom,kaanapali-ufshc
+          - qcom,maili-ufshc
           - qcom,nord-ufshc
           - qcom,sm8650-ufshc
           - qcom,sm8750-ufshc
@@ -78,6 +80,7 @@ allOf:
           contains:
             enum:
               - qcom,eliza-ufshc
+              - qcom,maili-ufshc
               - qcom,nord-ufshc
     then:
       properties:
diff --git a/Documentation/devicetree/bindings/ufs/ufs-common.yaml b/Documentation/devicetree/bindings/ufs/ufs-common.yaml
index ed97f5682509..cc32e1189d50 100644
--- a/Documentation/devicetree/bindings/ufs/ufs-common.yaml
+++ b/Documentation/devicetree/bindings/ufs/ufs-common.yaml
@@ -105,6 +105,64 @@ properties:
       Restricts the UFS controller to rate-a or rate-b for both TX and
       RX directions.
 
+  tx-precode-enable-g6:
+    $ref: /schemas/types.yaml#/definitions/uint32-matrix
+    minItems: 1
+    items:
+      - items:
+          - description: Host_Lane0 precode
+            enum: [0, 1]
+          - description: Device_Lane0 precode
+            enum: [0, 1]
+      - items:
+          - description: Host_Lane1 precode
+            enum: [0, 1]
+          - description: Device_Lane1 precode
+            enum: [0, 1]
+    description:
+      Static TX Precode enable values for HS-G6 only.
+
+patternProperties:
+  "^txeq-preshoot-g[1-6]$":
+    $ref: /schemas/types.yaml#/definitions/uint32-matrix
+    minItems: 1
+    items:
+      - items:
+          - description: Host_Lane0 Preshoot value
+            enum: [0, 1, 2, 3, 4, 5, 6, 7]
+          - description: Device_Lane0 Preshoot value
+            enum: [0, 1, 2, 3, 4, 5, 6, 7]
+      - items:
+          - description: Host_Lane1 Preshoot value
+            enum: [0, 1, 2, 3, 4, 5, 6, 7]
+          - description: Device_Lane1 Preshoot value
+            enum: [0, 1, 2, 3, 4, 5, 6, 7]
+    description: |
+      Static TX Equalization PreShoot settings for High Speed Gears. These
+      values are programmed to the corresponding UniPro PA layer attribute
+      PA_TxEQG[1-6]Setting. Each value selects a Pre-Shoot level as defined
+      by the MIPI M-PHY specification (TX_HS_PreShoot_Setting).
+
+  "^txeq-deemphasis-g[1-6]$":
+    $ref: /schemas/types.yaml#/definitions/uint32-matrix
+    minItems: 1
+    items:
+      - items:
+          - description: Host_Lane0 DeEmphasis value
+            enum: [0, 1, 2, 3, 4, 5, 6, 7]
+          - description: Device_Lane0 DeEmphasis value
+            enum: [0, 1, 2, 3, 4, 5, 6, 7]
+      - items:
+          - description: Host_Lane1 DeEmphasis value
+            enum: [0, 1, 2, 3, 4, 5, 6, 7]
+          - description: Device_Lane1 DeEmphasis value
+            enum: [0, 1, 2, 3, 4, 5, 6, 7]
+    description: |
+      Static TX Equalization DeEmphasis settings for High Speed Gears. These
+      values are programmed to the corresponding UniPro PA layer attribute
+      PA_TxEQG[1-6]Setting. Each value selects a De-Emphasis level as defined
+      by the MIPI M-PHY specification (TX_HS_DeEmphasis_Setting).
+
 dependencies:
   freq-table-hz: [ clocks ]
   operating-points-v2: [ clocks, clock-names ]
diff --git a/Documentation/scsi/index.rst b/Documentation/scsi/index.rst
index f15a0f348ae4..52970f0159ca 100644
--- a/Documentation/scsi/index.rst
+++ b/Documentation/scsi/index.rst
@@ -56,6 +56,7 @@ SCSI host adapter drivers
    g_NCR5380
    hpsa
    hptiop
+   leapraid
    libsas
    lpfc
    megaraid
diff --git a/Documentation/scsi/leapraid.rst b/Documentation/scsi/leapraid.rst
new file mode 100644
index 000000000000..99930ce2b8d0
--- /dev/null
+++ b/Documentation/scsi/leapraid.rst
@@ -0,0 +1,110 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================
+LeapRAID Driver for Linux
+=========================
+
+Introduction
+============
+
+LeapRAID is a storage RAID controller driver developed by LeapIO Tech Inc. The
+controller targets enterprise storage, cloud infrastructure, high performance
+computing (HPC), and AI workloads.
+
+It provides high-performance storage virtualization over PCI Express Gen4
+and supports both SAS and SATA HDDs and SSDs. It offers both Host Bus Adapter
+and RAID modes to meet diverse deployment requirements.
+
+Supported devices
+=================
+
+- LeapHBA-8200C
+
+Features
+========
+- PCIe Gen4 x8 host interface
+- Support for SAS and SATA devices
+- RAID levels: 0, 1, 10, 5, 50, 6, 60
+- Advanced error handling and end-to-end data integrity
+
+LeapRAID specific host attributes
+=================================
+
+::
+
+   /sys/class/scsi_host/host*/fw_queue_depth
+   /sys/class/scsi_host/host*/host_sas_address
+   /sys/class/scsi_host/host*/board_name
+
+The host "fw_queue_depth" read-only attribute shows the firmware queue
+depth of the host.
+
+The host "host_sas_address" read-only attribute shows the SAS address
+of the host.
+
+The host "board_name" read-only attribute shows the board name reported
+by manufacturing page 0.
+
+LeapRAID specific disk attributes
+=================================
+
+::
+
+   /sys/class/scsi_disk/host:bus:target:lun/device/sas_device_handle
+   /sys/class/scsi_disk/host:bus:target:lun/device/ncq_cmd_prio_enable
+
+The read-only attribute "sas_device_handle" represents the disk's device
+handle, which is a unique identifier maintained by the firmware.
+
+This attribute "ncq_cmd_prio_enable" controls NCQ command priority. A value
+of 0 disables NCQ priority handling for RT-priority I/O. Writing 1 enables
+NCQ priority handling when the device reports support for the feature through
+VPD page 0x89. Unsupported devices keep the effective state at 0.
+
+LeapRAID module parameters
+==========================
+
+The following module parameters can be configured at driver load time to
+control driver behavior and tuning options.
+
+1. open_pcie_trace
+------------------
+
+This parameter controls whether PCIe transaction tracing is enabled in the
+driver. When set to 1, PCIe trace collection is enabled by default, allowing
+detailed tracing of PCIe operations for debugging and performance analysis.
+Setting it to 0 disables the trace functionality to reduce overhead in
+production environments.
+
+2. enable_mp
+------------
+
+This parameter enables or disables multipath support for target devices.
+When set to 1, multipath functionality is enabled (default), allowing
+multiple paths to be established. Setting it to 0 disables multipath
+handling.
+
+3. max_msix_vectors
+-------------------
+
+This parameter sets the upper limit on the number of MSI-X interrupt
+vectors that the driver will request during initialization. The default
+value of -1 allows the driver to use all available vectors as provided
+by the device. Setting a positive integer restricts the number of vectors.
+
+4. poll_queues
+--------------
+
+This parameter specifies the number of I/O queues to be used when operating
+in io_uring poll mode. The default value is 0.
+
+File Location
+=============
+The driver source is located at:
+
+``drivers/scsi/leapraid/``
+
+.. note::
+
+   This document is intended for kernel developers and system
+   integrators who need to build, test, and deploy the LeapRAID driver.
diff --git a/MAINTAINERS b/MAINTAINERS
index 15011f5752a9..a8a58f059c49 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11788,7 +11788,7 @@ F:	Documentation/devicetree/bindings/infiniband/hisilicon-hns-roce.txt
 F:	drivers/infiniband/hw/hns/
 
 HISILICON SAS Controller
-M:	Yihang Li <liyihang9@h-partners.com>
+M:	Xingui Yang <yangxingui@huawei.com>
 S:	Supported
 W:	http://www.hisilicon.com
 F:	Documentation/devicetree/bindings/scsi/hisilicon-sas.txt
@@ -14650,6 +14650,13 @@ S:	Maintained
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
 F:	scripts/leaking_addresses.pl
 
+LEAPIO SCSI RAID DRIVER
+M:	Dongdong Hao <doubled@leap-io-kernel.com>
+L:	linux-scsi@vger.kernel.org
+S:	Supported
+F:	Documentation/scsi/leapraid.rst
+F:	drivers/scsi/leapraid/
+
 LED SUBSYSTEM
 M:	Lee Jones <lee@kernel.org>
 M:	Pavel Machek <pavel@kernel.org>
@@ -24300,9 +24307,8 @@ F:	include/uapi/scsi/
 F:	include/ufs/
 
 SCSI TAPE DRIVER
-M:	Kai Mäkisara <Kai.Makisara@kolumbus.fi>
 L:	linux-scsi@vger.kernel.org
-S:	Maintained
+S:	Orphan
 F:	Documentation/scsi/st.rst
 F:	drivers/scsi/st.*
 F:	drivers/scsi/st_*.h
diff --git a/drivers/ata/pata_buddha.c b/drivers/ata/pata_buddha.c
index c36ee991d5e5..3b1f0ee2f875 100644
--- a/drivers/ata/pata_buddha.c
+++ b/drivers/ata/pata_buddha.c
@@ -253,9 +253,9 @@ static void pata_buddha_remove(struct zorro_dev *z)
 }
 
 static const struct zorro_device_id pata_buddha_zorro_tbl[] = {
-	{ ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA, BOARD_BUDDHA},
-	{ ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL, BOARD_CATWEASEL},
-	{ 0 }
+	{ .id = ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA, .driver_data = BOARD_BUDDHA },
+	{ .id = ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL, .driver_data = BOARD_CATWEASEL },
+	{ }
 };
 MODULE_DEVICE_TABLE(zorro, pata_buddha_zorro_tbl);
 
@@ -282,7 +282,7 @@ static int __init pata_buddha_late_init(void)
 	/* Manually bind to all X-Surf boards */
 	while ((z = zorro_find_device(ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, z))) {
 		static struct zorro_device_id xsurf_ent = {
-			ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, BOARD_XSURF
+			.id = ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, .driver_data = BOARD_XSURF
 		};
 
 		pata_buddha_probe(z, &xsurf_ent);
diff --git a/drivers/s390/scsi/Makefile b/drivers/s390/scsi/Makefile
index 352056eb0dd1..01b41f7c6860 100644
--- a/drivers/s390/scsi/Makefile
+++ b/drivers/s390/scsi/Makefile
@@ -3,6 +3,8 @@
 # Makefile for the S/390 specific device drivers
 #
 
+CONTEXT_ANALYSIS := y
+
 zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_dbf.o zfcp_erp.o \
 	     zfcp_fc.o zfcp_fsf.o zfcp_qdio.o zfcp_scsi.o zfcp_sysfs.o \
 	     zfcp_unit.o zfcp_diag.o
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 71f625926ae1..81fb8af408e9 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -4,7 +4,7 @@
  *
  * Debug traces for zfcp.
  *
- * Copyright IBM Corp. 2002, 2023
+ * Copyright IBM Corp. 2002, 2026
  */
 
 #define pr_fmt(fmt) "zfcp: " fmt
@@ -35,13 +35,18 @@ static inline unsigned int zfcp_dbf_plen(unsigned int offset)
 	return sizeof(struct zfcp_dbf_pay) + offset - ZFCP_DBF_PAY_MAX_REC;
 }
 
+#define ZFCP_DBF_PAY_LEVEL 1
+
 static inline
 void zfcp_dbf_pl_write(struct zfcp_dbf *dbf, void *data, u16 length, char *area,
-		       u64 req_id)
+		       u64 req_id, int level)
 {
 	struct zfcp_dbf_pay *pl = &dbf->pay_buf;
 	u16 offset = 0, rec_length;
 
+	if (unlikely(!debug_level_enabled(dbf->pay, level)))
+		return;
+
 	spin_lock(&dbf->pay_lock);
 	memset(pl, 0, sizeof(*pl));
 	pl->fsf_req_id = req_id;
@@ -51,7 +56,7 @@ void zfcp_dbf_pl_write(struct zfcp_dbf *dbf, void *data, u16 length, char *area,
 		rec_length = min((u16) ZFCP_DBF_PAY_MAX_REC,
 				 (u16) (length - offset));
 		memcpy(pl->data, data + offset, rec_length);
-		debug_event(dbf->pay, 1, pl, zfcp_dbf_plen(rec_length));
+		debug_event(dbf->pay, level, pl, zfcp_dbf_plen(rec_length));
 
 		offset += rec_length;
 		pl->counter++;
@@ -96,7 +101,27 @@ void zfcp_dbf_hba_fsf_res(char *tag, int level, struct zfcp_fsf_req *req)
 
 	rec->pl_len = q_head->log_length;
 	zfcp_dbf_pl_write(dbf, (char *)q_pref + q_head->log_start,
-			  rec->pl_len, "fsf_res", req->req_id);
+			  rec->pl_len, "fsf_res", req->req_id,
+			  ZFCP_DBF_PAY_LEVEL);
+
+	if (q_head->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) {
+		struct fsf_qtcb_bottom_support *q_bott =
+			&req->qtcb->bottom.support;
+		u32 plogi_len = 0, prli_len = 0;
+
+		if (q_bott->els1_length) {
+			rec->u.res.plogi_len = q_bott->els1_length;
+			plogi_len = min_t(u32, q_bott->els1_length,
+					  sizeof(q_bott->els));
+		}
+		if (q_bott->els2_length) {
+			rec->u.res.prli_len = q_bott->els2_length;
+			prli_len = min_t(u32, q_bott->els2_length,
+					 sizeof(q_bott->els) - plogi_len);
+		}
+		zfcp_dbf_pl_write(dbf, q_bott->els, plogi_len + prli_len,
+				  "fsf_els", req->req_id, 4);
+	}
 
 	debug_event(dbf->hba, level, rec, sizeof(*rec));
 	spin_unlock_irqrestore(&dbf->hba_lock, flags);
@@ -220,6 +245,13 @@ void zfcp_dbf_hba_fsf_uss(char *tag, struct zfcp_fsf_req *req)
 	rec->u.uss.lun = srb->fcp_lun;
 	memcpy(&rec->u.uss.queue_designator, &srb->queue_designator,
 	       sizeof(rec->u.uss.queue_designator));
+	rec->u.uss.length = srb->length;
+	rec->u.uss.res1 = srb->res1;
+	rec->u.uss.res2 = srb->res2;
+	rec->u.uss.class = srb->class;
+	rec->u.uss.res3 = srb->res3;
+	rec->u.uss.s_id = ntoh24(srb->s_id);
+	memcpy(&rec->u.uss.res4, &srb->res4, sizeof(rec->u.uss.res4));
 
 	/* status read buffer payload length */
 	rec->pl_len = (!srb->length) ? 0 : srb->length -
@@ -227,12 +259,48 @@ void zfcp_dbf_hba_fsf_uss(char *tag, struct zfcp_fsf_req *req)
 
 	if (rec->pl_len)
 		zfcp_dbf_pl_write(dbf, srb->payload.data, rec->pl_len,
-				  "fsf_uss", req->req_id);
+				  "fsf_uss", req->req_id, ZFCP_DBF_PAY_LEVEL);
 log:
 	debug_event(dbf->hba, level, rec, sizeof(*rec));
 	spin_unlock_irqrestore(&dbf->hba_lock, flags);
 }
 
+/**
+ * zfcp_dbf_hba_uas - trace event for sysfs unit add store
+ * @tag: tag indicating which kind of unit add store condition occurred
+ * @level: debug trace level
+ * @adapter: pointer to struct zfcp_adapter
+ * @wwpn: remote port wwn
+ * @fcp_lun: FCP LUN
+ * @ret: return value
+ */
+void zfcp_dbf_hba_uas(char *tag, int level, struct zfcp_adapter *adapter,
+		      u64 wwpn, u64 fcp_lun, int ret)
+{
+	struct zfcp_dbf *dbf = adapter->dbf;
+	struct zfcp_dbf_hba *rec = &dbf->hba_buf;
+	unsigned long flags;
+
+	if (unlikely(!debug_level_enabled(dbf->hba, level)))
+		return;
+
+	spin_lock_irqsave(&dbf->hba_lock, flags);
+	memset(rec, 0, sizeof(*rec));
+
+	memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN);
+	rec->id = ZFCP_DBF_HBA_UAS;
+	rec->fsf_req_id = ~0u;
+	rec->fsf_req_status = ~0u;
+	rec->fsf_cmd = ~0u;
+	rec->fsf_seq_no = ~0u;
+	rec->u.uas.wwpn = wwpn;
+	rec->u.uas.fcp_lun = fcp_lun;
+	rec->u.uas.ret = ret;
+
+	debug_event(dbf->hba, level, rec, sizeof(*rec));
+	spin_unlock_irqrestore(&dbf->hba_lock, flags);
+}
+
 /**
  * zfcp_dbf_hba_bit_err - trace event for bit error conditions
  * @tag: tag indicating which kind of bit error unsolicited status was received
@@ -732,7 +800,7 @@ void zfcp_dbf_scsi_common(char *tag, int level, struct scsi_device *sdev,
 				min_t(u16, max_t(u16, rec->pl_len,
 						 ZFCP_DBF_PAY_MAX_REC),
 				      FSF_FCP_RSP_SIZE),
-				"fcp_riu", fsf->req_id);
+				"fcp_riu", fsf->req_id, ZFCP_DBF_PAY_LEVEL);
 	}
 
 	debug_event(dbf->scsi, level, rec, sizeof(*rec));
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 4d1435c573bc..79973fb24b1c 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -3,7 +3,7 @@
  * zfcp device driver
  * debug feature declarations
  *
- * Copyright IBM Corp. 2008, 2020
+ * Copyright IBM Corp. 2008, 2026
  */
 
 #ifndef ZFCP_DBF_H
@@ -140,6 +140,8 @@ struct zfcp_dbf_hba_res {
 	u8  fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
 	u32 port_handle;
 	u32 lun_handle;
+	u32 plogi_len;
+	u32 prli_len;
 } __packed;
 
 /**
@@ -149,6 +151,13 @@ struct zfcp_dbf_hba_res {
  * @d_id: destination ID
  * @lun: logical unit number
  * @queue_designator: queue designator
+ * @length: buffer length
+ * @res1: reserved field 1
+ * @res2: reserved field 2
+ * @class: class of service
+ * @res3: reserved field 3
+ * @s_id: source ID
+ * @res4: reserved field 4
  */
 struct zfcp_dbf_hba_uss {
 	u32 status_type;
@@ -156,6 +165,25 @@ struct zfcp_dbf_hba_uss {
 	u32 d_id;
 	u64 lun;
 	u64 queue_designator;
+	u32 length;
+	u32 res1;
+	u8 res2;
+	u32 class;
+	u8 res3;
+	u32 s_id;
+	u8 res4[20];
+} __packed;
+
+/**
+ * struct zfcp_dbf_hba_uas - trace record for sysfs unit add store
+ * @wwpn: remote port wwn
+ * @fcp_lun: FCP LUN
+ * @ret: return value
+ */
+struct zfcp_dbf_hba_uas {
+	u64 wwpn;
+	u64 fcp_lun;
+	u32 ret;
 } __packed;
 
 /**
@@ -184,6 +212,7 @@ struct zfcp_dbf_hba_fces {
  * @ZFCP_DBF_HBA_BIT: bit error trace record
  * @ZFCP_DBF_HBA_BASIC: basic adapter event, only trace tag, no other data
  * @ZFCP_DBF_HBA_FCES: FC Endpoint Security trace record
+ * @ZFCP_DBF_HBA_UAS: unit add store trace record
  */
 enum zfcp_dbf_hba_id {
 	ZFCP_DBF_HBA_RES	= 1,
@@ -191,6 +220,7 @@ enum zfcp_dbf_hba_id {
 	ZFCP_DBF_HBA_BIT	= 3,
 	ZFCP_DBF_HBA_BASIC	= 4,
 	ZFCP_DBF_HBA_FCES	= 5,
+	ZFCP_DBF_HBA_UAS        = 6,
 };
 
 /**
@@ -207,6 +237,7 @@ enum zfcp_dbf_hba_id {
  * @u.uss:  data for unsolicited status buffer
  * @u.be:   data for bit error unsolicited status buffer
  * @u.fces: data for FC Endpoint Security
+ * @u.uas:  data for unit add store
  */
 struct zfcp_dbf_hba {
 	u8 id;
@@ -221,6 +252,7 @@ struct zfcp_dbf_hba {
 		struct zfcp_dbf_hba_uss uss;
 		struct fsf_bit_error_payload be;
 		struct zfcp_dbf_hba_fces fces;
+		struct zfcp_dbf_hba_uas uas;
 	} u;
 } __packed;
 
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 9f5152b42b0e..0eb9bc9d03e4 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -4,7 +4,7 @@
  *
  * External function declarations.
  *
- * Copyright IBM Corp. 2002, 2023
+ * Copyright IBM Corp. 2002, 2026
  */
 
 #ifndef ZFCP_EXT_H
@@ -49,6 +49,8 @@ extern void zfcp_dbf_hba_fsf_fces(char *tag, const struct zfcp_fsf_req *req,
 extern void zfcp_dbf_hba_fsf_reqid(const char *const tag, const int level,
 				   struct zfcp_adapter *const adapter,
 				   const u64 req_id);
+extern void zfcp_dbf_hba_uas(char *tag, int level, struct zfcp_adapter *adapter,
+			     u64 wwpn, u64 fcp_lun, int ret);
 extern void zfcp_dbf_hba_bit_err(char *, struct zfcp_fsf_req *);
 extern void zfcp_dbf_hba_def_err(struct zfcp_adapter *, u64, u16, void **);
 extern void zfcp_dbf_san_req(char *, struct zfcp_fsf_req *, u32);
@@ -152,7 +154,8 @@ extern ssize_t zfcp_fsf_scnprint_fc_security(char *buf, size_t size,
 /* zfcp_qdio.c */
 extern int zfcp_qdio_setup(struct zfcp_adapter *);
 extern void zfcp_qdio_destroy(struct zfcp_qdio *);
-extern int zfcp_qdio_sbal_get(struct zfcp_qdio *);
+extern int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio)
+	__must_hold(qdio->req_q_lock);
 extern int zfcp_qdio_send(struct zfcp_qdio *, struct zfcp_qdio_req *);
 extern int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *, struct zfcp_qdio_req *,
 				   struct scatterlist *);
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index 5e6b601af980..4b92e85ec71d 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -246,7 +246,9 @@ struct fsf_status_read_buffer {
 	u8 d_id[3];
 	u32 class;
 	u64 fcp_lun;
-	u8  res3[24];
+	u8 res3;
+	u8 s_id[3];
+	u8 res4[20];
 	union {
 		u8  data[FSF_STATUS_READ_PAYLOAD_SIZE];
 		u32 word[FSF_STATUS_READ_PAYLOAD_SIZE/sizeof(u32)];
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c
index ce1af72d9b61..bac8385e069f 100644
--- a/drivers/s390/scsi/zfcp_qdio.c
+++ b/drivers/s390/scsi/zfcp_qdio.c
@@ -281,6 +281,7 @@ static int zfcp_qdio_sbal_check(struct zfcp_qdio *qdio)
  * Returns: 0 on success, -EIO if there is no free sbal after waiting.
  */
 int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio)
+	__must_hold(qdio->req_q_lock)
 {
 	long ret;
 
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
index 42423549e511..4f23d585d062 100644
--- a/drivers/s390/scsi/zfcp_sysfs.c
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -4,7 +4,7 @@
  *
  * sysfs attributes.
  *
- * Copyright IBM Corp. 2008, 2020
+ * Copyright IBM Corp. 2008, 2026
  */
 
 #define pr_fmt(fmt) "zfcp: " fmt
@@ -442,17 +442,24 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
 					 const char *buf, size_t count)
 {
 	struct zfcp_port *port = container_of(dev, struct zfcp_port, dev);
-	u64 fcp_lun;
-	int retval;
+	struct zfcp_adapter *adapter = port->adapter;
+	u64 fcp_lun = 0;
+	int retval = -EINVAL;
 
-	if (kstrtoull(buf, 0, (unsigned long long *) &fcp_lun))
-		return -EINVAL;
+	if (kstrtoull(buf, 0, (unsigned long long *)&fcp_lun)) {
+		zfcp_dbf_hba_uas("syuast1", 3, adapter, port->wwpn,
+				 fcp_lun, retval);
+		return retval;
+	}
 
 	flush_work(&port->rport_work);
 
 	retval = zfcp_unit_add(port, fcp_lun);
-	if (retval)
+	if (retval) {
+		zfcp_dbf_hba_uas("syuast2", 3, adapter, port->wwpn,
+				 fcp_lun, retval);
 		return retval;
+	}
 
 	return count;
 }
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index c3042393af23..4a2af0f702e1 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -499,6 +499,7 @@ source "drivers/scsi/esas2r/Kconfig"
 source "drivers/scsi/megaraid/Kconfig.megaraid"
 source "drivers/scsi/mpt3sas/Kconfig"
 source "drivers/scsi/mpi3mr/Kconfig"
+source "drivers/scsi/leapraid/Kconfig"
 source "drivers/scsi/smartpqi/Kconfig"
 
 config SCSI_HPTIOP
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 842c254bb226..098f35219e7d 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -91,6 +91,7 @@ obj-$(CONFIG_SCSI_BFA_FC)	+= bfa/
 obj-$(CONFIG_SCSI_CHELSIO_FCOE)	+= csiostor/
 obj-$(CONFIG_SCSI_DMX3191D)	+= dmx3191d.o
 obj-$(CONFIG_SCSI_HPSA)		+= hpsa.o
+obj-$(CONFIG_SCSI_LEAPRAID) += leapraid/
 obj-$(CONFIG_SCSI_SMARTPQI)	+= smartpqi/
 obj-$(CONFIG_SCSI_SYM53C8XX_2)	+= sym53c8xx_2/
 obj-$(CONFIG_SCSI_ZALON)	+= zalon7xx.o
diff --git a/drivers/scsi/a2091.c b/drivers/scsi/a2091.c
index 204448bfd04b..f81e53b53e20 100644
--- a/drivers/scsi/a2091.c
+++ b/drivers/scsi/a2091.c
@@ -275,9 +275,9 @@ static void a2091_remove(struct zorro_dev *z)
 }
 
 static struct zorro_device_id a2091_zorro_tbl[] = {
-	{ ZORRO_PROD_CBM_A590_A2091_1 },
-	{ ZORRO_PROD_CBM_A590_A2091_2 },
-	{ 0 }
+	{ .id = ZORRO_PROD_CBM_A590_A2091_1 },
+	{ .id = ZORRO_PROD_CBM_A590_A2091_2 },
+	{ }
 };
 MODULE_DEVICE_TABLE(zorro, a2091_zorro_tbl);
 
diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c
index fd766282d4a4..93dab19c1cb9 100644
--- a/drivers/scsi/aha1542.c
+++ b/drivers/scsi/aha1542.c
@@ -1083,7 +1083,7 @@ static int isa_registered;
 #ifdef CONFIG_PNP
 static const struct pnp_device_id aha1542_pnp_ids[] = {
 	{ .id = "ADP1542" },
-	{ .id = "" }
+	{ }
 };
 MODULE_DEVICE_TABLE(pnp, aha1542_pnp_ids);
 
diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c
index 6183ce05d8cf..ba490174a417 100644
--- a/drivers/scsi/dc395x.c
+++ b/drivers/scsi/dc395x.c
@@ -2182,7 +2182,6 @@ static void msgin_set_sync(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
 {
 	struct DeviceCtlBlk *dcb = srb->dcb;
 	u8 bval;
-	int fact;
 
 	if (srb->msgin_buf[4] > 15)
 		srb->msgin_buf[4] = 15;
@@ -2205,11 +2204,6 @@ static void msgin_set_sync(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb)
 	dcb->sync_period |= ALT_SYNC | bval;
 	dcb->min_nego_period = srb->msgin_buf[3];
 
-	if (dcb->sync_period & WIDE_SYNC)
-		fact = 500;
-	else
-		fact = 250;
-
 	if (!(srb->state & SRB_DO_SYNC_NEGO)) {
 		/* Reply with corrected SDTR Message */
 
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index 80ab0ff921d4..d81ec47a7140 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -5,6 +5,7 @@
  * Copyright (C) 2007-2010 Hannes Reinecke, SUSE Linux Products GmbH.
  * All rights reserved.
  */
+#include <linux/bitfield.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/module.h>
@@ -693,8 +694,8 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg)
 				    !(tmp_pg->flags & ALUA_PG_RUNNING)) {
 					struct alua_dh_data *h;
 
-					tmp_pg->state = desc[0] & 0x0f;
-					tmp_pg->pref = desc[0] >> 7;
+					tmp_pg->state = FIELD_GET(SCSI_ACCESS_STATE_MASK, desc[0]);
+					tmp_pg->pref = FIELD_GET(SCSI_ACCESS_STATE_PREFERRED, desc[0]);
 					rcu_read_lock();
 					list_for_each_entry_rcu(h,
 						&tmp_pg->dh_list, node) {
diff --git a/drivers/scsi/fnic/Makefile b/drivers/scsi/fnic/Makefile
index c025e875009e..48c5e0cb8ca6 100644
--- a/drivers/scsi/fnic/Makefile
+++ b/drivers/scsi/fnic/Makefile
@@ -6,6 +6,7 @@ fnic-y	:= \
 	fnic_attrs.o \
 	fnic_isr.o \
 	fnic_main.o \
+	fnic_nvme.o \
 	fnic_res.o \
 	fnic_fcs.o \
 	fdls_disc.o \
diff --git a/drivers/scsi/fnic/fcpio.h b/drivers/scsi/fnic/fcpio.h
index 54a0b2ba8f6f..a98a2a3bfe15 100644
--- a/drivers/scsi/fnic/fcpio.h
+++ b/drivers/scsi/fnic/fcpio.h
@@ -21,6 +21,18 @@
 #define FCPIO_HOST_SEQ_ID_RANGE_START       0x80
 #define FCPIO_HOST_SEQ_ID_RANGE_END         0xff
 
+struct fnic_nvme_io_event {
+	struct list_head links;
+	struct work_struct io_work;
+	void *arg1;
+};
+
+struct fnic_io_event_s {
+	struct list_head links;
+	struct work_struct io_work;
+	void *arg1;
+};
+
 /*
  * Command entry type
  */
@@ -33,6 +45,9 @@ enum fcpio_type {
 	FCPIO_ICMND_CMPL,
 	FCPIO_ITMF,
 	FCPIO_ITMF_CMPL,
+	FCPIO_NVME_CMD,
+	FCPIO_NVME_ERSP_HW_CMPL,
+	FCPIO_NVME_ERSP_FW_CMPL,
 
 	/*
 	 * Target request types
@@ -179,10 +194,22 @@ fcpio_header_dec(struct fcpio_header *hdr,
 	*tag = hdr->tag;
 }
 
+#define NVME_CMD_SZ 96
 #define CDB_16      16
 #define CDB_32      32
 #define LUN_ADDRESS 8
 
+struct fcpio_nvme_cmnd {
+	uint8_t d_id[3];
+	u_int8_t flags;		/* command flags */
+	u_int32_t sgl_cnt;	/* scatter-gather list count */
+	u_int64_t sgl_addr;	/* scatter-gather list addr */
+	u_int16_t cmd_len;
+	u_int16_t _resvd1;	/* reserved: should be 0 */
+	u_int32_t data_len;	/* length of data expected */
+	u_int8_t nvme_cmnd[NVME_CMD_SZ];	/* NVME command */
+};
+
 /*
  * fcpio_icmnd_16: host -> firmware request
  *
@@ -458,6 +485,7 @@ struct fcpio_host_req {
 		/*
 		 * Initiator host requests
 		 */
+		struct fcpio_nvme_cmnd              nvcmnd;
 		struct fcpio_icmnd_16               icmnd_16;
 		struct fcpio_icmnd_32               icmnd_32;
 		struct fcpio_itmf                   itmf;
@@ -482,6 +510,12 @@ struct fcpio_host_req {
 	} u;
 };
 
+struct fcpio_nvme_cmpl {
+	uint8_t resp_size;
+	uint8_t resvd[3];
+	uint8_t resp_bytes[32];
+};
+
 /*
  * fcpio_icmnd_cmpl: firmware -> host response
  *
@@ -682,6 +716,7 @@ struct fcpio_fw_req {
 		 * Initiator firmware responses
 		 */
 		struct fcpio_icmnd_cmpl         icmnd_cmpl;
+		struct fcpio_nvme_cmpl          nvme_cmpl;
 		struct fcpio_itmf_cmpl          itmf_cmpl;
 
 		/*
diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c
index 554dea767885..8cb40466872f 100644
--- a/drivers/scsi/fnic/fdls_disc.c
+++ b/drivers/scsi/fnic/fdls_disc.c
@@ -13,6 +13,10 @@
 #include <linux/utsname.h>
 
 #define FC_FC4_TYPE_SCSI 0x08
+#define FNIC_NVME_FEAT_TARG (1 << 0)
+#define FNIC_NVME_FEAT_INIT (1 << 1)
+#define FNIC_NVME_FEAT_DISC (1 << 2)
+
 #define PORT_SPEED_BIT_8 8
 #define PORT_SPEED_BIT_9 9
 #define PORT_SPEED_BIT_14 14
@@ -104,7 +108,7 @@ uint8_t *fdls_alloc_frame(struct fnic_iport_s *iport)
 
 	frame = mempool_alloc(fnic->frame_pool, GFP_ATOMIC);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 				"Failed to allocate frame");
 		return NULL;
 	}
@@ -136,7 +140,7 @@ uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport, int oxid_frame_type,
 	 */
 	idx = find_next_zero_bit(oxid_pool->bitmap, FNIC_OXID_POOL_SZ, oxid_pool->next_idx);
 	if (idx == FNIC_OXID_POOL_SZ) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Alloc oxid: all oxid slots are busy iport state:%d\n",
 			iport->state);
 		return FNIC_UNASSIGNED_OXID;
@@ -148,7 +152,7 @@ uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport, int oxid_frame_type,
 	oxid = FNIC_OXID_ENCODE(idx, oxid_frame_type);
 	*active_oxid = oxid;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 	   "alloc oxid: 0x%x, iport state: %d\n",
 	   oxid, iport->state);
 	return oxid;
@@ -169,7 +173,7 @@ static void fdls_free_oxid_idx(struct fnic_iport_s *iport, uint16_t oxid_idx)
 
 	lockdep_assert_held(&fnic->fnic_lock);
 
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 		"free oxid idx: 0x%x\n", oxid_idx);
 
 	WARN_ON(!test_and_clear_bit(oxid_idx, oxid_pool->bitmap));
@@ -194,7 +198,7 @@ void fdls_reclaim_oxid_handler(struct work_struct *work)
 	struct reclaim_entry_s *reclaim_entry, *next;
 	unsigned long delay_j, cur_jiffies;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		"Reclaim oxid callback\n");
 
 	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
@@ -223,7 +227,7 @@ void fdls_reclaim_oxid_handler(struct work_struct *work)
 
 		delay_j = reclaim_entry->expires - cur_jiffies;
 		schedule_delayed_work(&oxid_pool->oxid_reclaim_work, delay_j);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Scheduling next callback at:%ld jiffies\n", delay_j);
 	}
 
@@ -266,7 +270,7 @@ void fdls_schedule_oxid_free(struct fnic_iport_s *iport, uint16_t *active_oxid)
 
 	lockdep_assert_held(&fnic->fnic_lock);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		"Schedule oxid free. oxid: 0x%x\n", *active_oxid);
 
 	*active_oxid = FNIC_UNASSIGNED_OXID;
@@ -275,7 +279,7 @@ void fdls_schedule_oxid_free(struct fnic_iport_s *iport, uint16_t *active_oxid)
 		kzalloc_obj(struct reclaim_entry_s, GFP_ATOMIC);
 
 	if (!reclaim_entry) {
-		FNIC_FCS_DBG(KERN_WARNING, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_WARNING, fnic,
 			"Failed to allocate memory for reclaim struct for oxid idx: %d\n",
 			oxid_idx);
 
@@ -313,7 +317,7 @@ void fdls_schedule_oxid_free_retry_work(struct work_struct *work)
 
 	for_each_set_bit(idx, oxid_pool->pending_schedule_free, FNIC_OXID_POOL_SZ) {
 
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Schedule oxid free. oxid idx: %d\n", idx);
 
 		reclaim_entry = kzalloc_obj(*reclaim_entry);
@@ -323,12 +327,19 @@ void fdls_schedule_oxid_free_retry_work(struct work_struct *work)
 			return;
 		}
 
-		clear_bit(idx, oxid_pool->pending_schedule_free);
 		reclaim_entry->oxid_idx = idx;
 		reclaim_entry->expires = round_jiffies(jiffies + delay_j);
+
 		spin_lock_irqsave(&fnic->fnic_lock, flags);
+		/* Reset may have cleared this bit while this worker allocated. */
+		if (!test_and_clear_bit(idx, oxid_pool->pending_schedule_free)) {
+			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+			kfree(reclaim_entry);
+			continue;
+		}
 		list_add_tail(&reclaim_entry->links, &oxid_pool->oxid_reclaim_list);
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
 		schedule_delayed_work(&oxid_pool->oxid_reclaim_work, delay_j);
 	}
 }
@@ -383,10 +394,25 @@ static bool fdls_is_oxid_tgt_req(uint16_t oxid)
 	return true;
 }
 
+static inline bool fdls_is_oxid_nvme_req(uint16_t oxid)
+{
+	return FNIC_FRAME_TYPE(oxid) == FNIC_FRAME_TYPE_NVME_LS;
+}
+
 static void fdls_reset_oxid_pool(struct fnic_iport_s *iport)
 {
 	struct fnic_oxid_pool_s *oxid_pool = &iport->oxid_pool;
+	struct reclaim_entry_s *reclaim_entry, *next;
 
+	cancel_delayed_work(&oxid_pool->oxid_reclaim_work);
+	cancel_delayed_work(&oxid_pool->schedule_oxid_free_retry);
+	list_for_each_entry_safe(reclaim_entry, next,
+				 &oxid_pool->oxid_reclaim_list, links) {
+		list_del(&reclaim_entry->links);
+		kfree(reclaim_entry);
+	}
+	bitmap_clear(oxid_pool->pending_schedule_free, 0, FNIC_OXID_POOL_SZ);
+	bitmap_clear(oxid_pool->bitmap, 0, FNIC_OXID_POOL_SZ);
 	oxid_pool->next_idx = 0;
 }
 
@@ -416,7 +442,7 @@ fdls_start_fabric_timer(struct fnic_iport_s *iport, int timeout)
 	struct fnic *fnic = iport->fnic;
 
 	if (iport->fabric.timer_pending) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "iport fcid: 0x%x: Canceling fabric disc timer\n",
 					 iport->fcid);
 		fnic_del_fabric_timer_sync(fnic);
@@ -429,7 +455,7 @@ fdls_start_fabric_timer(struct fnic_iport_s *iport, int timeout)
 	fabric_tov = jiffies + msecs_to_jiffies(timeout);
 	mod_timer(&iport->fabric.retry_timer, round_jiffies(fabric_tov));
 	iport->fabric.timer_pending = 1;
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "fabric timer is %d ", timeout);
 }
 
@@ -441,7 +467,7 @@ fdls_start_tport_timer(struct fnic_iport_s *iport,
 	struct fnic *fnic = iport->fnic;
 
 	if (tport->timer_pending) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "tport fcid 0x%x: Canceling disc timer\n",
 					 tport->fcid);
 		fnic_del_tport_timer_sync(fnic, tport);
@@ -562,6 +588,22 @@ static void fdls_init_fabric_abts_frame(uint8_t *frame,
 	};
 }
 
+static void fdls_set_frame_type(struct fnic *fnic, uint8_t *frame_type)
+{
+	if (IS_FNIC_FCP_INITIATOR(fnic))
+		*frame_type = FC_TYPE_FCP;
+	else if (IS_FNIC_NVME_INITIATOR(fnic))
+		*frame_type = FC_TYPE_NVME;
+}
+
+static void fdls_set_feature_type(struct fnic *fnic, uint8_t *feature_type)
+{
+	if (IS_FNIC_FCP_INITIATOR(fnic))
+		*feature_type = FCP_FEAT_INIT;
+	else if (IS_FNIC_NVME_INITIATOR(fnic))
+		*feature_type = FNIC_NVME_FEAT_INIT;
+}
+
 static void
 fdls_send_rscn_resp(struct fnic_iport_s *iport,
 		    struct fc_frame_header *rscn_fchdr)
@@ -575,7 +617,7 @@ fdls_send_rscn_resp(struct fnic_iport_s *iport,
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 				"Failed to allocate frame to send RSCN response");
 		return;
 	}
@@ -588,7 +630,7 @@ fdls_send_rscn_resp(struct fnic_iport_s *iport,
 	oxid = FNIC_STD_GET_OX_ID(rscn_fchdr);
 	FNIC_STD_SET_OX_ID(pels_acc->fchdr, oxid);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "0x%x: FDLS send RSCN response with oxid: 0x%x",
 		 iport->fcid, oxid);
 
@@ -608,7 +650,7 @@ fdls_send_logo_resp(struct fnic_iport_s *iport,
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 				"Failed to allocate frame to send LOGO response");
 		return;
 	}
@@ -621,13 +663,57 @@ fdls_send_logo_resp(struct fnic_iport_s *iport,
 	oxid = FNIC_STD_GET_OX_ID(req_fchdr);
 	FNIC_STD_SET_OX_ID(plogo_resp->fchdr, oxid);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "0x%x: FDLS send LOGO response with oxid: 0x%x",
 		 iport->fcid, oxid);
 
 	fnic_send_fcoe_frame(iport, frame, frame_size);
 }
 
+int fdls_send_ls_req_abts(struct fnic_iport_s *iport,
+		struct fnic_tport_s *tport, unsigned int oxid)
+{
+	uint8_t *frame;
+	uint8_t s_id[3];
+	uint8_t d_id[3];
+	struct fnic *fnic = iport->fnic;
+	struct fc_frame_header *pls_req_abts;
+	uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+			sizeof(struct fc_frame_header);
+	int ret;
+
+	frame = fdls_alloc_frame(iport);
+	if (frame == NULL) {
+		FNIC_FCS_DBG(KERN_ERR, fnic,
+				"Failed to allocate frame to send ls req ABTS");
+		return -ENOMEM;
+	}
+
+	pls_req_abts = (struct fc_frame_header *) (frame +
+			FNIC_ETH_FCOE_HDRS_OFFSET);
+	fdls_init_fabric_abts_frame(frame, iport);
+
+	hton24(s_id, iport->fcid);
+	hton24(d_id, tport->fcid);
+	FNIC_STD_SET_S_ID(*pls_req_abts, s_id);
+	FNIC_STD_SET_D_ID(*pls_req_abts, d_id);
+
+	FNIC_STD_SET_OX_ID(*pls_req_abts, oxid);
+
+	FNIC_FCS_DBG(KERN_INFO, fnic,
+		"iport 0x%x: tport: 0x%x FDLS sending ls req abts with oxid: 0x%x",
+		iport->fcid, tport->fcid, oxid);
+
+	ret = fnic_send_fcoe_frame(iport, frame, frame_size);
+	if (ret) {
+		mempool_free(frame, fnic->frame_pool);
+		return ret;
+	}
+
+	return 0;
+}
+
+
 void
 fdls_send_tport_abts(struct fnic_iport_s *iport,
 					 struct fnic_tport_s *tport)
@@ -642,7 +728,7 @@ fdls_send_tport_abts(struct fnic_iport_s *iport,
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 				"Failed to allocate frame to send tport ABTS");
 		return;
 	}
@@ -665,7 +751,7 @@ fdls_send_tport_abts(struct fnic_iport_s *iport,
 
 	FNIC_STD_SET_OX_ID(*ptport_abts, tport->active_oxid);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: FDLS send tport abts: tport->state: %d ",
 				 iport->fcid, tport->state);
 
@@ -687,7 +773,7 @@ static void fdls_send_fabric_abts(struct fnic_iport_s *iport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 				"Failed to allocate frame to send fabric ABTS");
 		return;
 	}
@@ -750,7 +836,7 @@ static void fdls_send_fabric_abts(struct fnic_iport_s *iport)
 	oxid = iport->active_oxid_fabric_req;
 	FNIC_STD_SET_OX_ID(*pfabric_abts, oxid);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "0x%x: FDLS send fabric abts. iport->fabric.state: %d oxid: 0x%x",
 		 iport->fcid, iport->fabric.state, oxid);
 
@@ -773,7 +859,7 @@ static uint8_t *fdls_alloc_init_fdmi_abts_frame(struct fnic_iport_s *iport,
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 				"Failed to allocate frame to send FDMI ABTS");
 		return NULL;
 	}
@@ -802,7 +888,7 @@ static void fdls_send_fdmi_abts(struct fnic_iport_s *iport)
 		if (frame == NULL)
 			return;
 
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "0x%x: FDLS send FDMI PLOGI abts. iport->fabric.state: %d oxid: 0x%x",
 			 iport->fcid, iport->fabric.state, iport->active_oxid_fdmi_plogi);
 		fnic_send_fcoe_frame(iport, frame, frame_size);
@@ -813,7 +899,7 @@ static void fdls_send_fdmi_abts(struct fnic_iport_s *iport)
 			if (frame == NULL)
 				return;
 
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: FDLS send FDMI RHBA abts. iport->fabric.state: %d oxid: 0x%x",
 				 iport->fcid, iport->fabric.state, iport->active_oxid_fdmi_rhba);
 			fnic_send_fcoe_frame(iport, frame, frame_size);
@@ -828,7 +914,7 @@ static void fdls_send_fdmi_abts(struct fnic_iport_s *iport)
 					return;
 			}
 
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: FDLS send FDMI RPA abts. iport->fabric.state: %d oxid: 0x%x",
 				 iport->fcid, iport->fabric.state, iport->active_oxid_fdmi_rpa);
 			fnic_send_fcoe_frame(iport, frame, frame_size);
@@ -840,7 +926,7 @@ static void fdls_send_fdmi_abts(struct fnic_iport_s *iport)
 	mod_timer(&iport->fabric.fdmi_timer, round_jiffies(fdmi_tov));
 	iport->fabric.fdmi_pending |= FDLS_FDMI_ABORT_PENDING;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "0x%x: iport->fabric.fdmi_pending: 0x%x",
 		 iport->fcid, iport->fabric.fdmi_pending);
 }
@@ -856,7 +942,7 @@ static void fdls_send_fabric_flogi(struct fnic_iport_s *iport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send FLOGI");
 		iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
 		goto err_out;
@@ -885,7 +971,7 @@ static void fdls_send_fabric_flogi(struct fnic_iport_s *iport)
 		&iport->active_oxid_fabric_req);
 
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 		     "0x%x: Failed to allocate OXID to send FLOGI",
 			 iport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -894,7 +980,7 @@ static void fdls_send_fabric_flogi(struct fnic_iport_s *iport)
 	}
 	FNIC_STD_SET_OX_ID(pflogi->fchdr, oxid);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "0x%x: FDLS send fabric FLOGI with oxid: 0x%x", iport->fcid,
 		 oxid);
 
@@ -916,7 +1002,7 @@ static void fdls_send_fabric_plogi(struct fnic_iport_s *iport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send PLOGI");
 		iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
 		goto err_out;
@@ -928,7 +1014,7 @@ static void fdls_send_fabric_plogi(struct fnic_iport_s *iport)
 	oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_PLOGI,
 		&iport->active_oxid_fabric_req);
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 		     "0x%x: Failed to allocate OXID to send fabric PLOGI",
 			 iport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -937,7 +1023,7 @@ static void fdls_send_fabric_plogi(struct fnic_iport_s *iport)
 	}
 	FNIC_STD_SET_OX_ID(pplogi->fchdr, oxid);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "0x%x: FDLS send fabric PLOGI with oxid: 0x%x", iport->fcid,
 		 oxid);
 
@@ -962,7 +1048,7 @@ static void fdls_send_fdmi_plogi(struct fnic_iport_s *iport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send FDMI PLOGI");
 		goto err_out;
 	}
@@ -974,7 +1060,7 @@ static void fdls_send_fdmi_plogi(struct fnic_iport_s *iport)
 		&iport->active_oxid_fdmi_plogi);
 
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			     "0x%x: Failed to allocate OXID to send FDMI PLOGI",
 			     iport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -985,7 +1071,7 @@ static void fdls_send_fdmi_plogi(struct fnic_iport_s *iport)
 	hton24(d_id, FC_FID_MGMT_SERV);
 	FNIC_STD_SET_D_ID(pplogi->fchdr, d_id);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		     "0x%x: FDLS send FDMI PLOGI with oxid: 0x%x",
 		     iport->fcid, oxid);
 
@@ -1009,7 +1095,7 @@ static void fdls_send_rpn_id(struct fnic_iport_s *iport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send RPN_ID");
 		iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
 		goto err_out;
@@ -1036,7 +1122,7 @@ static void fdls_send_rpn_id(struct fnic_iport_s *iport)
 		&iport->active_oxid_fabric_req);
 
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 		     "0x%x: Failed to allocate OXID to send RPN_ID",
 			 iport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -1045,7 +1131,7 @@ static void fdls_send_rpn_id(struct fnic_iport_s *iport)
 	}
 	FNIC_STD_SET_OX_ID(prpn_id->fchdr, oxid);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "0x%x: FDLS send RPN ID with oxid: 0x%x", iport->fcid,
 		 oxid);
 
@@ -1068,7 +1154,7 @@ static void fdls_send_scr(struct fnic_iport_s *iport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send SCR");
 		iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
 		goto err_out;
@@ -1090,7 +1176,7 @@ static void fdls_send_scr(struct fnic_iport_s *iport)
 	oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_SCR,
 		&iport->active_oxid_fabric_req);
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 		     "0x%x: Failed to allocate OXID to send SCR",
 			 iport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -1099,7 +1185,7 @@ static void fdls_send_scr(struct fnic_iport_s *iport)
 	}
 	FNIC_STD_SET_OX_ID(pscr->fchdr, oxid);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "0x%x: FDLS send SCR with oxid: 0x%x", iport->fcid,
 		 oxid);
 
@@ -1123,7 +1209,7 @@ static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send GPN FT");
 		iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME;
 		goto err_out;
@@ -1137,10 +1223,10 @@ static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state)
 		      .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
 		.fc_std_ct_hdr = {.ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR,
 			      .ct_fs_subtype = FC_NS_SUBTYPE,
-			      .ct_cmd = cpu_to_be16(FC_NS_GPN_FT)},
-		.gpn_ft.fn_fc4_type = 0x08
+			      .ct_cmd = cpu_to_be16(FC_NS_GPN_FT)}
 	};
 
+	fdls_set_frame_type(fnic, &pgpn_ft->gpn_ft.fn_fc4_type);
 	hton24(fcid, iport->fcid);
 	FNIC_STD_SET_S_ID(pgpn_ft->fchdr, fcid);
 
@@ -1148,7 +1234,7 @@ static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state)
 		&iport->active_oxid_fabric_req);
 
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 		     "0x%x: Failed to allocate OXID to send GPN FT",
 			 iport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -1157,7 +1243,7 @@ static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state)
 	}
 	FNIC_STD_SET_OX_ID(pgpn_ft->fchdr, oxid);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "0x%x: FDLS send GPN FT with oxid: 0x%x", iport->fcid,
 		 oxid);
 
@@ -1183,7 +1269,7 @@ fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 				"Failed to allocate frame to send TGT ADISC");
 		tport->flags |= FNIC_FDLS_RETRY_FRAME;
 		goto err_out;
@@ -1203,7 +1289,7 @@ fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 
 	oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_ADISC, &tport->active_oxid);
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "0x%x: Failed to allocate OXID to send TGT ADISC",
 					 iport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -1222,7 +1308,7 @@ fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 
 	padisc->els.adisc_cmd = ELS_ADISC;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: FDLS send ADISC to tgt fcid: 0x%x",
 				 iport->fcid, tport->fcid);
 
@@ -1242,7 +1328,7 @@ bool fdls_delete_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 
 	if ((tport->state == FDLS_TGT_STATE_OFFLINING)
 	    || (tport->state == FDLS_TGT_STATE_OFFLINE)) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			     "tport fcid 0x%x: tport state is offlining/offline\n",
 			     tport->fcid);
 		return false;
@@ -1257,24 +1343,31 @@ bool fdls_delete_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 	tport->flags |= FNIC_FDLS_TPORT_TERMINATING;
 
 	if (tport->timer_pending) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "tport fcid 0x%x: Canceling disc timer\n",
 					 tport->fcid);
 		fnic_del_tport_timer_sync(fnic, tport);
 		tport->timer_pending = 0;
 	}
 
-	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
-	fnic_rport_exch_reset(iport->fnic, tport->fcid);
-	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+	if (IS_FNIC_FCP_INITIATOR(fnic)) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		fnic_rport_exch_reset(iport->fnic, tport->fcid);
+		spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+	} else if (IS_FNIC_NVME_INITIATOR(fnic)) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		nvfnic_exch_reset(iport, tport);
+		spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+	}
 
-	if (tport->flags & FNIC_FDLS_SCSI_REGISTERED) {
+	if ((tport->flags & FNIC_FDLS_SCSI_REGISTERED) ||
+	    (tport->flags & FNIC_FDLS_NVME_REGISTERED)) {
 		tport_del_evt =
 			kzalloc_obj(struct fnic_tport_event_s, GFP_ATOMIC);
 		if (!tport_del_evt) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
-				 "Failed to allocate memory for tport fcid: 0x%0x\n",
-				 tport->fcid);
+			FNIC_FCS_DBG(KERN_INFO, fnic,
+					"iport: 0x%x tport 0x%x: Failed to allocate memory\n",
+					iport->fcid, tport->fcid);
 			return false;
 		}
 		tport_del_evt->event = TGT_EV_RPORT_DEL;
@@ -1282,12 +1375,13 @@ bool fdls_delete_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 		list_add_tail(&tport_del_evt->links, &fnic->tport_event_list);
 		queue_work(fnic_event_queue, &fnic->tport_work);
 	} else {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
-			 "tport 0x%x not reg with scsi_transport. Freeing locally",
-			 tport->fcid);
+		FNIC_FCS_DBG(KERN_INFO, fnic,
+				"tport 0x%x not registered, freeing locally\n",
+				tport->fcid);
 		list_del(&tport->links);
 		kfree(tport);
 	}
+
 	return true;
 }
 
@@ -1305,7 +1399,7 @@ fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 				"Failed to allocate frame to send TGT PLOGI");
 		tport->flags |= FNIC_FDLS_RETRY_FRAME;
 		goto err_out;
@@ -1316,7 +1410,7 @@ fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 
 	oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_PLOGI, &tport->active_oxid);
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: Failed to allocate oxid to send PLOGI to fcid: 0x%x",
 				 iport->fcid, tport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -1330,7 +1424,7 @@ fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 	hton24(d_id, tport->fcid);
 	FNIC_STD_SET_D_ID(pplogi->fchdr, d_id);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: FDLS send tgt PLOGI to tgt: 0x%x with oxid: 0x%x",
 				 iport->fcid, tport->fcid, oxid);
 
@@ -1353,7 +1447,7 @@ fnic_fc_plogi_rsp_rdf(struct fnic_iport_s *iport,
 	    be16_to_cpu(plogi_rsp->els.fl_cssp[2].cp_rdfs) & FNIC_FC_C3_RDF;
 	struct fnic *fnic = iport->fnic;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "MFS: b2b_rdf_size: 0x%x spc3_rdf_size: 0x%x",
 			 b2b_rdf_size, spc3_rdf_size);
 
@@ -1372,7 +1466,7 @@ static void fdls_send_register_fc4_types(struct fnic_iport_s *iport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send RFT");
 		return;
 	}
@@ -1396,7 +1490,7 @@ static void fdls_send_register_fc4_types(struct fnic_iport_s *iport)
 		&iport->active_oxid_fabric_req);
 
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 		     "0x%x: Failed to allocate OXID to send RFT",
 			 iport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -1404,15 +1498,22 @@ static void fdls_send_register_fc4_types(struct fnic_iport_s *iport)
 	}
 	FNIC_STD_SET_OX_ID(prft_id->fchdr, oxid);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
-		 "0x%x: FDLS send RFT with oxid: 0x%x", iport->fcid,
-		 oxid);
+	if (IS_FNIC_NVME_INITIATOR(fnic))
+		prft_id->rft_id.fr_fts.ff_type_map[FC_TYPE_NVME / FC_NS_BPW] =
+			cpu_to_be32(1 << (FC_TYPE_NVME % FC_NS_BPW));
+	else if (IS_FNIC_FCP_INITIATOR(fnic))
+		prft_id->rft_id.fr_fts.ff_type_map[FC_TYPE_FCP / FC_NS_BPW] =
+			cpu_to_be32(1 << (FC_TYPE_FCP % FC_NS_BPW));
 
-	prft_id->rft_id.fr_fts.ff_type_map[0] =
-	    cpu_to_be32(1 << FC_TYPE_FCP);
+	prft_id->rft_id.fr_fts.ff_type_map[FC_TYPE_CT / FC_NS_BPW] |=
+		cpu_to_be32(1 << (FC_TYPE_CT % FC_NS_BPW));
 
-	prft_id->rft_id.fr_fts.ff_type_map[1] =
-	cpu_to_be32(1 << (FC_TYPE_CT % FC_NS_BPW));
+	FNIC_FCS_DBG(KERN_INFO, fnic,
+		     "0x%x: FDLS send RFT 0x%08x 0x%08x 0x%08x with oxid: 0x%x",
+		     iport->fcid, prft_id->rft_id.fr_fts.ff_type_map[0],
+		     prft_id->rft_id.fr_fts.ff_type_map[1],
+		     prft_id->rft_id.fr_fts.ff_type_map[2],
+		     oxid);
 
 	fnic_send_fcoe_frame(iport, frame, frame_size);
 
@@ -1432,7 +1533,7 @@ static void fdls_send_register_fc4_features(struct fnic_iport_s *iport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send RFF");
 		return;
 	}
@@ -1446,10 +1547,11 @@ static void fdls_send_register_fc4_features(struct fnic_iport_s *iport)
 		.fc_std_ct_hdr = {.ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR,
 			      .ct_fs_subtype = FC_NS_SUBTYPE,
 			      .ct_cmd = cpu_to_be16(FC_NS_RFF_ID)},
-		.rff_id.fr_feat = 0x2,
-		.rff_id.fr_type = FC_TYPE_FCP
 	};
 
+	fdls_set_frame_type(fnic, &prff_id->rff_id.fr_type);
+	fdls_set_feature_type(fnic, &prff_id->rff_id.fr_feat);
+
 	hton24(fcid, iport->fcid);
 	FNIC_STD_SET_S_ID(prff_id->fchdr, fcid);
 	FNIC_STD_SET_PORT_ID(prff_id->rff_id, fcid);
@@ -1458,7 +1560,7 @@ static void fdls_send_register_fc4_features(struct fnic_iport_s *iport)
 		&iport->active_oxid_fabric_req);
 
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			     "0x%x: Failed to allocate OXID to send RFF",
 				 iport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -1466,11 +1568,10 @@ static void fdls_send_register_fc4_features(struct fnic_iport_s *iport)
 	}
 	FNIC_STD_SET_OX_ID(prff_id->fchdr, oxid);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
-		 "0x%x: FDLS send RFF with oxid: 0x%x", iport->fcid,
-		 oxid);
-
-	prff_id->rff_id.fr_type = FC_TYPE_FCP;
+	FNIC_FCS_DBG(KERN_INFO, fnic,
+		"0x%x: FDLS send RFF with oxid: 0x%x type 0%x feat 0%x",
+		iport->fcid, oxid, prff_id->rff_id.fr_type,
+		prff_id->rff_id.fr_feat);
 
 	fnic_send_fcoe_frame(iport, frame, frame_size);
 
@@ -1493,7 +1594,7 @@ fdls_send_tgt_prli(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 				"Failed to allocate frame to send TGT PRLI");
 		tport->flags |= FNIC_FDLS_RETRY_FRAME;
 		goto err_out;
@@ -1504,16 +1605,24 @@ fdls_send_tgt_prli(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 		.fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_type = FC_TYPE_ELS,
 			  .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
 			  .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
-		.els_prli = {.prli_cmd = ELS_PRLI,
-			     .prli_spp_len = 16,
-			     .prli_len = cpu_to_be16(0x14)},
-		.sp = {.spp_type = 0x08, .spp_flags = 0x0020,
-		       .spp_params = cpu_to_be32(0xA2)}
+		.els_prli = {.prli_cmd = ELS_PRLI},
+		.sp = {.spp_params = cpu_to_be32(iport->service_params)}
 	};
 
+	fdls_set_frame_type(fnic, &pprli->sp.spp_type);
+	if (IS_FNIC_FCP_INITIATOR(fnic)) {
+		pprli->els_prli.prli_spp_len = 16;
+		pprli->els_prli.prli_len = cpu_to_be16(0x14);
+		pprli->sp.spp_flags = FC_SPP_EST_IMG_PAIR;
+	} else if (IS_FNIC_NVME_INITIATOR(fnic)) {
+		/* Per FC-NVMe, Establish Image Pair must not be set for NVMe PRLI. */
+		pprli->els_prli.prli_spp_len = 20;
+		pprli->els_prli.prli_len = cpu_to_be16(0x18);
+	}
+
 	oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_PRLI, &tport->active_oxid);
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 			"0x%x: Failed to allocate OXID to send TGT PRLI to 0x%x",
 			iport->fcid, tport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -1530,7 +1639,7 @@ fdls_send_tgt_prli(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 	FNIC_STD_SET_S_ID(pprli->fchdr, s_id);
 	FNIC_STD_SET_D_ID(pprli->fchdr, d_id);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 			"0x%x: FDLS send PRLI to tgt: 0x%x with oxid: 0x%x",
 			iport->fcid, tport->fcid, oxid);
 
@@ -1564,7 +1673,7 @@ void fdls_send_fabric_logo(struct fnic_iport_s *iport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send fabric LOGO");
 		return;
 	}
@@ -1576,7 +1685,7 @@ void fdls_send_fabric_logo(struct fnic_iport_s *iport)
 		&iport->active_oxid_fabric_req);
 
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 		     "0x%x: Failed to allocate OXID to send fabric LOGO",
 			 iport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -1589,7 +1698,7 @@ void fdls_send_fabric_logo(struct fnic_iport_s *iport)
 
 	iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		     "0x%x: FDLS send fabric LOGO with oxid: 0x%x",
 		     iport->fcid, oxid);
 
@@ -1621,7 +1730,7 @@ void fdls_tgt_logout(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send fabric LOGO");
 		return;
 	}
@@ -1631,7 +1740,7 @@ void fdls_tgt_logout(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 
 	oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_LOGO, &tport->active_oxid);
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 		     "0x%x: Failed to allocate OXID to send tgt LOGO",
 		     iport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -1642,7 +1751,7 @@ void fdls_tgt_logout(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
 	hton24(d_id, tport->fcid);
 	FNIC_STD_SET_D_ID(plogo->fchdr, d_id);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "0x%x: FDLS send tgt LOGO with oxid: 0x%x",
 		 iport->fcid, oxid);
 
@@ -1657,7 +1766,7 @@ static void fdls_tgt_discovery_start(struct fnic_iport_s *iport)
 	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
 	struct fnic *fnic = iport->fnic;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: Starting FDLS target discovery", iport->fcid);
 
 	list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
@@ -1711,7 +1820,7 @@ static void fdls_target_restart_nexus(struct fnic_tport_s *tport)
 	struct fnic *fnic = iport->fnic;
 	bool retval = true;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "tport fcid: 0x%x state: %d restart_count: %d",
 				 tport->fcid, tport->state, tport->nexus_restart_count);
 
@@ -1721,13 +1830,13 @@ static void fdls_target_restart_nexus(struct fnic_tport_s *tport)
 
 	retval = fdls_delete_tport(iport, tport);
 	if (retval != true) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 			     "Error deleting tport: 0x%x", fcid);
 		return;
 	}
 
 	if (nexus_restart_count >= FNIC_TPORT_MAX_NEXUS_RESTART) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			     "Exceeded nexus restart retries tport: 0x%x",
 			     fcid);
 		return;
@@ -1744,7 +1853,7 @@ static void fdls_target_restart_nexus(struct fnic_tport_s *tport)
 	 */
 	new_tport = fdls_create_tport(iport, fcid, wwpn);
 	if (!new_tport) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Error creating new tport: 0x%x", fcid);
 		return;
 	}
@@ -1773,12 +1882,12 @@ static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
 	struct fnic_tport_s *tport;
 	struct fnic *fnic = iport->fnic;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "FDLS create tport: fcid: 0x%x wwpn: 0x%llx", fcid, wwpn);
 
 	tport = kzalloc_obj(struct fnic_tport_s, GFP_ATOMIC);
 	if (!tport) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Memory allocation failure while creating tport: 0x%x\n",
 			 fcid);
 		return NULL;
@@ -1790,13 +1899,14 @@ static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport,
 	tport->fcid = fcid;
 	tport->wwpn = wwpn;
 	tport->iport = iport;
+	INIT_LIST_HEAD(&tport->ls_req_list);
 
-	FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_DEBUG, fnic,
 				 "Need to setup tport timer callback");
 
 	timer_setup(&tport->retry_timer, fdls_tport_timer_callback, 0);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Added tport 0x%x", tport->fcid);
 	fdls_set_tport_state(tport, FDLS_TGT_STATE_INIT);
 	list_add_tail(&tport->links, &iport->tport_list);
@@ -1847,7 +1957,7 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send FDMI RHBA");
 		return;
 	}
@@ -1875,7 +1985,7 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport)
 		&iport->active_oxid_fdmi_rhba);
 
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			     "0x%x: Failed to allocate OXID to send FDMI RHBA",
 		     iport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -1896,14 +2006,14 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport)
 	fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_NODE_NAME,
 		FNIC_FDMI_NN_LEN, data, &attr_off_bytes);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 			"NN set, off=%d", attr_off_bytes);
 
 	strscpy_pad(data, FNIC_FDMI_MANUFACTURER, FNIC_FDMI_MANU_LEN);
 	fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MANUFACTURER,
 		FNIC_FDMI_MANU_LEN, data, &attr_off_bytes);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 			"MFG set <%s>, off=%d", data, attr_off_bytes);
 
 	err = vnic_dev_fw_info(fnic->vdev, &fw_info);
@@ -1912,7 +2022,7 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport)
 				FNIC_FDMI_SERIAL_LEN);
 		fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_SERIAL_NUMBER,
 			FNIC_FDMI_SERIAL_LEN, data, &attr_off_bytes);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 				"SERIAL set <%s>, off=%d", data, attr_off_bytes);
 
 	}
@@ -1923,21 +2033,21 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport)
 	fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MODEL, FNIC_FDMI_MODEL_LEN,
 		data, &attr_off_bytes);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 			"MODEL set <%s>, off=%d", data, attr_off_bytes);
 
 	strscpy_pad(data, FNIC_FDMI_MODEL_DESCRIPTION, FNIC_FDMI_MODEL_DES_LEN);
 	fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MODEL_DES,
 		FNIC_FDMI_MODEL_DES_LEN, data, &attr_off_bytes);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 			"MODEL_DESC set <%s>, off=%d", data, attr_off_bytes);
 
 	if (!err) {
 		strscpy_pad(data, fw_info->hw_version, FNIC_FDMI_HW_VER_LEN);
 		fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_HARDWARE_VERSION,
 			FNIC_FDMI_HW_VER_LEN, data, &attr_off_bytes);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 				"HW_VER set <%s>, off=%d", data, attr_off_bytes);
 
 	}
@@ -1946,14 +2056,14 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport)
 	fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_DRIVER_VERSION,
 		FNIC_FDMI_DR_VER_LEN, data, &attr_off_bytes);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 			"DRV_VER set <%s>, off=%d", data, attr_off_bytes);
 
 	strscpy_pad(data, "N/A", FNIC_FDMI_ROM_VER_LEN);
 	fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_ROM_VERSION,
 		FNIC_FDMI_ROM_VER_LEN, data, &attr_off_bytes);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 			"ROM_VER set <%s>, off=%d", data, attr_off_bytes);
 
 	if (!err) {
@@ -1961,14 +2071,14 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport)
 		fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_FIRMWARE_VERSION,
 			FNIC_FDMI_FW_VER_LEN, data, &attr_off_bytes);
 
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 				"FW_VER set <%s>, off=%d", data, attr_off_bytes);
 	}
 
 	len = sizeof(struct fc_std_fdmi_rhba) + attr_off_bytes;
 	frame_size += len;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "0x%x: FDLS send FDMI RHBA with oxid: 0x%x fs: %d", iport->fcid,
 		 oxid, frame_size);
 
@@ -1992,7 +2102,7 @@ static void fdls_fdmi_register_pa(struct fnic_iport_s *iport)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send FDMI RPA");
 		return;
 	}
@@ -2020,7 +2130,7 @@ static void fdls_fdmi_register_pa(struct fnic_iport_s *iport)
 		&iport->active_oxid_fdmi_rpa);
 
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			     "0x%x: Failed to allocate OXID to send FDMI RPA",
 			     iport->fcid);
 		mempool_free(frame, fnic->frame_pool);
@@ -2063,7 +2173,10 @@ static void fdls_fdmi_register_pa(struct fnic_iport_s *iport)
 	put_unaligned_be64(iport->wwnn, data);
 
 	memset(data, 0, FNIC_FDMI_FC4_LEN);
-	data[2] = 1;
+	if (IS_FNIC_FCP_INITIATOR(fnic))
+		data[2] = 1;
+	else if (IS_FNIC_NVME_INITIATOR(fnic))
+		data[6] = 1;
 	fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_FC4_TYPES,
 		FNIC_FDMI_FC4_LEN, data, &attr_off_bytes);
 
@@ -2079,28 +2192,34 @@ static void fdls_fdmi_register_pa(struct fnic_iport_s *iport)
 	fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MAX_FRAME_SIZE,
 		FNIC_FDMI_MFS_LEN, data, &attr_off_bytes);
 
-	snprintf(tmp_data, FNIC_FDMI_OS_NAME_LEN - 1, "host%d",
-		 fnic->host->host_no);
+	if (IS_FNIC_FCP_INITIATOR(fnic))
+		snprintf(tmp_data, FNIC_FDMI_OS_NAME_LEN - 1, "host%d",
+			fnic->host->host_no);
+	else if (IS_FNIC_NVME_INITIATOR(fnic))
+		snprintf(tmp_data, FNIC_FDMI_OS_NAME_LEN - 1, "nvfnic%d",
+			fnic->fnic_num);
 	strscpy_pad(data, tmp_data, FNIC_FDMI_OS_NAME_LEN);
 	fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_OS_NAME,
 		FNIC_FDMI_OS_NAME_LEN, data, &attr_off_bytes);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 			"OS name set <%s>, off=%d", data, attr_off_bytes);
 
-	sprintf(fc_host_system_hostname(fnic->host), "%s", utsname()->nodename);
-	strscpy_pad(data, fc_host_system_hostname(fnic->host),
-					FNIC_FDMI_HN_LEN);
+	if (IS_FNIC_FCP_INITIATOR(fnic))
+		sprintf(fc_host_system_hostname(fnic->host), "%s",
+			utsname()->nodename);
+
+	strscpy_pad(data, utsname()->nodename, FNIC_FDMI_HN_LEN);
 	fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_HOST_NAME,
 		FNIC_FDMI_HN_LEN, data, &attr_off_bytes);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Host name set <%s>, off=%d", data, attr_off_bytes);
 
 	len = sizeof(struct fc_std_fdmi_rpa) + attr_off_bytes;
 	frame_size += len;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "0x%x: FDLS send FDMI RPA with oxid: 0x%x fs: %d", iport->fcid,
 		 oxid, frame_size);
 
@@ -2117,7 +2236,7 @@ void fdls_fabric_timer_callback(struct timer_list *t)
 	struct fnic *fnic = iport->fnic;
 	unsigned long flags;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "tp: %d fab state: %d fab retry counter: %d max_flogi_retries: %d",
 		 iport->fabric.timer_pending, iport->fabric.state,
 		 iport->fabric.retry_counter, iport->max_flogi_retries);
@@ -2132,7 +2251,7 @@ void fdls_fabric_timer_callback(struct timer_list *t)
 	if (iport->fabric.del_timer_inprogress) {
 		iport->fabric.del_timer_inprogress = 0;
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "fabric_del_timer inprogress(%d). Skip timer cb",
 					 iport->fabric.del_timer_inprogress);
 		return;
@@ -2160,7 +2279,7 @@ void fdls_fabric_timer_callback(struct timer_list *t)
 				iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
 				fdls_send_fabric_flogi(iport);
 			} else
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "Exceeded max FLOGI retries");
 		}
 		break;
@@ -2182,7 +2301,7 @@ void fdls_fabric_timer_callback(struct timer_list *t)
 				iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
 				fdls_send_fabric_plogi(iport);
 			} else
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "Exceeded max PLOGI retries");
 		}
 		break;
@@ -2213,7 +2332,7 @@ void fdls_fabric_timer_callback(struct timer_list *t)
 		else {
 			/* ABTS has timed out */
 			fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req);
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "ABTS timed out. Starting PLOGI: %p", iport);
 			fnic_fdls_start_plogi(iport);
 		}
@@ -2230,7 +2349,7 @@ void fdls_fabric_timer_callback(struct timer_list *t)
 		} else {
 			/* ABTS has timed out */
 			fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req);
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "ABTS timed out. Starting PLOGI: %p", iport);
 			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
 		}
@@ -2247,7 +2366,7 @@ void fdls_fabric_timer_callback(struct timer_list *t)
 		else {
 			/* ABTS has timed out */
 			fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req);
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "ABTS timed out. Starting PLOGI %p", iport);
 			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
 		}
@@ -2269,7 +2388,7 @@ void fdls_fabric_timer_callback(struct timer_list *t)
 			if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) {
 				fdls_send_gpn_ft(iport, iport->fabric.state);
 			} else {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "ABTS timeout for fabric GPN_FT. Check name server: %p",
 					 iport);
 			}
@@ -2289,7 +2408,7 @@ void fdls_fdmi_retry_plogi(struct fnic_iport_s *iport)
 	/* If max retries not exhausted, start over from fdmi plogi */
 	if (iport->fabric.fdmi_retry < FDLS_FDMI_MAX_RETRY) {
 		iport->fabric.fdmi_retry++;
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Retry FDMI PLOGI. FDMI retry: %d",
 					 iport->fabric.fdmi_retry);
 		fdls_send_fdmi_plogi(iport);
@@ -2307,7 +2426,7 @@ void fdls_fdmi_timer_callback(struct timer_list *t)
 
 	spin_lock_irqsave(&fnic->fnic_lock, flags);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		"iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending);
 
 	if (!iport->fabric.fdmi_pending) {
@@ -2315,7 +2434,7 @@ void fdls_fdmi_timer_callback(struct timer_list *t)
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 		return;
 	}
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		"iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending);
 
 	/* if not abort pending, send an abort */
@@ -2324,7 +2443,7 @@ void fdls_fdmi_timer_callback(struct timer_list *t)
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 		return;
 	}
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		"iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending);
 
 	/* ABTS pending for an active fdmi request that is pending.
@@ -2332,29 +2451,36 @@ void fdls_fdmi_timer_callback(struct timer_list *t)
 	 * Schedule to free the OXID after 2*r_a_tov and proceed
 	 */
 	if (iport->fabric.fdmi_pending & FDLS_FDMI_PLOGI_PENDING) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"FDMI PLOGI ABTS timed out. Schedule oxid free: 0x%x\n",
 			iport->active_oxid_fdmi_plogi);
 		fdls_schedule_oxid_free(iport, &iport->active_oxid_fdmi_plogi);
 	} else {
 		if (iport->fabric.fdmi_pending & FDLS_FDMI_REG_HBA_PENDING) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						"FDMI RHBA ABTS timed out. Schedule oxid free: 0x%x\n",
 						iport->active_oxid_fdmi_rhba);
 			fdls_schedule_oxid_free(iport, &iport->active_oxid_fdmi_rhba);
 		}
 		if (iport->fabric.fdmi_pending & FDLS_FDMI_RPA_PENDING) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						"FDMI RPA ABTS timed out. Schedule oxid free: 0x%x\n",
 						iport->active_oxid_fdmi_rpa);
 			fdls_schedule_oxid_free(iport, &iport->active_oxid_fdmi_rpa);
 		}
 	}
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		"iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending);
 
-	fdls_fdmi_retry_plogi(iport);
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	iport->fabric.fdmi_pending = 0;
+	/* If max retries not exhaused, start over from fdmi plogi */
+	if (iport->fabric.fdmi_retry < FDLS_FDMI_MAX_RETRY) {
+		iport->fabric.fdmi_retry++;
+		FNIC_FCS_DBG(KERN_INFO, fnic,
+					 "retry fdmi timer %d", iport->fabric.fdmi_retry);
+		fdls_send_fdmi_plogi(iport);
+	}
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		"iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending);
 	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 }
@@ -2367,7 +2493,7 @@ static void fdls_send_delete_tport_msg(struct fnic_tport_s *tport)
 
 	tport_del_evt = kzalloc_obj(struct fnic_tport_event_s, GFP_ATOMIC);
 	if (!tport_del_evt) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Failed to allocate memory for tport event fcid: 0x%x",
 			 tport->fcid);
 		return;
@@ -2385,6 +2511,8 @@ static void fdls_tport_timer_callback(struct timer_list *t)
 	struct fnic *fnic = iport->fnic;
 	uint16_t oxid;
 	unsigned long flags;
+	struct fc_frame_header fchdr = {0};
+	uint8_t fcid[3];
 
 	spin_lock_irqsave(&fnic->fnic_lock, flags);
 	if (!tport->timer_pending) {
@@ -2400,13 +2528,13 @@ static void fdls_tport_timer_callback(struct timer_list *t)
 	if (tport->del_timer_inprogress) {
 		tport->del_timer_inprogress = 0;
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "tport_del_timer inprogress. Skip timer cb tport fcid: 0x%x\n",
 			 tport->fcid);
 		return;
 	}
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "tport fcid: 0x%x timer pending: %d state: %d retry counter: %d",
 		 tport->fcid, tport->timer_pending, tport->state,
 		 tport->retry_counter);
@@ -2467,15 +2595,22 @@ static void fdls_tport_timer_callback(struct timer_list *t)
 		} else {
 			/* exceeded retry count */
 			fdls_schedule_oxid_free(iport, &tport->active_oxid);
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "ADISC not responding. Deleting target port: 0x%x",
 					 tport->fcid);
 			fdls_send_delete_tport_msg(tport);
 		}
 		break;
 	default:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
-			 "oxid: 0x%x Unknown tport state: 0x%x", oxid, tport->state);
+		FNIC_FCS_DBG(KERN_INFO, fnic,
+			"0x%x timeout tport 0x%x oxid 0x%x state %d\n",
+			iport->fcid, tport->fcid, oxid, tport->state);
+		if (IS_FNIC_NVME_INITIATOR(fnic)) {
+			hton24(fcid, tport->fcid);
+			FNIC_STD_SET_S_ID(fchdr, fcid);
+			FNIC_STD_SET_OX_ID(fchdr, oxid);
+			nvfnic_process_ls_abts_rsp(iport, &fchdr);
+		}
 		break;
 	}
 	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
@@ -2524,26 +2659,26 @@ fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport,
 	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
 
 	if (!tport) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Tgt ADISC response tport not found: 0x%x", tgt_fcid);
 		return;
 	}
 	if ((iport->state != FNIC_IPORT_STATE_READY)
 		|| (tport->state != FDLS_TGT_STATE_ADISC)
 		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Dropping this ADISC response");
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "iport state: %d tport state: %d Is abort issued on PRLI? %d",
 			 iport->state, tport->state,
 			 (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED));
 		return;
 	}
 	if (FNIC_STD_GET_OX_ID(fchdr) != tport->active_oxid) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Dropping frame from target: 0x%x",
 			 tgt_fcid);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Reason: Stale ADISC/Aborted ADISC/OOO frame delivery");
 		return;
 	}
@@ -2555,7 +2690,7 @@ fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport,
 	case ELS_LS_ACC:
 		atomic64_inc(&iport->iport_stats.tport_adisc_ls_accepts);
 		if (tport->timer_pending) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "tport 0x%p Canceling fabric disc timer\n",
 						 tport);
 			fnic_del_tport_timer_sync(fnic, tport);
@@ -2565,12 +2700,12 @@ fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport,
 		frame_wwnn = get_unaligned_be64(&adisc_rsp->els.adisc_wwnn);
 		frame_wwpn = get_unaligned_be64(&adisc_rsp->els.adisc_wwpn);
 		if ((frame_wwnn == tport->wwnn) && (frame_wwpn == tport->wwpn)) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "ADISC accepted from target: 0x%x. Target logged in",
 				 tgt_fcid);
 			fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
 		} else {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "Error mismatch frame: ADISC");
 		}
 		break;
@@ -2580,14 +2715,14 @@ fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport,
 		if (((els_rjt->rej.er_reason == ELS_RJT_BUSY)
 		     || (els_rjt->rej.er_reason == ELS_RJT_UNAB))
 			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "ADISC ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x",
 				 tgt_fcid);
 
 			/* Retry ADISC again from the timer routine. */
 			tport->flags |= FNIC_FDLS_RETRY_FRAME;
 		} else {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "ADISC returned ELS_LS_RJT from target: 0x%x",
 						 tgt_fcid);
 			fdls_delete_tport(iport, tport);
@@ -2611,33 +2746,33 @@ fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport,
 	fcid = FNIC_STD_GET_S_ID(fchdr);
 	tgt_fcid = ntoh24(fcid);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "FDLS processing target PLOGI response: tgt_fcid: 0x%x",
 				 tgt_fcid);
 
 	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
 	if (!tport) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "tport not found: 0x%x", tgt_fcid);
 		return;
 	}
 	if ((iport->state != FNIC_IPORT_STATE_READY)
 		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Dropping frame! iport state: %d tport state: %d",
 					 iport->state, tport->state);
 		return;
 	}
 
 	if (tport->state != FDLS_TGT_STATE_PLOGI) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			     "PLOGI rsp recvd in wrong state. Drop the frame and restart nexus");
 		fdls_target_restart_nexus(tport);
 		return;
 	}
 
 	if (FNIC_STD_GET_OX_ID(fchdr) != tport->active_oxid) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "PLOGI response from target: 0x%x. Dropping frame",
 			 tgt_fcid);
 		return;
@@ -2649,7 +2784,7 @@ fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport,
 	switch (plogi_rsp->els.fl_cmd) {
 	case ELS_LS_ACC:
 		atomic64_inc(&iport->iport_stats.tport_plogi_ls_accepts);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "PLOGI accepted by target: 0x%x", tgt_fcid);
 		break;
 
@@ -2658,14 +2793,14 @@ fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport,
 		if (((els_rjt->rej.er_reason == ELS_RJT_BUSY)
 		     || (els_rjt->rej.er_reason == ELS_RJT_UNAB))
 			&& (tport->retry_counter < iport->max_plogi_retries)) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "PLOGI ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x",
 				 tgt_fcid);
 			/* Retry plogi again from the timer routine. */
 			tport->flags |= FNIC_FDLS_RETRY_FRAME;
 			return;
 		}
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "PLOGI returned ELS_LS_RJT from target: 0x%x",
 					 tgt_fcid);
 		fdls_delete_tport(iport, tport);
@@ -2673,18 +2808,18 @@ fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport,
 
 	default:
 		atomic64_inc(&iport->iport_stats.tport_plogi_misc_rejects);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "PLOGI not accepted from target fcid: 0x%x",
 					 tgt_fcid);
 		return;
 	}
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Found the PLOGI target: 0x%x and state: %d",
 				 (unsigned int) tgt_fcid, tport->state);
 
 	if (tport->timer_pending) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "tport fcid 0x%x: Canceling disc timer\n",
 					 tport->fcid);
 		fnic_del_tport_timer_sync(fnic, tport);
@@ -2702,13 +2837,13 @@ fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport,
 		min(max_payload_size, iport->max_payload_size);
 
 	if (tport->max_payload_size < FNIC_MIN_DATA_FIELD_SIZE) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "MFS: tport max frame size below spec bounds: %d",
 			 tport->max_payload_size);
 		tport->max_payload_size = FNIC_MIN_DATA_FIELD_SIZE;
 	}
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "MAX frame size: %u iport max_payload_size: %d tport mfs: %d",
 		 max_payload_size, iport->max_payload_size,
 		 tport->max_payload_size);
@@ -2736,12 +2871,12 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
 	fcid = FNIC_STD_GET_S_ID(fchdr);
 	tgt_fcid = ntoh24(fcid);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "FDLS process tgt PRLI response: 0x%x", tgt_fcid);
 
 	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
 	if (!tport) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "tport not found: 0x%x", tgt_fcid);
 		/* Handle or just drop? */
 		return;
@@ -2749,24 +2884,24 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
 
 	if ((iport->state != FNIC_IPORT_STATE_READY)
 		|| (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Dropping frame! iport st: %d tport st: %d tport fcid: 0x%x",
 			 iport->state, tport->state, tport->fcid);
 		return;
 	}
 
 	if (tport->state != FDLS_TGT_STATE_PRLI) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			     "PRLI rsp recvd in wrong state. Drop frame. Restarting nexus");
 		fdls_target_restart_nexus(tport);
 		return;
 	}
 
 	if (FNIC_STD_GET_OX_ID(fchdr) != tport->active_oxid) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Dropping PRLI response from target: 0x%x ",
 			 tgt_fcid);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Reason: Stale PRLI response/Aborted PDISC/OOO frame delivery");
 		return;
 	}
@@ -2777,14 +2912,21 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
 	switch (prli_rsp->els_prli.prli_cmd) {
 	case ELS_LS_ACC:
 		atomic64_inc(&iport->iport_stats.tport_prli_ls_accepts);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "PRLI accepted from target: 0x%x", tgt_fcid);
 
-		if (prli_rsp->sp.spp_type != FC_FC4_TYPE_SCSI) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		if (IS_FNIC_FCP_INITIATOR(fnic) &&
+		    prli_rsp->sp.spp_type != FC_FC4_TYPE_SCSI) {
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "mismatched target zoned with FC SCSI initiator: 0x%x",
 				 tgt_fcid);
 			mismatched_tgt = true;
+		} else if (IS_FNIC_NVME_INITIATOR(fnic) &&
+			   prli_rsp->sp.spp_type != FC_TYPE_NVME) {
+			FNIC_FCS_DBG(KERN_ERR, fnic,
+				 "mismatched target zoned with NVME initiator: 0x%x",
+				 tgt_fcid);
+			mismatched_tgt = true;
 		}
 		if (mismatched_tgt) {
 			fdls_tgt_logout(iport, tport);
@@ -2798,7 +2940,7 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
 		     || (els_rjt->rej.er_reason == ELS_RJT_UNAB))
 			&& (tport->retry_counter < FDLS_RETRY_COUNT)) {
 
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "PRLI ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x",
 				 tgt_fcid);
 
@@ -2806,7 +2948,7 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
 			tport->flags |= FNIC_FDLS_RETRY_FRAME;
 			return;
 		}
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "PRLI returned ELS_LS_RJT from target: 0x%x",
 					 tgt_fcid);
 
@@ -2815,17 +2957,17 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
 		return;
 	default:
 		atomic64_inc(&iport->iport_stats.tport_prli_misc_rejects);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "PRLI not accepted from target: 0x%x", tgt_fcid);
 		return;
 	}
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Found the PRLI target: 0x%x and state: %d",
 				 (unsigned int) tgt_fcid, tport->state);
 
 	if (tport->timer_pending) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "tport fcid 0x%x: Canceling disc timer\n",
 					 tport->fcid);
 		fnic_del_tport_timer_sync(fnic, tport);
@@ -2836,14 +2978,22 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
 	tport->fcp_csp = be32_to_cpu(prli_rsp->sp.spp_params);
 	tport->retry_counter = 0;
 
-	if (tport->fcp_csp & FCP_SPPF_RETRY)
-		tport->tgt_flags |= FNIC_FC_RP_FLAGS_RETRY;
+	if (IS_FNIC_FCP_INITIATOR(fnic)) {
+		if (tport->fcp_csp & FCP_SPPF_RETRY)
+			tport->tgt_flags |= FNIC_FC_RP_FLAGS_RETRY;
+	} else if (IS_FNIC_NVME_INITIATOR(fnic)) {
+		if (tport->fcp_csp & FNIC_NVME_SP_SLER)
+			tport->tgt_flags |= FNIC_FC_RP_FLAGS_RETRY;
+	}
 
 	/* Check if the device plays Target Mode Function */
-	if (!(tport->fcp_csp & FCP_PRLI_FUNC_TARGET)) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
-			 "Remote port(0x%x): no target support. Deleting it\n",
-			 tgt_fcid);
+	if ((IS_FNIC_FCP_INITIATOR(fnic) &&
+	     !(tport->fcp_csp & FCP_PRLI_FUNC_TARGET)) ||
+	    (IS_FNIC_NVME_INITIATOR(fnic) &&
+	     !(tport->fcp_csp & FCP_PRLI_FUNC_TARGET))) {
+		FNIC_FCS_DBG(KERN_INFO, fnic,
+				 "Remote port(0x%x): no target support. Deleting it\n",
+				 tgt_fcid);
 		fdls_tgt_logout(iport, tport);
 		fdls_delete_tport(iport, tport);
 		return;
@@ -2852,20 +3002,23 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport,
 	fdls_set_tport_state(tport, FDLS_TGT_STATE_READY);
 
 	/* Inform the driver about new target added */
-	tport_add_evt = kzalloc_obj(struct fnic_tport_event_s, GFP_ATOMIC);
-	if (!tport_add_evt) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
-				 "tport event memory allocation failure: 0x%0x\n",
-				 tport->fcid);
-		return;
+	if (IS_FNIC_FCP_INITIATOR(fnic) ||
+	    IS_FNIC_NVME_INITIATOR(fnic)) {
+		tport_add_evt = kzalloc_obj(struct fnic_tport_event_s, GFP_ATOMIC);
+		if (!tport_add_evt) {
+			FNIC_FCS_DBG(KERN_INFO, fnic,
+				     "iport fcid: 0x%x tport event memory allocation failure: 0x%0x\n",
+				     iport->fcid, tport->fcid);
+			return;
+		}
+		tport_add_evt->event = TGT_EV_RPORT_ADD;
+		tport_add_evt->arg1 = (void *)tport;
+		FNIC_FCS_DBG(KERN_INFO, fnic,
+			     "iport fcid: 0x%x add tport event fcid: 0x%x\n",
+			     tport->fcid, iport->fcid);
+		list_add_tail(&tport_add_evt->links, &fnic->tport_event_list);
+		queue_work(fnic_event_queue, &fnic->tport_work);
 	}
-	tport_add_evt->event = TGT_EV_RPORT_ADD;
-	tport_add_evt->arg1 = (void *) tport;
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
-			 "iport fcid: 0x%x add tport event fcid: 0x%x\n",
-			 tport->fcid, iport->fcid);
-	list_add_tail(&tport_add_evt->links, &fnic->tport_event_list);
-	queue_work(fnic_event_queue, &fnic->tport_work);
 }
 
 
@@ -2881,21 +3034,21 @@ fdls_process_rff_id_rsp(struct fnic_iport_s *iport,
 	uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
 
 	if (fdls_get_state(fdls) != FDLS_STATE_REGISTER_FC4_FEATURES) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "RFF_ID resp recvd in state(%d). Dropping.",
 					 fdls_get_state(fdls));
 		return;
 	}
 
 	if (iport->active_oxid_fabric_req != oxid) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
 			fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
 		return;
 	}
 
 	rsp = FNIC_STD_GET_FC_CT_CMD((&rff_rsp->fc_std_ct_hdr));
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: FDLS process RFF ID response: 0x%04x", iport->fcid,
 				 (uint32_t) rsp);
 
@@ -2904,7 +3057,7 @@ fdls_process_rff_id_rsp(struct fnic_iport_s *iport,
 	switch (rsp) {
 	case FC_FS_ACC:
 		if (iport->fabric.timer_pending) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "Canceling fabric disc timer %p\n", iport);
 			fnic_del_fabric_timer_sync(fnic);
 		}
@@ -2918,18 +3071,18 @@ fdls_process_rff_id_rsp(struct fnic_iport_s *iport,
 		if (((reason_code == FC_FS_RJT_BSY)
 			|| (reason_code == FC_FS_RJT_UNABL))
 			&& (fdls->retry_counter < FDLS_RETRY_COUNT)) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "RFF_ID ret ELS_LS_RJT BUSY. Retry from timer routine %p",
 					 iport);
 
 			/* Retry again from the timer routine */
 			fdls->flags |= FNIC_FDLS_RETRY_FRAME;
 		} else {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "RFF_ID returned ELS_LS_RJT. Halting discovery %p",
 			 iport);
 			if (iport->fabric.timer_pending) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "Canceling fabric disc timer %p\n", iport);
 				fnic_del_fabric_timer_sync(fnic);
 			}
@@ -2954,14 +3107,14 @@ fdls_process_rft_id_rsp(struct fnic_iport_s *iport,
 	uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
 
 	if (fdls_get_state(fdls) != FDLS_STATE_REGISTER_FC4_TYPES) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "RFT_ID resp recvd in state(%d). Dropping.",
 					 fdls_get_state(fdls));
 		return;
 	}
 
 	if (iport->active_oxid_fabric_req != oxid) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
 			fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
 		return;
@@ -2969,7 +3122,7 @@ fdls_process_rft_id_rsp(struct fnic_iport_s *iport,
 
 
 	rsp = FNIC_STD_GET_FC_CT_CMD((&rft_rsp->fc_std_ct_hdr));
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: FDLS process RFT ID response: 0x%04x", iport->fcid,
 				 (uint32_t) rsp);
 
@@ -2978,7 +3131,7 @@ fdls_process_rft_id_rsp(struct fnic_iport_s *iport,
 	switch (rsp) {
 	case FC_FS_ACC:
 		if (iport->fabric.timer_pending) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "Canceling fabric disc timer %p\n", iport);
 			fnic_del_fabric_timer_sync(fnic);
 		}
@@ -2992,19 +3145,19 @@ fdls_process_rft_id_rsp(struct fnic_iport_s *iport,
 		if (((reason_code == FC_FS_RJT_BSY)
 			|| (reason_code == FC_FS_RJT_UNABL))
 			&& (fdls->retry_counter < FDLS_RETRY_COUNT)) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: RFT_ID ret ELS_LS_RJT BUSY. Retry from timer routine",
 				 iport->fcid);
 
 			/* Retry again from the timer routine */
 			fdls->flags |= FNIC_FDLS_RETRY_FRAME;
 		} else {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: RFT_ID REJ. Halting discovery reason %d expl %d",
 				 iport->fcid, reason_code,
 			 rft_rsp->fc_std_ct_hdr.ct_explan);
 			if (iport->fabric.timer_pending) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "Canceling fabric disc timer %p\n", iport);
 				fnic_del_fabric_timer_sync(fnic);
 			}
@@ -3029,20 +3182,20 @@ fdls_process_rpn_id_rsp(struct fnic_iport_s *iport,
 	uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
 
 	if (fdls_get_state(fdls) != FDLS_STATE_RPN_ID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "RPN_ID resp recvd in state(%d). Dropping.",
 					 fdls_get_state(fdls));
 		return;
 	}
 	if (iport->active_oxid_fabric_req != oxid) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
 			fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
 		return;
 	}
 
 	rsp = FNIC_STD_GET_FC_CT_CMD((&rpn_rsp->fc_std_ct_hdr));
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: FDLS process RPN ID response: 0x%04x", iport->fcid,
 				 (uint32_t) rsp);
 	fdls_free_oxid(iport, oxid, &iport->active_oxid_fabric_req);
@@ -3050,7 +3203,7 @@ fdls_process_rpn_id_rsp(struct fnic_iport_s *iport,
 	switch (rsp) {
 	case FC_FS_ACC:
 		if (iport->fabric.timer_pending) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "Canceling fabric disc timer %p\n", iport);
 			fnic_del_fabric_timer_sync(fnic);
 		}
@@ -3064,17 +3217,17 @@ fdls_process_rpn_id_rsp(struct fnic_iport_s *iport,
 		if (((reason_code == FC_FS_RJT_BSY)
 			|| (reason_code == FC_FS_RJT_UNABL))
 			&& (fdls->retry_counter < FDLS_RETRY_COUNT)) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "RPN_ID returned REJ BUSY. Retry from timer routine %p",
 					 iport);
 
 			/* Retry again from the timer routine */
 			fdls->flags |= FNIC_FDLS_RETRY_FRAME;
 		} else {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "RPN_ID ELS_LS_RJT. Halting discovery %p", iport);
 			if (iport->fabric.timer_pending) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "Canceling fabric disc timer %p\n", iport);
 				fnic_del_fabric_timer_sync(fnic);
 			}
@@ -3097,18 +3250,18 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport,
 	struct fnic *fnic = iport->fnic;
 	uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "FDLS process SCR response: 0x%04x",
 		 (uint32_t) scr_rsp->scr.scr_cmd);
 
 	if (fdls_get_state(fdls) != FDLS_STATE_SCR) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "SCR resp recvd in state(%d). Dropping.",
 					 fdls_get_state(fdls));
 		return;
 	}
 	if (iport->active_oxid_fabric_req != oxid) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
 			fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
 	}
@@ -3119,7 +3272,7 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport,
 	case ELS_LS_ACC:
 		atomic64_inc(&iport->iport_stats.fabric_scr_ls_accepts);
 		if (iport->fabric.timer_pending) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "Canceling fabric disc timer %p\n", iport);
 			fnic_del_fabric_timer_sync(fnic);
 		}
@@ -3133,17 +3286,17 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport,
 		if (((els_rjt->rej.er_reason == ELS_RJT_BUSY)
 	     || (els_rjt->rej.er_reason == ELS_RJT_UNAB))
 			&& (fdls->retry_counter < FDLS_RETRY_COUNT)) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "SCR ELS_LS_RJT BUSY. Retry from timer routine %p",
 						 iport);
 			/* Retry again from the timer routine */
 			fdls->flags |= FNIC_FDLS_RETRY_FRAME;
 		} else {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "SCR returned ELS_LS_RJT. Halting discovery %p",
 						 iport);
 			if (iport->fabric.timer_pending) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 					     "Canceling fabric disc timer %p\n",
 					     iport);
 				fnic_del_fabric_timer_sync(fnic);
@@ -3171,7 +3324,7 @@ fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport,
 	u32 old_link_down_cnt = iport->fnic->link_down_cnt;
 	struct fnic *fnic = iport->fnic;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: FDLS process GPN_FT tgt list", iport->fcid);
 
 	gpn_ft_tgt =
@@ -3185,7 +3338,7 @@ fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport,
 		fcid = ntoh24(gpn_ft_tgt->fcid);
 		wwpn = be64_to_cpu(gpn_ft_tgt->wwpn);
 
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "tport: 0x%x: ctrl:0x%x", fcid, gpn_ft_tgt->ctrl);
 
 		if (fcid == iport->fcid) {
@@ -3232,7 +3385,7 @@ fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport,
 		rem_len -= sizeof(struct fc_gpn_ft_rsp_iu);
 	}
 	if (rem_len <= 0) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "GPN_FT response: malformed/corrupt frame rxlen: %d remlen: %d",
 			 len, rem_len);
 }
@@ -3242,7 +3395,7 @@ fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport,
 		list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
 
 			if (!(tport->flags & FNIC_FDLS_TPORT_IN_GPN_FT_LIST)) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Remove port: 0x%x not found in GPN_FT list",
 					 tport->fcid);
 				fdls_delete_tport(iport, tport);
@@ -3271,7 +3424,7 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
 	struct fnic *fnic = iport->fnic;
 	uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "FDLS process GPN_FT response: iport state: %d len: %d",
 				 iport->state, len);
 
@@ -3291,14 +3444,14 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
 			  && ((fdls_get_state(fdls) == FDLS_STATE_RSCN_GPN_FT)
 				  || (fdls_get_state(fdls) == FDLS_STATE_SEND_GPNFT)
 				  || (fdls_get_state(fdls) == FDLS_STATE_TGT_DISCOVERY))))) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "GPNFT resp recvd in fab state(%d) iport_state(%d). Dropping.",
 			 fdls_get_state(fdls), iport->state);
 		return;
 	}
 
 	if (iport->active_oxid_fabric_req != oxid) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
 			fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
 	}
@@ -3311,10 +3464,10 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
 	switch (rsp) {
 
 	case FC_FS_ACC:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "0x%x: GPNFT_RSP accept", iport->fcid);
 		if (iport->fabric.timer_pending) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "0x%x: Canceling fabric disc timer\n",
 						 iport->fcid);
 			fnic_del_fabric_timer_sync(fnic);
@@ -3329,7 +3482,7 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
 		 * that will be taken care in next link up event
 		 */
 		if (iport->state != FNIC_IPORT_STATE_READY) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Halting target discovery: fab st: %d iport st: %d ",
 				 fdls_get_state(fdls), iport->state);
 			break;
@@ -3339,22 +3492,22 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
 
 	case FC_FS_RJT:
 		reason_code = gpn_ft_rsp->fc_std_ct_hdr.ct_reason;
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "0x%x: GPNFT_RSP Reject reason: %d", iport->fcid, reason_code);
 
 		if (((reason_code == FC_FS_RJT_BSY)
 		     || (reason_code == FC_FS_RJT_UNABL))
 			&& (fdls->retry_counter < FDLS_RETRY_COUNT)) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: GPNFT_RSP ret REJ/BSY. Retry from timer routine",
 				 iport->fcid);
 			/* Retry again from the timer routine */
 			fdls->flags |= FNIC_FDLS_RETRY_FRAME;
 		} else {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "0x%x: GPNFT_RSP reject", iport->fcid);
 			if (iport->fabric.timer_pending) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "0x%x: Canceling fabric disc timer\n",
 							 iport->fcid);
 				fnic_del_fabric_timer_sync(fnic);
@@ -3368,7 +3521,7 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
 			count = 0;
 			list_for_each_entry_safe(tport, next, &iport->tport_list,
 									 links) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "GPN_FT_REJECT: Remove port: 0x%x",
 							 tport->fcid);
 				fdls_delete_tport(iport, tport);
@@ -3378,7 +3531,7 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport,
 				}
 				count++;
 			}
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "GPN_FT_REJECT: Removed (0x%x) ports", count);
 		}
 		break;
@@ -3403,7 +3556,7 @@ fdls_process_fabric_logo_rsp(struct fnic_iport_s *iport,
 	uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
 
 	if (iport->active_oxid_fabric_req != oxid) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
 			fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
 	}
@@ -3412,7 +3565,7 @@ fdls_process_fabric_logo_rsp(struct fnic_iport_s *iport,
 	switch (flogo_rsp->els.fl_cmd) {
 	case ELS_LS_ACC:
 		if (iport->fabric.state != FDLS_STATE_FABRIC_LOGO) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Flogo response. Fabric not in LOGO state. Dropping! %p",
 				 iport);
 			return;
@@ -3422,25 +3575,25 @@ fdls_process_fabric_logo_rsp(struct fnic_iport_s *iport,
 		iport->state = FNIC_IPORT_STATE_LINK_WAIT;
 
 		if (iport->fabric.timer_pending) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "iport 0x%p Canceling fabric disc timer\n",
 						 iport);
 			fnic_del_fabric_timer_sync(fnic);
 		}
 		iport->fabric.timer_pending = 0;
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Flogo response from Fabric for did: 0x%x",
 		     ntoh24(fchdr->fh_d_id));
 		return;
 
 	case ELS_LS_RJT:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Flogo response from Fabric for did: 0x%x returned ELS_LS_RJT",
 		     ntoh24(fchdr->fh_d_id));
 		return;
 
 	default:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "FLOGO response not accepted or rejected: 0x%x",
 		     flogo_rsp->els.fl_cmd);
 	}
@@ -3458,17 +3611,17 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport,
 	struct fnic *fnic = iport->fnic;
 	uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: FDLS processing FLOGI response", iport->fcid);
 
 	if (fdls_get_state(fabric) != FDLS_STATE_FABRIC_FLOGI) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "FLOGI response received in state (%d). Dropping frame",
 					 fdls_get_state(fabric));
 		return;
 	}
 	if (iport->active_oxid_fabric_req != oxid) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
 			fdls_get_state(fabric), oxid, iport->active_oxid_fabric_req);
 		return;
@@ -3480,7 +3633,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport,
 	case ELS_LS_ACC:
 		atomic64_inc(&iport->iport_stats.fabric_flogi_ls_accepts);
 		if (iport->fabric.timer_pending) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "iport fcid: 0x%x Canceling fabric disc timer\n",
 						 iport->fcid);
 			fnic_del_fabric_timer_sync(fnic);
@@ -3490,7 +3643,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport,
 		iport->fabric.retry_counter = 0;
 		fcid = FNIC_STD_GET_D_ID(fchdr);
 		iport->fcid = ntoh24(fcid);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "0x%x: FLOGI response accepted", iport->fcid);
 
 		/* Learn the Service Params */
@@ -3500,7 +3653,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport,
 			iport->max_payload_size = min(rdf_size,
 								  iport->max_payload_size);
 
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "max_payload_size from fabric: %u set: %d", rdf_size,
 					 iport->max_payload_size);
 
@@ -3510,24 +3663,26 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport,
 		if (FNIC_LOGI_FEATURES(flogi_rsp->els) & FNIC_FC_EDTOV_NSEC)
 			iport->e_d_tov = iport->e_d_tov / FNIC_NSEC_TO_MSEC;
 
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "From fabric: R_A_TOV: %d E_D_TOV: %d",
 					 iport->r_a_tov, iport->e_d_tov);
 
-		fc_host_fabric_name(iport->fnic->host) =
-		get_unaligned_be64(&FNIC_LOGI_NODE_NAME(flogi_rsp->els));
-		fc_host_port_id(iport->fnic->host) = iport->fcid;
+		if (IS_FNIC_FCP_INITIATOR(fnic)) {
+			fc_host_fabric_name(iport->fnic->host) =
+					get_unaligned_be64(&FNIC_LOGI_NODE_NAME(flogi_rsp->els));
+			fc_host_port_id(iport->fnic->host) = iport->fcid;
+		}
 
 		fnic_fdls_learn_fcoe_macs(iport, rx_frame, fcid);
 
 		if (fnic_fdls_register_portid(iport, iport->fcid, rx_frame) != 0) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "0x%x: FLOGI registration failed", iport->fcid);
 			break;
 		}
 
 		memcpy(&fcmac[3], fcid, 3);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Adding vNIC device MAC addr: %02x:%02x:%02x:%02x:%02x:%02x",
 			 fcmac[0], fcmac[1], fcmac[2], fcmac[3], fcmac[4],
 			 fcmac[5]);
@@ -3535,7 +3690,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport,
 
 		if (fdls_get_state(fabric) == FDLS_STATE_FABRIC_FLOGI) {
 			fnic_fdls_start_plogi(iport);
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "FLOGI response received. Starting PLOGI");
 		} else {
 			/* From FDLS_STATE_FABRIC_FLOGI state fabric can only go to
@@ -3543,7 +3698,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport,
 			 * state, hence we don't have to worry about undoing:
 			 * the fnic_fdls_register_portid and vnic_dev_add_addr
 			 */
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "FLOGI response received in state (%d). Dropping frame",
 				 fdls_get_state(fabric));
 		}
@@ -3552,7 +3707,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport,
 	case ELS_LS_RJT:
 		atomic64_inc(&iport->iport_stats.fabric_flogi_ls_rejects);
 		if (fabric->retry_counter < iport->max_flogi_retries) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "FLOGI returned ELS_LS_RJT BUSY. Retry from timer routine %p",
 				 iport);
 
@@ -3560,11 +3715,11 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport,
 			fabric->flags |= FNIC_FDLS_RETRY_FRAME;
 
 		} else {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "FLOGI returned ELS_LS_RJT. Halting discovery %p",
 			 iport);
 			if (iport->fabric.timer_pending) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "iport 0x%p Canceling fabric disc timer\n",
 							 iport);
 				fnic_del_fabric_timer_sync(fnic);
@@ -3575,7 +3730,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport,
 		break;
 
 	default:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "FLOGI response not accepted: 0x%x",
 		     flogi_rsp->els.fl_cmd);
 		atomic64_inc(&iport->iport_stats.fabric_flogi_misc_rejects);
@@ -3594,13 +3749,13 @@ fdls_process_fabric_plogi_rsp(struct fnic_iport_s *iport,
 	uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
 
 	if (fdls_get_state((&iport->fabric)) != FDLS_STATE_FABRIC_PLOGI) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Fabric PLOGI response received in state (%d). Dropping frame",
 			 fdls_get_state(&iport->fabric));
 		return;
 	}
 	if (iport->active_oxid_fabric_req != oxid) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
 			fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req);
 		return;
@@ -3611,7 +3766,7 @@ fdls_process_fabric_plogi_rsp(struct fnic_iport_s *iport,
 	case ELS_LS_ACC:
 		atomic64_inc(&iport->iport_stats.fabric_plogi_ls_accepts);
 		if (iport->fabric.timer_pending) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "iport fcid: 0x%x fabric PLOGI response: Accepted\n",
 				 iport->fcid);
 			fnic_del_fabric_timer_sync(fnic);
@@ -3626,15 +3781,15 @@ fdls_process_fabric_plogi_rsp(struct fnic_iport_s *iport,
 		if (((els_rjt->rej.er_reason == ELS_RJT_BUSY)
 	     || (els_rjt->rej.er_reason == ELS_RJT_UNAB))
 			&& (iport->fabric.retry_counter < iport->max_plogi_retries)) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: Fabric PLOGI ELS_LS_RJT BUSY. Retry from timer routine",
 				 iport->fcid);
 		} else {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: Fabric PLOGI ELS_LS_RJT. Halting discovery",
 				 iport->fcid);
 			if (iport->fabric.timer_pending) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "iport fcid: 0x%x Canceling fabric disc timer\n",
 							 iport->fcid);
 				fnic_del_fabric_timer_sync(fnic);
@@ -3645,7 +3800,7 @@ fdls_process_fabric_plogi_rsp(struct fnic_iport_s *iport,
 		}
 		break;
 	default:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "PLOGI response not accepted: 0x%x",
 		     plogi_rsp->els.fl_cmd);
 		atomic64_inc(&iport->iport_stats.fabric_plogi_misc_rejects);
@@ -3664,7 +3819,7 @@ static void fdls_process_fdmi_plogi_rsp(struct fnic_iport_s *iport,
 	uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
 
 	if (iport->active_oxid_fdmi_plogi != oxid) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n",
 			fdls_get_state(fdls), oxid, iport->active_oxid_fdmi_plogi);
 		return;
@@ -3678,9 +3833,9 @@ static void fdls_process_fdmi_plogi_rsp(struct fnic_iport_s *iport,
 		iport->fabric.fdmi_pending = 0;
 		switch (plogi_rsp->els.fl_cmd) {
 		case ELS_LS_ACC:
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "FDLS process fdmi PLOGI response status: ELS_LS_ACC\n");
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Sending fdmi registration for port 0x%x\n",
 				 iport->fcid);
 
@@ -3691,7 +3846,7 @@ static void fdls_process_fdmi_plogi_rsp(struct fnic_iport_s *iport,
 				  round_jiffies(fdmi_tov));
 			break;
 		case ELS_LS_RJT:
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Fabric FDMI PLOGI returned ELS_LS_RJT reason: 0x%x",
 				     els_rjt->rej.er_reason);
 
@@ -3715,7 +3870,7 @@ static void fdls_process_fdmi_reg_ack(struct fnic_iport_s *iport,
 	uint16_t oxid;
 
 	if (!iport->fabric.fdmi_pending) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 			     "Received FDMI ack while not waiting: 0x%x\n",
 			     FNIC_STD_GET_OX_ID(fchdr));
 		return;
@@ -3725,7 +3880,7 @@ static void fdls_process_fdmi_reg_ack(struct fnic_iport_s *iport,
 
 	if ((iport->active_oxid_fdmi_rhba != oxid) &&
 		(iport->active_oxid_fdmi_rpa != oxid))  {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Incorrect OXID in response. oxid recvd: 0x%x, active oxids(rhba,rpa): 0x%x, 0x%x\n",
 			oxid, iport->active_oxid_fdmi_rhba, iport->active_oxid_fdmi_rpa);
 		return;
@@ -3738,13 +3893,13 @@ static void fdls_process_fdmi_reg_ack(struct fnic_iport_s *iport,
 		fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rpa);
 	}
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		"iport fcid: 0x%x: Received FDMI registration ack\n",
 		 iport->fcid);
 
 	if (!iport->fabric.fdmi_pending) {
 		timer_delete_sync(&iport->fabric.fdmi_timer);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "iport fcid: 0x%x: Canceling FDMI timer\n",
 					 iport->fcid);
 	}
@@ -3760,7 +3915,7 @@ static void fdls_process_fdmi_abts_rsp(struct fnic_iport_s *iport,
 	s_id = ntoh24(FNIC_STD_GET_S_ID(fchdr));
 
 	if (!(s_id != FC_FID_MGMT_SERV)) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			     "Received abts rsp with invalid SID: 0x%x. Dropping frame",
 			     s_id);
 		return;
@@ -3770,23 +3925,23 @@ static void fdls_process_fdmi_abts_rsp(struct fnic_iport_s *iport,
 
 	switch (FNIC_FRAME_TYPE(oxid)) {
 	case FNIC_FRAME_TYPE_FDMI_PLOGI:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Received FDMI PLOGI ABTS rsp with oxid: 0x%x", oxid);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "0x%x: iport->fabric.fdmi_pending: 0x%x",
 			 iport->fcid, iport->fabric.fdmi_pending);
 		fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_plogi);
 
 		iport->fabric.fdmi_pending &= ~FDLS_FDMI_PLOGI_PENDING;
 		iport->fabric.fdmi_pending &= ~FDLS_FDMI_ABORT_PENDING;
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "0x%x: iport->fabric.fdmi_pending: 0x%x",
 			 iport->fcid, iport->fabric.fdmi_pending);
 		break;
 	case FNIC_FRAME_TYPE_FDMI_RHBA:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Received FDMI RHBA ABTS rsp with oxid: 0x%x", oxid);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "0x%x: iport->fabric.fdmi_pending: 0x%x",
 			 iport->fcid, iport->fabric.fdmi_pending);
 
@@ -3800,14 +3955,14 @@ static void fdls_process_fdmi_abts_rsp(struct fnic_iport_s *iport,
 			iport->fabric.fdmi_pending &= ~FDLS_FDMI_ABORT_PENDING;
 
 		fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rhba);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "0x%x: iport->fabric.fdmi_pending: 0x%x",
 			 iport->fcid, iport->fabric.fdmi_pending);
 		break;
 	case FNIC_FRAME_TYPE_FDMI_RPA:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Received FDMI RPA ABTS rsp with oxid: 0x%x", oxid);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "0x%x: iport->fabric.fdmi_pending: 0x%x",
 			 iport->fcid, iport->fabric.fdmi_pending);
 
@@ -3821,12 +3976,12 @@ static void fdls_process_fdmi_abts_rsp(struct fnic_iport_s *iport,
 			iport->fabric.fdmi_pending &= ~FDLS_FDMI_ABORT_PENDING;
 
 		fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rpa);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "0x%x: iport->fabric.fdmi_pending: 0x%x",
 			 iport->fcid, iport->fabric.fdmi_pending);
 		break;
 	default:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Received abts rsp with invalid oxid: 0x%x. Dropping frame",
 			oxid);
 		break;
@@ -3861,7 +4016,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 
 	if (!((s_id == FC_FID_DIR_SERV) || (s_id == FC_FID_FLOGI)
 		  || (s_id == FC_FID_FCTRL))) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Received abts rsp with invalid SID: 0x%x. Dropping frame",
 			 s_id);
 		return;
@@ -3869,14 +4024,14 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 
 	oxid = FNIC_STD_GET_OX_ID(fchdr);
 	if (iport->active_oxid_fabric_req != oxid) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Received abts rsp with invalid oxid: 0x%x. Dropping frame",
 			oxid);
 		return;
 	}
 
 	if (iport->fabric.timer_pending) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Canceling fabric disc timer %p\n", iport);
 		fnic_del_fabric_timer_sync(fnic);
 	}
@@ -3884,11 +4039,11 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 	iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED;
 
 	if (fchdr->fh_r_ctl == FC_RCTL_BA_ACC) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Received abts rsp BA_ACC for fabric_state: %d OX_ID: 0x%x",
 		     fabric_state, be16_to_cpu(ba_acc->acc.ba_ox_id));
 	} else if (fchdr->fh_r_ctl == FC_RCTL_BA_RJT) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "BA_RJT fs: %d OX_ID: 0x%x rc: 0x%x rce: 0x%x",
 		     fabric_state, FNIC_STD_GET_OX_ID(&ba_rjt->fchdr),
 		     ba_rjt->rjt.br_reason, ba_rjt->rjt.br_explan);
@@ -3903,7 +4058,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 		if (iport->fabric.retry_counter < iport->max_flogi_retries)
 			fdls_send_fabric_flogi(iport);
 		else
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Exceeded max FLOGI retries");
 		break;
 	case FNIC_FRAME_TYPE_FABRIC_LOGO:
@@ -3914,7 +4069,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 		if (iport->fabric.retry_counter < iport->max_plogi_retries)
 			fdls_send_fabric_plogi(iport);
 		else
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Exceeded max PLOGI retries");
 		break;
 	case FNIC_FRAME_TYPE_FABRIC_RPN:
@@ -3928,7 +4083,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 		if (iport->fabric.retry_counter < FDLS_RETRY_COUNT)
 			fdls_send_scr(iport);
 		else {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				"SCR exhausted retries. Start fabric PLOGI %p",
 				 iport);
 			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
@@ -3938,7 +4093,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 		if (iport->fabric.retry_counter < FDLS_RETRY_COUNT)
 			fdls_send_register_fc4_types(iport);
 		else {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				"RFT exhausted retries. Start fabric PLOGI %p",
 				 iport);
 			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
@@ -3948,7 +4103,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 		if (iport->fabric.retry_counter < FDLS_RETRY_COUNT)
 			fdls_send_register_fc4_features(iport);
 		else {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				"RFF exhausted retries. Start fabric PLOGI %p",
 				 iport);
 			fnic_fdls_start_plogi(iport);	/* go back to fabric Plogi */
@@ -3958,7 +4113,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 		if (iport->fabric.retry_counter <= FDLS_RETRY_COUNT)
 			fdls_send_gpn_ft(iport, fabric_state);
 		else
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				"GPN FT exhausted retries. Start fabric PLOGI %p",
 				iport);
 		break;
@@ -3967,7 +4122,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport,
 		 * We should not be here since we already validated rx oxid with
 		 * our active_oxid_fabric_req
 		 */
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Invalid OXID/active oxid 0x%x\n", oxid);
 		WARN_ON(true);
 		return;
@@ -3987,7 +4142,7 @@ fdls_process_abts_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 			sizeof(struct fc_std_abts_ba_acc);
 
 	nport_id = ntoh24(fchdr->fh_s_id);
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Received abort from SID 0x%8x", nport_id);
 
 	tport = fnic_find_tport_by_fcid(iport, nport_id);
@@ -4000,7 +4155,7 @@ fdls_process_abts_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 				"0x%x: Failed to allocate frame to send response for ABTS req",
 				iport->fcid);
 		return;
@@ -4021,7 +4176,7 @@ fdls_process_abts_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 	pba_acc->acc.ba_rx_id = cpu_to_be16(FNIC_STD_GET_RX_ID(fchdr));
 	pba_acc->acc.ba_ox_id = cpu_to_be16(FNIC_STD_GET_OX_ID(fchdr));
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "0x%x: FDLS send BA ACC with oxid: 0x%x",
 		 iport->fcid, oxid);
 
@@ -4041,7 +4196,7 @@ fdls_process_unsupported_els_req(struct fnic_iport_s *iport,
 			sizeof(struct fc_std_els_rjt_rsp);
 
 	if (iport->fcid != d_id) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Dropping unsupported ELS with illegal frame bits 0x%x\n",
 			 d_id);
 		atomic64_inc(&iport->iport_stats.unsupported_frames_dropped);
@@ -4050,7 +4205,7 @@ fdls_process_unsupported_els_req(struct fnic_iport_s *iport,
 
 	if ((iport->state != FNIC_IPORT_STATE_READY)
 		&& (iport->state != FNIC_IPORT_STATE_FABRIC_DISC)) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Dropping unsupported ELS request in iport state: %d",
 			 iport->state);
 		atomic64_inc(&iport->iport_stats.unsupported_frames_dropped);
@@ -4059,7 +4214,7 @@ fdls_process_unsupported_els_req(struct fnic_iport_s *iport,
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 			"Failed to allocate frame to send response to unsupported ELS request");
 		return;
 	}
@@ -4067,7 +4222,7 @@ fdls_process_unsupported_els_req(struct fnic_iport_s *iport,
 	pls_rsp = (struct fc_std_els_rjt_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 	fdls_init_els_rjt_frame(frame, iport);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: Process unsupported ELS request from SID: 0x%x",
 		     iport->fcid, ntoh24(fchdr->fh_s_id));
 
@@ -4094,12 +4249,12 @@ fdls_process_rls_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 	uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
 			sizeof(struct fc_std_rls_acc);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Process RLS request %d", iport->fnic->fnic_num);
 
 	if ((iport->state != FNIC_IPORT_STATE_READY)
 		&& (iport->state != FNIC_IPORT_STATE_FABRIC_DISC)) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Received RLS req in iport state: %d. Dropping the frame.",
 			 iport->state);
 		return;
@@ -4107,7 +4262,7 @@ fdls_process_rls_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 				"Failed to allocate frame to send RLS accept");
 		return;
 	}
@@ -4148,33 +4303,33 @@ fdls_process_els_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr,
 
 	if ((iport->state != FNIC_IPORT_STATE_READY)
 		&& (iport->state != FNIC_IPORT_STATE_FABRIC_DISC)) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Dropping ELS frame type: 0x%x in iport state: %d",
 				 type, iport->state);
 		return;
 	}
 	switch (type) {
 	case ELS_ECHO:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "sending LS_ACC for ECHO request %d\n",
 					 iport->fnic->fnic_num);
 		break;
 
 	case ELS_RRQ:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "sending LS_ACC for RRQ request %d\n",
 					 iport->fnic->fnic_num);
 		break;
 
 	default:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "sending LS_ACC for 0x%x ELS frame\n", type);
 		break;
 	}
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 				"Failed to allocate frame to send ELS response for 0x%x",
 				type);
 		return;
@@ -4220,17 +4375,17 @@ fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport,
 
 	tport = fnic_find_tport_by_fcid(iport, s_id);
 	if (!tport) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 					 "Received tgt abts rsp with invalid SID: 0x%x", s_id);
 		return;
 	}
 	if (tport->timer_pending) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 					 "tport 0x%p Canceling fabric disc timer\n", tport);
 		fnic_del_tport_timer_sync(fnic, tport);
 	}
 	if (iport->state != FNIC_IPORT_STATE_READY) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 					 "Received tgt abts rsp in iport state(%d). Dropping.",
 					 iport->state);
 		return;
@@ -4245,15 +4400,15 @@ fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport,
 	switch (frame_type) {
 	case FNIC_FRAME_TYPE_TGT_ADISC:
 		if (fchdr->fh_r_ctl == FC_RCTL_BA_ACC) {
-			FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_ERR, fnic,
 				     "OX_ID: 0x%x tgt_fcid: 0x%x rcvd tgt adisc abts resp BA_ACC",
 				     be16_to_cpu(ba_acc->acc.ba_ox_id),
 				     tport->fcid);
 		} else if (fchdr->fh_r_ctl == FC_RCTL_BA_RJT) {
-			FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_ERR, fnic,
 				 "ADISC BA_RJT rcvd tport_fcid: 0x%x tport_state: %d ",
 				 tport->fcid, tport_state);
-			FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_ERR, fnic,
 				 "reason code: 0x%x reason code explanation:0x%x ",
 				     ba_rjt->rjt.br_reason,
 				     ba_rjt->rjt.br_explan);
@@ -4265,7 +4420,7 @@ fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport,
 			return;
 		}
 		fdls_free_oxid(iport, oxid, &tport->active_oxid);
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 					 "ADISC not responding. Deleting target port: 0x%x",
 					 tport->fcid);
 		fdls_delete_tport(iport, tport);
@@ -4278,14 +4433,14 @@ fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport,
 		break;
 	case FNIC_FRAME_TYPE_TGT_PLOGI:
 		if (fchdr->fh_r_ctl == FC_RCTL_BA_ACC) {
-			FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_ERR, fnic,
 				 "Received tgt PLOGI abts response BA_ACC tgt_fcid: 0x%x",
 				 tport->fcid);
 		} else if (fchdr->fh_r_ctl == FC_RCTL_BA_RJT) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "PLOGI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x",
 				     tport->fcid, FNIC_STD_GET_OX_ID(fchdr));
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "reason code: 0x%x reason code explanation: 0x%x",
 				     ba_rjt->rjt.br_reason,
 				     ba_rjt->rjt.br_explan);
@@ -4308,14 +4463,14 @@ fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport,
 		break;
 	case FNIC_FRAME_TYPE_TGT_PRLI:
 		if (fchdr->fh_r_ctl == FC_RCTL_BA_ACC) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: Received tgt PRLI abts response BA_ACC",
 				 tport->fcid);
 		} else if (fchdr->fh_r_ctl == FC_RCTL_BA_RJT) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "PRLI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x ",
 				     tport->fcid, FNIC_STD_GET_OX_ID(fchdr));
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "reason code: 0x%x reason code explanation: 0x%x",
 				     ba_rjt->rjt.br_reason,
 				     ba_rjt->rjt.br_explan);
@@ -4331,7 +4486,7 @@ fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport,
 		fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI);
 		break;
 	default:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Received ABTS response for unknown frame %p", iport);
 		break;
 	}
@@ -4351,14 +4506,14 @@ fdls_process_plogi_req(struct fnic_iport_s *iport,
 			sizeof(struct fc_std_els_rjt_rsp);
 
 	if (iport->fcid != d_id) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Received PLOGI with illegal frame bits. Dropping frame from 0x%x",
 			 d_id);
 		return;
 	}
 
 	if (iport->state != FNIC_IPORT_STATE_READY) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Received PLOGI request in iport state: %d Dropping frame",
 			 iport->state);
 		return;
@@ -4366,7 +4521,7 @@ fdls_process_plogi_req(struct fnic_iport_s *iport,
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 			"Failed to allocate frame to send response to PLOGI request");
 		return;
 	}
@@ -4374,7 +4529,7 @@ fdls_process_plogi_req(struct fnic_iport_s *iport,
 	pplogi_rsp = (struct fc_std_els_rjt_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET);
 	fdls_init_els_rjt_frame(frame, iport);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: Process PLOGI request from SID: 0x%x",
 				 iport->fcid, ntoh24(fchdr->fh_s_id));
 
@@ -4404,11 +4559,11 @@ fdls_process_logo_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 	nport_id = ntoh24(logo->els.fl_n_port_id);
 	nport_name = be64_to_cpu(logo->els.fl_n_port_wwn);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Process LOGO request from fcid: 0x%x", nport_id);
 
 	if (iport->state != FNIC_IPORT_STATE_READY) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 			 "Dropping LOGO req from 0x%x in iport state: %d",
 			 nport_id, iport->state);
 		return;
@@ -4418,19 +4573,19 @@ fdls_process_logo_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 
 	if (!tport) {
 		/* We are not logged in with the nport, log and drop... */
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 			 "Received LOGO from an nport not logged in: 0x%x(0x%llx)",
 			 nport_id, nport_name);
 		return;
 	}
 	if (tport->fcid != nport_id) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 		 "Received LOGO with invalid target port fcid: 0x%x(0x%llx)",
 		 nport_id, nport_name);
 		return;
 	}
 	if (tport->timer_pending) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 					 "tport fcid 0x%x: Canceling disc timer\n",
 					 tport->fcid);
 		fnic_del_tport_timer_sync(fnic, tport);
@@ -4447,7 +4602,7 @@ fdls_process_logo_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 		if ((iport->state == FNIC_IPORT_STATE_READY)
 			&& (fdls_get_state(&iport->fabric) != FDLS_STATE_SEND_GPNFT)
 			&& (fdls_get_state(&iport->fabric) != FDLS_STATE_RSCN_GPN_FT)) {
-			FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_ERR, fnic,
 						 "Sending GPNFT in response to LOGO from Target:0x%x",
 						 nport_id);
 			fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
@@ -4460,7 +4615,7 @@ fdls_process_logo_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 		fdls_send_logo_resp(iport, &logo->fchdr);
 		if ((fdls_get_state(&iport->fabric) != FDLS_STATE_SEND_GPNFT) &&
 			(fdls_get_state(&iport->fabric) != FDLS_STATE_RSCN_GPN_FT)) {
-			FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_ERR, fnic,
 						 "Sending GPNFT in response to LOGO from Target:0x%x",
 						 nport_id);
 			fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT);
@@ -4487,11 +4642,11 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 
 	atomic64_inc(&iport->iport_stats.num_rscns);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "FDLS process RSCN %p", iport);
 
 	if (iport->state != FNIC_IPORT_STATE_READY) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "FDLS RSCN received in state(%d). Dropping",
 					 fdls_get_state(fdls));
 		return;
@@ -4508,18 +4663,18 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 		if ((rscn_payload_len == 0xFFFF)
 		    && (sid == FC_FID_FCTRL)) {
 			rscn_type = PC_RSCN;
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				     "pcrscn: PCRSCN received. sid: 0x%x payload len: 0x%x",
 				     sid, rscn_payload_len);
 		} else {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "RSCN payload_len: 0x%x page_len: 0x%x",
 				     rscn_payload_len, rscn->els.rscn_page_len);
 			/* if this happens then we need to send ADISC to all the tports. */
 			list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
 				if (tport->state == FDLS_TGT_STATE_READY)
 					tport->flags |= FNIC_FDLS_TPORT_SEND_ADISC;
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "RSCN for port id: 0x%x", tport->fcid);
 			}
 		} /* end else */
@@ -4527,7 +4682,7 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 		num_ports = (rscn_payload_len - 4) / rscn->els.rscn_page_len;
 		rscn_port = (struct fc_els_rscn_page *)(rscn + 1);
 	}
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "RSCN received for num_ports: %d payload_len: %d page_len: %d ",
 		     num_ports, rscn_payload_len, rscn->els.rscn_page_len);
 
@@ -4551,14 +4706,14 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 				if (tport->state == FDLS_TGT_STATE_READY)
 					tport->flags |= FNIC_FDLS_TPORT_SEND_ADISC;
 
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "RSCN for port id: 0x%x", tport->fcid);
 			}
 			break;
 		}
 		tport = fnic_find_tport_by_fcid(iport, nport_id);
 
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "RSCN port id list: 0x%x", nport_id);
 
 		if (!tport) {
@@ -4573,13 +4728,13 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 		rscn_type == PC_RSCN && fnic->role == FNIC_ROLE_FCP_INITIATOR) {
 
 		if (fnic->pc_rscn_handling_status == PC_RSCN_HANDLING_IN_PROGRESS) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "PCRSCN handling already in progress. Skip host reset: %d",
 				 iport->fnic->fnic_num);
 			return;
 		}
 
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Processing PCRSCN. Queuing fnic for host reset: %d",
 			 iport->fnic->fnic_num);
 		fnic->pc_rscn_handling_status = PC_RSCN_HANDLING_IN_PROGRESS;
@@ -4595,7 +4750,7 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
 		queue_work(reset_fnic_work_queue, &reset_fnic_work);
 		spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
 	} else {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "FDLS process RSCN sending GPN_FT: newports: %d", newports);
 		fdls_send_gpn_ft(iport, FDLS_STATE_RSCN_GPN_FT);
 		fdls_send_rscn_resp(iport, fchdr);
@@ -4606,9 +4761,11 @@ void fnic_fdls_disc_start(struct fnic_iport_s *iport)
 {
 	struct fnic *fnic = iport->fnic;
 
-	fc_host_fabric_name(iport->fnic->host) = 0;
-	fc_host_post_event(iport->fnic->host, fc_get_event_number(),
-					   FCH_EVT_LIPRESET, 0);
+	if (IS_FNIC_FCP_INITIATOR(fnic)) {
+		fc_host_fabric_name(iport->fnic->host) = 0;
+		fc_host_post_event(iport->fnic->host, fc_get_event_number(),
+						FCH_EVT_LIPRESET, 0);
+	}
 
 	if (!iport->usefip) {
 		if (iport->flags & FNIC_FIRST_LINK_UP) {
@@ -4644,20 +4801,20 @@ fdls_process_adisc_req(struct fnic_iport_s *iport,
 	uint16_t acc_frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
 			sizeof(struct fc_std_els_adisc);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Process ADISC request %d", iport->fnic->fnic_num);
 
 	fcid = FNIC_STD_GET_S_ID(fchdr);
 	tgt_fcid = ntoh24(fcid);
 	tport = fnic_find_tport_by_fcid(iport, tgt_fcid);
 	if (!tport) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 					 "tport for fcid: 0x%x not found. Dropping ADISC req.",
 					 tgt_fcid);
 		return;
 	}
 	if (iport->state != FNIC_IPORT_STATE_READY) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 			 "Dropping ADISC req from fcid: 0x%x in iport state: %d",
 			 tgt_fcid, iport->state);
 		return;
@@ -4668,16 +4825,16 @@ fdls_process_adisc_req(struct fnic_iport_s *iport,
 
 	if ((frame_wwnn != tport->wwnn) || (frame_wwpn != tport->wwpn)) {
 		/* send reject */
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 			 "ADISC req from fcid: 0x%x mismatch wwpn: 0x%llx wwnn: 0x%llx",
 			 tgt_fcid, frame_wwpn, frame_wwnn);
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 			 "local tport wwpn: 0x%llx wwnn: 0x%llx. Sending RJT",
 			 tport->wwpn, tport->wwnn);
 
 		rjt_frame = fdls_alloc_frame(iport);
 		if (rjt_frame == NULL) {
-			FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_ERR, fnic,
 				"Failed to allocate rjt_frame to send response to ADISC request");
 			return;
 		}
@@ -4700,7 +4857,7 @@ fdls_process_adisc_req(struct fnic_iport_s *iport,
 
 	acc_frame = fdls_alloc_frame(iport);
 	if (acc_frame == NULL) {
-		FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_ERR, fnic,
 				"Failed to allocate frame to send ADISC accept");
 		return;
 	}
@@ -4754,7 +4911,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 	/* some common validation */
 	if (fdls_get_state(fabric) > FDLS_STATE_FABRIC_FLOGI) {
 		if (iport->fcid != d_id || (!FNIC_FC_FRAME_CS_CTL(fchdr))) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				     "invalid frame received. Dropping frame");
 			return -1;
 		}
@@ -4764,14 +4921,14 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 	if ((fchdr->fh_r_ctl == FC_RCTL_BA_ACC)
 	|| (fchdr->fh_r_ctl == FC_RCTL_BA_RJT)) {
 		if (!(FNIC_FC_FRAME_TYPE_BLS(fchdr))) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "Received ABTS invalid frame. Dropping frame");
 			return -1;
 
 		}
 		if (fdls_is_oxid_fabric_req(oxid)) {
 			if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 					"Received unexpected ABTS RSP(oxid:0x%x) from 0x%x. Dropping frame",
 					oxid, s_id);
 				return -1;
@@ -4781,8 +4938,10 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 			return FNIC_FDMI_BLS_ABTS_RSP;
 		} else if (fdls_is_oxid_tgt_req(oxid)) {
 			return FNIC_TPORT_BLS_ABTS_RSP;
+		} else if (fdls_is_oxid_nvme_req(oxid)) {
+			return FNIC_LS_REQ_ABTS_RSP;
 		}
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Received ABTS rsp with unknown oxid(0x%x) from 0x%x. Dropping frame",
 			oxid, s_id);
 		return -1;
@@ -4791,7 +4950,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 	/* BLS ABTS Req */
 	if ((fchdr->fh_r_ctl == FC_RCTL_BA_ABTS)
 	&& (FNIC_FC_FRAME_TYPE_BLS(fchdr))) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Receiving Abort Request from s_id: 0x%x", s_id);
 		return FNIC_BLS_ABTS_REQ;
 	}
@@ -4803,7 +4962,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 			if ((!FNIC_FC_FRAME_FCTL_FIRST_LAST_SEQINIT(fchdr))
 				|| (!FNIC_FC_FRAME_UNSOLICITED(fchdr))
 				|| (!FNIC_FC_FRAME_TYPE_ELS(fchdr))) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "Received LOGO invalid frame. Dropping frame");
 				return -1;
 			}
@@ -4812,12 +4971,12 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 			if ((!FNIC_FC_FRAME_FCTL_FIRST_LAST_SEQINIT(fchdr))
 				|| (!FNIC_FC_FRAME_TYPE_ELS(fchdr))
 				|| (!FNIC_FC_FRAME_UNSOLICITED(fchdr))) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "Received RSCN invalid FCTL. Dropping frame");
 				return -1;
 			}
 			if (s_id != FC_FID_FCTRL)
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 				     "Received RSCN from target FCTL: 0x%x type: 0x%x s_id: 0x%x.",
 				     fchdr->fh_f_ctl[0], fchdr->fh_type, s_id);
 			return FNIC_ELS_RSCN_REQ;
@@ -4832,7 +4991,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 		case ELS_RRQ:
 			return FNIC_ELS_RRQ;
 		default:
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Unsupported frame (type:0x%02x) from fcid: 0x%x",
 				 type, s_id);
 			return FNIC_ELS_UNSUPPORTED_REQ;
@@ -4841,14 +5000,14 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 
 	/* solicited response from fabric or target */
 	oxid_frame_type = FNIC_FRAME_TYPE(oxid);
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 			"oxid frame code: 0x%x, oxid: 0x%x\n", oxid_frame_type, oxid);
 	switch (oxid_frame_type) {
 	case FNIC_FRAME_TYPE_FABRIC_FLOGI:
 		if (type == ELS_LS_ACC) {
 			if ((s_id != FC_FID_FLOGI)
 				|| (!FNIC_FC_FRAME_TYPE_ELS(fchdr))) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Received unknown frame. Dropping frame");
 				return -1;
 			}
@@ -4859,7 +5018,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 		if (type == ELS_LS_ACC) {
 			if ((s_id != FC_FID_DIR_SERV)
 				|| (!FNIC_FC_FRAME_TYPE_ELS(fchdr))) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Received unknown frame. Dropping frame");
 				return -1;
 			}
@@ -4870,7 +5029,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 		if (type == ELS_LS_ACC) {
 			if ((s_id != FC_FID_FCTRL)
 				|| (!FNIC_FC_FRAME_TYPE_ELS(fchdr))) {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Received unknown frame. Dropping frame");
 				return -1;
 			}
@@ -4879,7 +5038,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 
 	case FNIC_FRAME_TYPE_FABRIC_RPN:
 		if ((s_id != FC_FID_DIR_SERV) || (!FNIC_FC_FRAME_TYPE_FC_GS(fchdr))) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Received unknown frame. Dropping frame");
 			return -1;
 		}
@@ -4887,7 +5046,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 
 	case FNIC_FRAME_TYPE_FABRIC_RFT:
 		if ((s_id != FC_FID_DIR_SERV) || (!FNIC_FC_FRAME_TYPE_FC_GS(fchdr))) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Received unknown frame. Dropping frame");
 			return -1;
 		}
@@ -4895,7 +5054,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 
 	case FNIC_FRAME_TYPE_FABRIC_RFF:
 		if ((s_id != FC_FID_DIR_SERV) || (!FNIC_FC_FRAME_TYPE_FC_GS(fchdr))) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Received unknown frame. Dropping frame");
 			return -1;
 		}
@@ -4903,7 +5062,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 
 	case FNIC_FRAME_TYPE_FABRIC_GPN_FT:
 		if ((s_id != FC_FID_DIR_SERV) || (!FNIC_FC_FRAME_TYPE_FC_GS(fchdr))) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Received unknown frame. Dropping frame");
 			return -1;
 		}
@@ -4925,7 +5084,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 		return FNIC_TPORT_ADISC_RSP;
 	case FNIC_FRAME_TYPE_TGT_LOGO:
 		if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				"Dropping Unknown frame in tport solicited exchange range type: 0x%x.",
 				     fchdr->fh_type);
 			return -1;
@@ -4933,7 +5092,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport,
 		return FNIC_TPORT_LOGO_RSP;
 	default:
 		/* Drop the Rx frame and log/stats it */
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Solicited response: unknown OXID: 0x%x", oxid);
 		return -1;
 	}
@@ -5003,7 +5162,7 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
 		break;
 	case FNIC_TPORT_LOGO_RSP:
 		/* Logo response from tgt which we have deleted */
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Logo response from tgt: 0x%x",
 			     ntoh24(fchdr->fh_s_id));
 		break;
@@ -5013,6 +5172,9 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
 	case FNIC_FABRIC_BLS_ABTS_RSP:
 			fdls_process_fabric_abts_rsp(iport, fchdr);
 		break;
+	case FNIC_LS_REQ_ABTS_RSP:
+		nvfnic_process_ls_abts_rsp(iport, fchdr);
+		break;
 	case FNIC_FDMI_BLS_ABTS_RSP:
 		fdls_process_fdmi_abts_rsp(iport, fchdr);
 		break;
@@ -5046,9 +5208,9 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame,
 		fdls_process_fdmi_reg_ack(iport, fchdr, frame_type);
 		break;
 	default:
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "s_id: 0x%x d_did: 0x%x", s_id, d_id);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Received unknown FCoE frame of len: %d. Dropping frame", len);
 		break;
 	}
@@ -5065,20 +5227,24 @@ void fnic_fdls_link_down(struct fnic_iport_s *iport)
 	struct fnic_tport_s *tport, *next;
 	struct fnic *fnic = iport->fnic;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: FDLS processing link down", iport->fcid);
 
 	fdls_set_state((&iport->fabric), FDLS_STATE_LINKDOWN);
 	iport->fabric.flags = 0;
 
-	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
-	fnic_fcpio_reset(iport->fnic);
-	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
-	list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	if (IS_FNIC_FCP_INITIATOR(fnic) ||
+	    IS_FNIC_NVME_INITIATOR(fnic)) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		fnic_fcpio_reset(iport->fnic);
+		spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+		list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "removing rport: 0x%x", tport->fcid);
-		fdls_delete_tport(iport, tport);
+			fdls_delete_tport(iport, tport);
+		}
 	}
+	fdls_reset_oxid_pool(iport);
 
 	if (fnic_fdmi_support == 1) {
 		if (iport->fabric.fdmi_pending > 0) {
@@ -5088,6 +5254,6 @@ void fnic_fdls_link_down(struct fnic_iport_s *iport)
 		iport->flags &= ~FNIC_FDMI_ACTIVE;
 	}
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "0x%x: FDLS finish processing link down", iport->fcid);
 }
diff --git a/drivers/scsi/fnic/fdls_fc.h b/drivers/scsi/fnic/fdls_fc.h
index 012f43afd083..cdf84462dd37 100644
--- a/drivers/scsi/fnic/fdls_fc.h
+++ b/drivers/scsi/fnic/fdls_fc.h
@@ -30,6 +30,9 @@
 #include <linux/if_ether.h>
 #include <scsi/fc/fc_encaps.h>
 #include <scsi/fc/fc_fcoe.h>
+#include <linux/nvme.h>
+#include <linux/nvme-fc.h>
+#include <linux/nvme-fc-driver.h>
 
 #define FDLS_MIN_FRAMES	(32)
 #define FDLS_MIN_FRAME_ELEM	(4)
@@ -39,6 +42,9 @@
 #define FNIC_FCP_SP_CONF_CMPL   0x00000080
 #define FNIC_FCP_SP_RETRY       0x00000100
 
+#define FNIC_NVME_SP_INITIATOR   0x00000020
+#define FNIC_NVME_SP_SLER        0x00000100
+
 #define FNIC_FC_CONCUR_SEQS    (0xFF)
 #define FNIC_FC_RO_INFO        (0x1F)
 
@@ -50,6 +56,7 @@
 
 #define FNIC_FCP_RSP_FCTL      (0x000099)
 #define FNIC_REQ_ABTS_FCTL     (0x000009)
+#define FNIC_NVME_LS_REQ_FCTL  (0x000029)
 
 #define FNIC_FC_PH_VER_HI      (0x20)
 #define FNIC_FC_PH_VER_LO      (0x20)
diff --git a/drivers/scsi/fnic/fip.c b/drivers/scsi/fnic/fip.c
index 132f00512ee1..231dec180675 100644
--- a/drivers/scsi/fnic/fip.c
+++ b/drivers/scsi/fnic/fip.c
@@ -27,7 +27,7 @@ void fnic_fcoe_reset_vlans(struct fnic *fnic)
 	}
 
 	spin_unlock_irqrestore(&fnic->vlans_lock, flags);
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "Reset vlan complete\n");
 }
 
@@ -46,7 +46,7 @@ void fnic_fcoe_send_vlan_req(struct fnic *fnic)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FIP_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send VLAN req");
 		return;
 	}
@@ -54,10 +54,10 @@ void fnic_fcoe_send_vlan_req(struct fnic *fnic)
 	fnic_fcoe_reset_vlans(fnic);
 
 	fnic->set_vlan(fnic, 0);
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "set vlan done\n");
 
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "got MAC 0x%x:%x:%x:%x:%x:%x\n", iport->hwmac[0],
 		     iport->hwmac[1], iport->hwmac[2], iport->hwmac[3],
 		     iport->hwmac[4], iport->hwmac[5]);
@@ -81,13 +81,13 @@ void fnic_fcoe_send_vlan_req(struct fnic *fnic)
 
 	iport->fip.state = FDLS_FIP_VLAN_DISCOVERY_STARTED;
 
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "Send VLAN req\n");
 	fnic_send_fip_frame(iport, frame, frame_size);
 
 	vlan_tov = jiffies + msecs_to_jiffies(FCOE_CTLR_FIPVLAN_TOV);
 	mod_timer(&fnic->retry_fip_timer, round_jiffies(vlan_tov));
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "fip timer set\n");
 }
 
@@ -111,11 +111,11 @@ void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct fip_header *fiph)
 	struct fip_vlan_desc *vlan_desc;
 	unsigned long flags;
 
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "fnic 0x%p got vlan resp\n", fnic);
 
 	desc_len = be16_to_cpu(vlan_notif->fip.fip_dl_len);
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "desc_len %d\n", desc_len);
 
 	spin_lock_irqsave(&fnic->vlans_lock, flags);
@@ -128,23 +128,20 @@ void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct fip_header *fiph)
 
 		if (vlan_desc->fd_desc.fip_dtype == FIP_DT_VLAN) {
 			if (vlan_desc->fd_desc.fip_dlen != 1) {
-				FNIC_FIP_DBG(KERN_INFO, fnic->host,
-					     fnic->fnic_num,
+				FNIC_FIP_DBG(KERN_INFO, fnic,
 					     "Invalid descriptor length(%x) in VLan response\n",
 					     vlan_desc->fd_desc.fip_dlen);
 
 			}
 			num_vlan++;
 			vid = be16_to_cpu(vlan_desc->fd_vlan);
-			FNIC_FIP_DBG(KERN_INFO, fnic->host,
-				     fnic->fnic_num,
+			FNIC_FIP_DBG(KERN_INFO, fnic,
 				     "process_vlan_resp: FIP VLAN %d\n", vid);
-			vlan = kzalloc_obj(*vlan);
+			vlan = kzalloc_obj(*vlan, GFP_ATOMIC);
 
 			if (!vlan) {
 				/* retry from timer */
-				FNIC_FIP_DBG(KERN_INFO, fnic->host,
-					     fnic->fnic_num,
+				FNIC_FIP_DBG(KERN_INFO, fnic,
 					     "Mem Alloc failure\n");
 				spin_unlock_irqrestore(&fnic->vlans_lock,
 						       flags);
@@ -155,14 +152,14 @@ void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct fip_header *fiph)
 			list_add_tail(&vlan->list, &fnic->vlan_list);
 			break;
 		}
-		FNIC_FIP_DBG(KERN_INFO, fnic->host,
-			     fnic->fnic_num,
-			     "Invalid descriptor type(%x) in VLan response\n",
-			     vlan_desc->fd_desc.fip_dtype);
 		/*
-		 * Note : received a type=2 descriptor here i.e. FIP
-		 * MAC Address Descriptor
+		 * Note : skip any type=2 descriptor here
+		 * (i.e. FIP MAC Address Descriptor)
 		 */
+		if (vlan_desc->fd_desc.fip_dtype != FIP_DT_MAC)
+			FNIC_FIP_DBG(KERN_INFO, fnic,
+				     "Invalid descriptor type(0x%x) in vlan response\n",
+				     vlan_desc->fd_desc.fip_dtype);
 		cur_desc += vlan_desc->fd_desc.fip_dlen;
 		desc_len -= vlan_desc->fd_desc.fip_dlen;
 	}
@@ -170,7 +167,7 @@ void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct fip_header *fiph)
 	/* any VLAN descriptors present ? */
 	if (num_vlan == 0) {
 		atomic64_inc(&fnic_stats->vlan_stats.resp_withno_vlanID);
-		FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FIP_DBG(KERN_INFO, fnic,
 			     "fnic 0x%p No VLAN descriptors in FIP VLAN response\n",
 			     fnic);
 	}
@@ -195,7 +192,7 @@ void fnic_fcoe_start_fcf_discovery(struct fnic *fnic)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FIP_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to start FCF discovery");
 		return;
 	}
@@ -222,7 +219,7 @@ void fnic_fcoe_start_fcf_discovery(struct fnic *fnic)
 
 	FNIC_STD_SET_NODE_NAME(&pdisc_sol->name_desc.fd_wwn, iport->wwnn);
 
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "Start FCF discovery\n");
 	fnic_send_fip_frame(iport, frame, frame_size);
 
@@ -257,16 +254,14 @@ void fnic_fcoe_fip_discovery_resp(struct fnic *fnic, struct fip_header *fiph)
 	switch (iport->fip.state) {
 	case FDLS_FIP_FCF_DISCOVERY_STARTED:
 		if (be16_to_cpu(disc_adv->fip.fip_flags) & FIP_FL_SOL) {
-			FNIC_FIP_DBG(KERN_INFO, fnic->host,
-				     fnic->fnic_num,
+			FNIC_FIP_DBG(KERN_INFO, fnic,
 				     "fnic 0x%p Solicited adv\n", fnic);
 
 			if ((disc_adv->prio_desc.fd_pri <
 			     iport->selected_fcf.fcf_priority)
 			    && (be16_to_cpu(disc_adv->fip.fip_flags) & FIP_FL_AVAIL)) {
 
-				FNIC_FIP_DBG(KERN_INFO, fnic->host,
-					     fnic->fnic_num,
+				FNIC_FIP_DBG(KERN_INFO, fnic,
 					     "fnic 0x%p FCF Available\n", fnic);
 				memcpy(iport->selected_fcf.fcf_mac,
 				       disc_adv->mac_desc.fd_mac, ETH_ALEN);
@@ -274,8 +269,8 @@ void fnic_fcoe_fip_discovery_resp(struct fnic *fnic, struct fip_header *fiph)
 				    disc_adv->prio_desc.fd_pri;
 				iport->selected_fcf.fka_adv_period =
 				    be32_to_cpu(disc_adv->fka_adv_desc.fd_fka_period);
-				FNIC_FIP_DBG(KERN_INFO, fnic->host,
-					     fnic->fnic_num, "adv time %d",
+				FNIC_FIP_DBG(KERN_INFO, fnic,
+					     "adv time %d",
 					     iport->selected_fcf.fka_adv_period);
 				iport->selected_fcf.ka_disabled =
 				    (disc_adv->fka_adv_desc.fd_flags & 1);
@@ -294,8 +289,7 @@ void fnic_fcoe_fip_discovery_resp(struct fnic *fnic, struct fip_header *fiph)
 					iport->selected_fcf.fka_adv_period =
 					    be32_to_cpu(disc_adv->fka_adv_desc.fd_fka_period);
 					FNIC_FIP_DBG(KERN_INFO,
-						     fnic->host,
-						     fnic->fnic_num,
+						     fnic,
 						     "change fka to %d",
 						     iport->selected_fcf.fka_adv_period);
 				}
@@ -362,7 +356,7 @@ void fnic_fcoe_start_flogi(struct fnic *fnic)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FIP_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to start FIP FLOGI");
 		return;
 	}
@@ -415,7 +409,7 @@ void fnic_fcoe_start_flogi(struct fnic *fnic)
 	oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_FLOGI,
 		&iport->active_oxid_fabric_req);
 	if (oxid == FNIC_UNASSIGNED_OXID) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 		     "Failed to allocate OXID to send FIP FLOGI");
 		mempool_free(frame, fnic->frame_pool);
 		return;
@@ -427,7 +421,7 @@ void fnic_fcoe_start_flogi(struct fnic *fnic)
 	FNIC_STD_SET_NODE_NAME(&pflogi_req->flogi_desc.flogi.els.fl_wwnn,
 			iport->wwnn);
 
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "FIP start FLOGI\n");
 	fnic_send_fip_frame(iport, frame, frame_size);
 	iport->fip.flogi_retry++;
@@ -457,11 +451,11 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph)
 	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
 	struct fc_frame_header *fchdr = &flogi_rsp->rsp_desc.flogi.fchdr;
 
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "fnic 0x%p FIP FLOGI rsp\n", fnic);
 	desc_len = be16_to_cpu(flogi_rsp->fip.fip_dl_len);
 	if (desc_len != 38) {
-		FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FIP_DBG(KERN_INFO, fnic,
 			     "Invalid Descriptor List len (%x). Dropping frame\n",
 			     desc_len);
 		return;
@@ -471,7 +465,7 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph)
 	      && (flogi_rsp->rsp_desc.fd_desc.fip_dlen == 36))
 	    || !((flogi_rsp->mac_desc.fd_desc.fip_dtype == 2)
 		 && (flogi_rsp->mac_desc.fd_desc.fip_dlen == 2))) {
-		FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FIP_DBG(KERN_INFO, fnic,
 			     "Dropping frame invalid type and len mix\n");
 		return;
 	}
@@ -484,7 +478,7 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph)
 	    || (s_id != FC_FID_FLOGI)
 	    || (frame_type != FNIC_FABRIC_FLOGI_RSP)
 	    || (fchdr->fh_type != 0x01)) {
-		FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FIP_DBG(KERN_INFO, fnic,
 			     "Dropping invalid frame: s_id %x F %x R %x t %x OX_ID %x\n",
 			     s_id, fchdr->fh_f_ctl[0], fchdr->fh_r_ctl,
 			     fchdr->fh_type, FNIC_STD_GET_OX_ID(fchdr));
@@ -492,7 +486,7 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph)
 	}
 
 	if (iport->fip.state == FDLS_FIP_FLOGI_STARTED) {
-		FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FIP_DBG(KERN_INFO, fnic,
 			     "fnic 0x%p rsp for pending FLOGI\n", fnic);
 
 		oxid = FNIC_STD_GET_OX_ID(fchdr);
@@ -502,8 +496,7 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph)
 		if ((be16_to_cpu(flogi_rsp->fip.fip_dl_len) == FIP_FLOGI_LEN)
 		    && (flogi_rsp->rsp_desc.flogi.els.fl_cmd == ELS_LS_ACC)) {
 
-			FNIC_FIP_DBG(KERN_INFO, fnic->host,
-				     fnic->fnic_num,
+			FNIC_FIP_DBG(KERN_INFO, fnic,
 				     "fnic 0x%p FLOGI success\n", fnic);
 			memcpy(iport->fpma, flogi_rsp->mac_desc.fd_mac, ETH_ALEN);
 			iport->fcid =
@@ -519,8 +512,7 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph)
 
 			if (fnic_fdls_register_portid(iport, iport->fcid, NULL)
 			    != 0) {
-				FNIC_FIP_DBG(KERN_INFO, fnic->host,
-					     fnic->fnic_num,
+				FNIC_FIP_DBG(KERN_INFO, fnic,
 					     "fnic 0x%p flogi registration failed\n",
 					     fnic);
 				return;
@@ -528,8 +520,8 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph)
 
 			iport->fip.state = FDLS_FIP_FLOGI_COMPLETE;
 			iport->state = FNIC_IPORT_STATE_FABRIC_DISC;
-			FNIC_FIP_DBG(KERN_INFO, fnic->host,
-				     fnic->fnic_num, "iport->state:%d\n",
+			FNIC_FIP_DBG(KERN_INFO, fnic,
+				     "iport->state:%d\n",
 				     iport->state);
 			fnic_fdls_disc_start(iport);
 			if (!((iport->selected_fcf.ka_disabled)
@@ -575,7 +567,7 @@ void fnic_common_fip_cleanup(struct fnic *fnic)
 
 	if (!iport->usefip)
 		return;
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "fnic 0x%p fip cleanup\n", fnic);
 
 	iport->fip.state = FDLS_FIP_INIT;
@@ -617,7 +609,7 @@ void fnic_fcoe_process_cvl(struct fnic *fnic, struct fip_header *fiph)
 	int found = false;
 	int max_count = 0;
 
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "fnic 0x%p clear virtual link handler\n", fnic);
 
 	if (!((cvl_msg->fcf_mac_desc.fd_desc.fip_dtype == 2)
@@ -625,7 +617,7 @@ void fnic_fcoe_process_cvl(struct fnic *fnic, struct fip_header *fiph)
 	    || !((cvl_msg->name_desc.fd_desc.fip_dtype == 4)
 		 && (cvl_msg->name_desc.fd_desc.fip_dlen == 3))) {
 
-		FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FIP_DBG(KERN_INFO, fnic,
 			     "invalid mix: ft %x fl %x ndt %x ndl %x",
 			     cvl_msg->fcf_mac_desc.fd_desc.fip_dtype,
 			     cvl_msg->fcf_mac_desc.fd_desc.fip_dlen,
@@ -640,8 +632,7 @@ void fnic_fcoe_process_cvl(struct fnic *fnic, struct fip_header *fiph)
 			if (!((cvl_msg->vn_ports_desc[i].fd_desc.fip_dtype == 11)
 			      && (cvl_msg->vn_ports_desc[i].fd_desc.fip_dlen == 5))) {
 
-				FNIC_FIP_DBG(KERN_INFO, fnic->host,
-					     fnic->fnic_num,
+				FNIC_FIP_DBG(KERN_INFO, fnic,
 					     "Invalid type and len mix type: %d len: %d\n",
 					     cvl_msg->vn_ports_desc[i].fd_desc.fip_dtype,
 					     cvl_msg->vn_ports_desc[i].fd_desc.fip_dlen);
@@ -664,12 +655,12 @@ void fnic_fcoe_process_cvl(struct fnic *fnic, struct fip_header *fiph)
 			spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
 			max_count++;
 			if (max_count >= FIP_FNIC_RESET_WAIT_COUNT) {
-				FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FIP_DBG(KERN_INFO, fnic,
 					 "Rthr waited too long. Skipping handle link event %p\n",
 					 fnic);
 				return;
 			}
-			FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FIP_DBG(KERN_INFO, fnic,
 				 "fnic reset in progress. Link event needs to wait %p",
 				 fnic);
 		}
@@ -714,7 +705,7 @@ int fdls_fip_recv_frame(struct fnic *fnic, void *frame)
 		return true;
 	}
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		"Not a FIP Frame");
 	return false;
 }
@@ -724,7 +715,7 @@ void fnic_work_on_fip_timer(struct work_struct *work)
 	struct fnic *fnic = container_of(work, struct fnic, fip_timer_work);
 	struct fnic_iport_s *iport = &fnic->iport;
 
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "FIP timeout\n");
 
 	if (iport->fip.state == FDLS_FIP_VLAN_DISCOVERY_STARTED) {
@@ -732,7 +723,7 @@ void fnic_work_on_fip_timer(struct work_struct *work)
 	} else if (iport->fip.state == FDLS_FIP_FCF_DISCOVERY_STARTED) {
 		u8 zmac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
 
-		FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FIP_DBG(KERN_INFO, fnic,
 			     "FCF Discovery timeout\n");
 		if (memcmp(iport->selected_fcf.fcf_mac, zmac, ETH_ALEN) != 0) {
 
@@ -754,13 +745,13 @@ void fnic_work_on_fip_timer(struct work_struct *work)
 					  round_jiffies(fcf_tov));
 			}
 		} else {
-			FNIC_FIP_DBG(KERN_INFO, fnic->host,
-				     fnic->fnic_num, "FCF Discovery timeout\n");
+			FNIC_FIP_DBG(KERN_INFO, fnic,
+				     "FCF Discovery timeout\n");
 			fnic_vlan_discovery_timeout(fnic);
 		}
 	} else if (iport->fip.state == FDLS_FIP_FLOGI_STARTED) {
 		fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req);
-		FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FIP_DBG(KERN_INFO, fnic,
 			     "FLOGI timeout\n");
 		if (iport->fip.flogi_retry < fnic->config.flogi_retries)
 			fnic_fcoe_start_flogi(fnic);
@@ -807,7 +798,7 @@ void fnic_handle_enode_ka_timer(struct timer_list *t)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FIP_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send enode ka");
 		return;
 	}
@@ -828,7 +819,7 @@ void fnic_handle_enode_ka_timer(struct timer_list *t)
 	memcpy(penode_ka->eth.h_dest, iport->selected_fcf.fcf_mac, ETH_ALEN);
 	memcpy(penode_ka->mac_desc.fd_mac, iport->hwmac, ETH_ALEN);
 
-	FNIC_FIP_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_DEBUG, fnic,
 		     "Handle enode KA timer\n");
 	fnic_send_fip_frame(iport, frame, frame_size);
 	enode_ka_tov = jiffies
@@ -861,7 +852,7 @@ void fnic_handle_vn_ka_timer(struct timer_list *t)
 
 	frame = fdls_alloc_frame(iport);
 	if (frame == NULL) {
-		FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_FIP_DBG(KERN_ERR, fnic,
 		     "Failed to allocate frame to send vn ka");
 		return;
 	}
@@ -887,7 +878,7 @@ void fnic_handle_vn_ka_timer(struct timer_list *t)
 	memcpy(pvn_port_ka->vn_port_desc.fd_fc_id, fcid, 3);
 	FNIC_STD_SET_NPORT_NAME(&pvn_port_ka->vn_port_desc.fd_wwpn, iport->wwpn);
 
-	FNIC_FIP_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_DEBUG, fnic,
 		     "Handle vnport KA timer\n");
 	fnic_send_fip_frame(iport, frame, frame_size);
 	vn_ka_tov = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
@@ -977,7 +968,7 @@ void fnic_work_on_fcs_ka_timer(struct work_struct *work)
 	*fnic = container_of(work, struct fnic, fip_timer_work);
 	struct fnic_iport_s *iport = &fnic->iport;
 
-	FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FIP_DBG(KERN_INFO, fnic,
 		     "fnic 0x%p fcs ka timeout\n", fnic);
 
 	fnic_common_fip_cleanup(fnic);
diff --git a/drivers/scsi/fnic/fip.h b/drivers/scsi/fnic/fip.h
index 79fee7628870..4866152a32df 100644
--- a/drivers/scsi/fnic/fip.h
+++ b/drivers/scsi/fnic/fip.h
@@ -142,7 +142,7 @@ fnic_debug_dump_fip_frame(struct fnic *fnic, struct ethhdr *eth,
 	u16 op = be16_to_cpu(fiph->fip_op);
 	u8 sub = fiph->fip_subcode;
 
-	FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_DEBUG, fnic,
 		"FIP %s packet contents: op: 0x%x sub: 0x%x (len = %d)",
 		pfx, op, sub, len);
 
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index 8724d64f2525..c576a7f5083e 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -27,10 +27,11 @@
 #include "vnic_stats.h"
 #include "vnic_scsi.h"
 #include "fnic_fdls.h"
+#include "fnic_nvme.h"
 
 #define DRV_NAME		"fnic"
 #define DRV_DESCRIPTION		"Cisco FCoE HBA Driver"
-#define DRV_VERSION		"1.8.0.3"
+#define DRV_VERSION		"1.9.0.0"
 #define PFX			DRV_NAME ": "
 #define DFX                     DRV_NAME "%d: "
 
@@ -43,6 +44,7 @@
 #define FNIC_DFLT_QUEUE_DEPTH	256
 #define	FNIC_STATS_RATE_LIMIT	4 /* limit rate at which stats are pulled up */
 #define LUN0_DELAY_TIME			9
+#define FNIC_ROLE_CONFIG_MASK   (0xFF0)
 
 /*
  * Tag bits used for special requests.
@@ -79,6 +81,8 @@
 #define FNIC_DEV_RST_ABTS_DONE          BIT(19)
 #define FNIC_DEV_RST_TERM_DONE          BIT(20)
 #define FNIC_DEV_RST_ABTS_PENDING       BIT(21)
+#define FNIC_NVME_ADMIN_IO_TIMER_PENDING BIT(22)
+#define FNIC_NVME_ADMIN_IO              BIT(23)
 
 #define FNIC_FW_RESET_TIMEOUT        60000	/* mSec   */
 #define FNIC_FCOE_MAX_CMD_LEN        16
@@ -144,6 +148,9 @@
 #define PCI_SUBDEVICE_ID_CISCO_HELSINKI         0x02e4	/* VIC 15235 */
 #define PCI_SUBDEVICE_ID_CISCO_GOTHENBURG       0x02f2	/* VIC 15425 */
 
+#define IS_FNIC_FCP_INITIATOR(fnic) (fnic->role == FNIC_ROLE_FCP_INITIATOR)
+#define IS_FNIC_NVME_INITIATOR(fnic) (fnic->role == FNIC_ROLE_NVME_INITIATOR)
+
 struct fnic_pcie_device {
 	u32 device;
 	u8 *desc;
@@ -234,12 +241,15 @@ extern spinlock_t reset_fnic_list_lock;
 extern struct list_head reset_fnic_list;
 extern struct workqueue_struct *reset_fnic_work_queue;
 extern struct work_struct reset_fnic_work;
-
+extern struct workqueue_struct *fnic_cmpl_queue;
 
 #define FNIC_MAIN_LOGGING 0x01
 #define FNIC_FCS_LOGGING 0x02
 #define FNIC_SCSI_LOGGING 0x04
 #define FNIC_ISR_LOGGING 0x08
+#define FNIC_FDLS_LOGGING 0x10
+#define FNIC_NVME_LOGGING 0x20
+#define FNIC_FIP_LOGGING 0x40
 
 #define FNIC_CHECK_LOGGING(LEVEL, CMD)				\
 do {								\
@@ -249,38 +259,39 @@ do {								\
 		} while (0);					\
 } while (0)
 
-#define FNIC_MAIN_DBG(kern_level, host, fnic_num, fmt, args...)		\
-	FNIC_CHECK_LOGGING(FNIC_MAIN_LOGGING,			\
-			 shost_printk(kern_level, host,			\
-				"fnic<%d>: %s: %d: " fmt, fnic_num,\
-				__func__, __LINE__, ##args);)
-
-#define FNIC_FCS_DBG(kern_level, host, fnic_num, fmt, args...)		\
-	FNIC_CHECK_LOGGING(FNIC_FCS_LOGGING,			\
-			 shost_printk(kern_level, host,			\
-				"fnic<%d>: %s: %d: " fmt, fnic_num,\
-				__func__, __LINE__, ##args);)
-
-#define FNIC_FIP_DBG(kern_level, host, fnic_num, fmt, args...)		\
-	FNIC_CHECK_LOGGING(FNIC_FCS_LOGGING,			\
-			 shost_printk(kern_level, host,			\
-				"fnic<%d>: %s: %d: " fmt, fnic_num,\
-				__func__, __LINE__, ##args);)
-
-#define FNIC_SCSI_DBG(kern_level, host, fnic_num, fmt, args...)		\
-	FNIC_CHECK_LOGGING(FNIC_SCSI_LOGGING,			\
-			 shost_printk(kern_level, host,			\
-				"fnic<%d>: %s: %d: " fmt, fnic_num,\
-				__func__, __LINE__, ##args);)
-
-#define FNIC_ISR_DBG(kern_level, host, fnic_num, fmt, args...)		\
-	FNIC_CHECK_LOGGING(FNIC_ISR_LOGGING,			\
-			 shost_printk(kern_level, host,			\
-				"fnic<%d>: %s: %d: " fmt, fnic_num,\
-				__func__, __LINE__, ##args);)
-
-#define FNIC_MAIN_NOTE(kern_level, host, fmt, args...)          \
-	shost_printk(kern_level, host, fmt, ##args)
+#define fnic_printk(kern_level, fnic, fmt, ...)                      \
+	(IS_FNIC_FCP_INITIATOR(fnic) ?                                   \
+	shost_printk(kern_level, fnic->host, "fnic<%d>: %s: %d: " fmt,   \
+				fnic->fnic_num, __func__, __LINE__, ##__VA_ARGS__) : \
+	printk(kern_level "fnic<%d>: %s: %d: " fmt, fnic->fnic_num,      \
+				__func__, __LINE__, ##__VA_ARGS__))
+
+#define FNIC_MAIN_DBG(kern_level, fnic, fmt, args...)				\
+		FNIC_CHECK_LOGGING(FNIC_MAIN_LOGGING,						\
+				fnic_printk(kern_level, fnic, fmt, ##args);)
+
+#define FNIC_FCS_DBG(kern_level, fnic, fmt, args...)				\
+		FNIC_CHECK_LOGGING(FNIC_FCS_LOGGING,						\
+				fnic_printk(kern_level, fnic, fmt, ##args);)
+
+#define FNIC_FIP_DBG(kern_level, fnic, fmt, args...)				\
+		FNIC_CHECK_LOGGING(FNIC_FIP_LOGGING,						\
+				fnic_printk(kern_level, fnic, fmt, ##args);)
+
+#define FNIC_SCSI_DBG(kern_level, fnic, fmt, args...)				\
+		FNIC_CHECK_LOGGING(FNIC_SCSI_LOGGING,						\
+				fnic_printk(kern_level, fnic, fmt, ##args);)
+
+#define FNIC_ISR_DBG(kern_level, fnic, fmt, args...)				\
+		FNIC_CHECK_LOGGING(FNIC_ISR_LOGGING,						\
+				fnic_printk(kern_level, fnic, fmt, ##args);)
+
+#define FNIC_NVME_DBG(kern_level, fnic, fmt, args...)				\
+		FNIC_CHECK_LOGGING(FNIC_NVME_LOGGING,						\
+				fnic_printk(kern_level, fnic, fmt, ##args);)
+
+#define FNIC_MAIN_NOTE(kern_level, fnic, fmt, args...)				\
+			fnic_printk(kern_level, fnic, fmt, ##args)
 
 #define FNIC_WQ_COPY_MAX 64
 #define FNIC_WQ_MAX 1
@@ -325,6 +336,7 @@ enum fnic_state {
 
 enum fnic_role_e {
 	FNIC_ROLE_FCP_INITIATOR = 0,
+	FNIC_ROLE_NVME_INITIATOR,
 };
 
 enum fnic_evt {
@@ -343,6 +355,11 @@ struct fnic_frame_list {
 	int rx_ethhdr_stripped;
 };
 
+struct fnic_tag_t {
+	struct list_head free_list;
+	int tag_id;
+};
+
 struct fnic_event {
 	struct list_head list;
 	struct fnic *fnic;
@@ -460,6 +477,15 @@ struct fnic {
 	struct list_head vlan_list;
 	/*** FIP related data members  -- end ***/
 
+	/* NVME data members */
+	struct dentry *fnic_nvmef_debugfs_host;
+	struct dentry *fnic_nvmef_debugfs_file;
+	struct sbitmap nvfnic_tag_map;
+	struct work_struct nvme_io_cmpl_work;
+	atomic_t nvme_io_event_queued;
+	struct llist_head nvme_io_event_llist;
+	struct completion *nvme_lport_unreg_done;
+
 	/* copy work queue cache line section */
 	____cacheline_aligned struct vnic_wq_copy hw_copy_wq[FNIC_WQ_COPY_MAX];
 	____cacheline_aligned struct fnic_cpy_wq sw_copy_wq[FNIC_WQ_COPY_MAX];
@@ -513,6 +539,7 @@ int fnic_host_reset(struct Scsi_Host *shost);
 void fnic_reset(struct Scsi_Host *shost);
 int fnic_issue_fc_host_lip(struct Scsi_Host *shost);
 void fnic_get_host_port_state(struct Scsi_Host *shost);
+void fnic_fcpio_reset(struct fnic *fnic);
 int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int copy_work_to_do, unsigned int cq_index);
 int fnic_wq_cmpl_handler(struct fnic *fnic, int);
 int fnic_flogi_reg_handler(struct fnic *fnic, u32);
@@ -526,6 +553,9 @@ void fnic_log_q_error(struct fnic *fnic);
 void fnic_handle_link_event(struct fnic *fnic);
 int fnic_stats_debugfs_init(struct fnic *fnic);
 void fnic_stats_debugfs_remove(struct fnic *fnic);
+void fnic_nvmef_debugfs_init(struct fnic *fnic);
+void fnic_nvmef_debugfs_remove(struct fnic *fnic);
+int nvfnic_get_nvmef_info(struct fnic *fnic, struct fnic_nvmef_info *info);
 int fnic_is_abts_pending(struct fnic *, struct scsi_cmnd *);
 
 void fnic_handle_fip_frame(struct work_struct *work);
@@ -559,6 +589,8 @@ void fnic_scsi_unload(struct fnic *fnic);
 void fnic_scsi_unload_cleanup(struct fnic *fnic);
 int fnic_get_debug_info(struct stats_debug_info *info,
 			struct fnic *fnic);
+int free_wq_copy_descs(struct fnic *fnic, struct vnic_wq_copy *wq,
+			unsigned int hwq);
 
 struct fnic_scsi_iter_data {
 	struct fnic *fnic;
@@ -598,7 +630,7 @@ fnic_debug_dump(struct fnic *fnic, uint8_t *u8arr, int len)
 	int i;
 
 	for (i = 0; i < len; i = i+8) {
-		FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 		    "%d: %02x %02x %02x %02x %02x %02x %02x %02x", i / 8,
 		    u8arr[i + 0], u8arr[i + 1], u8arr[i + 2], u8arr[i + 3],
 		    u8arr[i + 4], u8arr[i + 5], u8arr[i + 6], u8arr[i + 7]);
@@ -613,7 +645,7 @@ fnic_debug_dump_fc_frame(struct fnic *fnic, struct fc_frame_header *fchdr,
 
 	s_id = ntoh24(fchdr->fh_s_id);
 	d_id = ntoh24(fchdr->fh_d_id);
-	FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 		"%s packet contents: sid/did/type/oxid = 0x%x/0x%x/0x%x/0x%x (len = %d)\n",
 		pfx, s_id, d_id, fchdr->fh_type,
 		FNIC_STD_GET_OX_ID(fchdr), len);
diff --git a/drivers/scsi/fnic/fnic_debugfs.c b/drivers/scsi/fnic/fnic_debugfs.c
index ba86964fb45e..61f167e20574 100644
--- a/drivers/scsi/fnic/fnic_debugfs.c
+++ b/drivers/scsi/fnic/fnic_debugfs.c
@@ -10,10 +10,19 @@
 extern int fnic_get_debug_info(struct stats_debug_info *debug_buffer,
 							   struct fnic *fnic);
 
+static int fnic_nvmef_debugfs_open(struct inode *inode,
+			struct file *file);
+static ssize_t fnic_nvmef_debugfs_read(struct file *file,
+			char __user *ubuf,
+			size_t nbytes, loff_t *pos);
+static int fnic_nvmef_debugfs_release(struct inode *inode,
+			struct file *file);
+
 static struct dentry *fnic_trace_debugfs_root;
 static struct dentry *fnic_trace_debugfs_file;
 static struct dentry *fnic_trace_enable;
 static struct dentry *fnic_stats_debugfs_root;
+static struct dentry *fnic_nvmef_debugfs_root;
 
 static struct dentry *fnic_fc_trace_debugfs_file;
 static struct dentry *fnic_fc_rdata_trace_debugfs_file;
@@ -46,6 +55,9 @@ int fnic_debugfs_init(void)
 	fnic_stats_debugfs_root = debugfs_create_dir("statistics",
 						fnic_trace_debugfs_root);
 
+	fnic_nvmef_debugfs_root = debugfs_create_dir("nvme_info",
+						     fnic_trace_debugfs_root);
+
 	/* Allocate memory to structure */
 	fc_trc_flag = vmalloc(sizeof(struct fc_trace_flag_type));
 
@@ -70,6 +82,9 @@ int fnic_debugfs_init(void)
  */
 void fnic_debugfs_terminate(void)
 {
+	debugfs_remove(fnic_nvmef_debugfs_root);
+	fnic_nvmef_debugfs_root = NULL;
+
 	debugfs_remove(fnic_stats_debugfs_root);
 	fnic_stats_debugfs_root = NULL;
 
@@ -669,6 +684,13 @@ static const struct file_operations fnic_reset_debugfs_fops = {
 	.release = fnic_reset_stats_release,
 };
 
+static const struct file_operations fnic_nvmef_debugfs_fops = {
+	.owner = THIS_MODULE,
+	.open = fnic_nvmef_debugfs_open,
+	.read = fnic_nvmef_debugfs_read,
+	.release = fnic_nvmef_debugfs_release,
+};
+
 /*
  * fnic_stats_init - Initialize stats struct and create stats file per fnic
  *
@@ -681,7 +703,10 @@ int fnic_stats_debugfs_init(struct fnic *fnic)
 {
 	char name[16];
 
-	snprintf(name, sizeof(name), "host%d", fnic->host->host_no);
+	if (IS_FNIC_FCP_INITIATOR(fnic))
+		snprintf(name, sizeof(name), "host%d", fnic->host->host_no);
+	else if (IS_FNIC_NVME_INITIATOR(fnic))
+		snprintf(name, sizeof(name), "nvfnic%d", fnic->fnic_num);
 
 	fnic->fnic_stats_debugfs_host = debugfs_create_dir(name,
 						fnic_stats_debugfs_root);
@@ -719,3 +744,75 @@ void fnic_stats_debugfs_remove(struct fnic *fnic)
 	debugfs_remove(fnic->fnic_stats_debugfs_host);
 	fnic->fnic_stats_debugfs_host = NULL;
 }
+
+void fnic_nvmef_debugfs_init(struct fnic *fnic)
+{
+	char name[16];
+
+	snprintf(name, sizeof(name), "host%d", fnic->fnic_num);
+
+	fnic->fnic_nvmef_debugfs_host = debugfs_create_dir(name,
+							   fnic_nvmef_debugfs_root);
+	fnic->fnic_nvmef_debugfs_file = debugfs_create_file("nvmef_info",
+							    S_IFREG | 0444,
+							    fnic->fnic_nvmef_debugfs_host,
+							    fnic,
+							    &fnic_nvmef_debugfs_fops);
+}
+
+static int fnic_nvmef_debugfs_open(struct inode *inode, struct file *file)
+{
+
+	struct fnic *fnic = inode->i_private;
+	struct fnic_nvmef_info *info;
+	int buf_size = 2 * PAGE_SIZE;
+
+	info = kzalloc_obj(struct fnic_nvmef_info, GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->info_buffer = vmalloc(buf_size);
+	if (!info->info_buffer) {
+		kfree(info);
+		return -ENOMEM;
+	}
+
+	info->buf_size = buf_size;
+	memset((void *)info->info_buffer, 0, buf_size);
+	info->buffer_len = nvfnic_get_nvmef_info(fnic, info);
+
+	file->private_data = info;
+
+	return 0;
+}
+
+static ssize_t fnic_nvmef_debugfs_read(struct file *file,
+				       char __user *ubuf,
+				       size_t nbytes, loff_t *pos)
+{
+	struct fnic_nvmef_info *info = file->private_data;
+
+	return simple_read_from_buffer(ubuf, nbytes, pos,
+				     info->info_buffer, info->buffer_len);
+}
+
+static int fnic_nvmef_debugfs_release(struct inode *inode, struct file *file)
+{
+	struct fnic_nvmef_info *info = file->private_data;
+
+	vfree(info->info_buffer);
+	kfree(info);
+	return 0;
+}
+
+void fnic_nvmef_debugfs_remove(struct fnic *fnic)
+{
+	if (!fnic)
+		return;
+
+	debugfs_remove(fnic->fnic_nvmef_debugfs_file);
+	fnic->fnic_nvmef_debugfs_file = NULL;
+
+	debugfs_remove(fnic->fnic_nvmef_debugfs_host);
+	fnic->fnic_nvmef_debugfs_host = NULL;
+}
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
index 063eb864a5cd..b00672ef8b00 100644
--- a/drivers/scsi/fnic/fnic_fcs.c
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -31,6 +31,11 @@ struct workqueue_struct *fnic_event_queue;
 
 static uint8_t FCOE_ALL_FCF_MAC[6] = FC_FCOE_FLOGI_MAC;
 
+static inline bool fnic_is_nvme_frame(struct fc_frame_header *fchdr)
+{
+	return (fchdr->fh_type == FC_TYPE_NVME);
+}
+
 /*
  * Internal Functions
  * This function will initialize the src_mac address to be
@@ -39,7 +44,7 @@ static uint8_t FCOE_ALL_FCF_MAC[6] = FC_FCOE_FLOGI_MAC;
 static inline void fnic_fdls_set_fcoe_srcmac(struct fnic *fnic,
 							 uint8_t *src_mac)
 {
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Setting src mac: %02x:%02x:%02x:%02x:%02x:%02x",
 				 src_mac[0], src_mac[1], src_mac[2], src_mac[3],
 				 src_mac[4], src_mac[5]);
@@ -54,7 +59,7 @@ static inline void fnic_fdls_set_fcoe_srcmac(struct fnic *fnic,
 static inline  void fnic_fdls_set_fcoe_dstmac(struct fnic *fnic,
 							 uint8_t *dst_mac)
 {
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Setting dst mac: %02x:%02x:%02x:%02x:%02x:%02x",
 				 dst_mac[0], dst_mac[1], dst_mac[2], dst_mac[3],
 				 dst_mac[4], dst_mac[5]);
@@ -82,7 +87,7 @@ void fnic_fdls_link_status_change(struct fnic *fnic, int linkup)
 {
 	struct fnic_iport_s *iport = &fnic->iport;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "link up: %d, usefip: %d", linkup, iport->usefip);
 
 	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
@@ -90,12 +95,12 @@ void fnic_fdls_link_status_change(struct fnic *fnic, int linkup)
 	if (linkup) {
 		if (iport->usefip) {
 			iport->state = FNIC_IPORT_STATE_FIP;
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "link up: %d, usefip: %d", linkup, iport->usefip);
 			fnic_fcoe_send_vlan_req(fnic);
 		} else {
 			iport->state = FNIC_IPORT_STATE_FABRIC_DISC;
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "iport->state: %d", iport->state);
 			fnic_fdls_disc_start(iport);
 		}
@@ -125,13 +130,13 @@ void fnic_fdls_learn_fcoe_macs(struct fnic_iport_s *iport, void *rx_frame,
 
 	memcpy(&fcmac[3], fcid, 3);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "learn fcoe: dst_mac: %02x:%02x:%02x:%02x:%02x:%02x",
 				 ethhdr->h_dest[0], ethhdr->h_dest[1],
 				 ethhdr->h_dest[2], ethhdr->h_dest[3],
 				 ethhdr->h_dest[4], ethhdr->h_dest[5]);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "learn fcoe: fc_mac: %02x:%02x:%02x:%02x:%02x:%02x",
 				 fcmac[0], fcmac[1], fcmac[2], fcmac[3], fcmac[4],
 				 fcmac[5]);
@@ -149,13 +154,12 @@ void fnic_fdls_init(struct fnic *fnic, int usefip)
 	iport->fnic = fnic;
 	iport->usefip = usefip;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "iportsrcmac: %02x:%02x:%02x:%02x:%02x:%02x",
 				 iport->hwmac[0], iport->hwmac[1], iport->hwmac[2],
 				 iport->hwmac[3], iport->hwmac[4], iport->hwmac[5]);
 
 	INIT_LIST_HEAD(&iport->tport_list);
-	INIT_LIST_HEAD(&iport->tport_list_pending_del);
 
 	fnic_fdls_disc_init(iport);
 }
@@ -168,14 +172,14 @@ void fnic_handle_link(struct work_struct *work)
 	int max_count = 0;
 
 	if (vnic_dev_get_intr_mode(fnic->vdev) != VNIC_DEV_INTR_MODE_MSI)
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Interrupt mode is not MSI\n");
 
 	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
 
 	if (fnic->stop_rx_link_events) {
 		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Stop link rx events\n");
 		return;
 	}
@@ -184,10 +188,10 @@ void fnic_handle_link(struct work_struct *work)
 	if ((fnic->state != FNIC_IN_ETH_MODE)
 		&& (fnic->state != FNIC_IN_FC_MODE)) {
 		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "fnic in transitional state: %d. link up: %d ignored",
 			 fnic->state, vnic_dev_link_status(fnic->vdev));
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Current link status: %d iport state: %d\n",
 			 fnic->link_status, fnic->iport.state);
 		return;
@@ -199,36 +203,36 @@ void fnic_handle_link(struct work_struct *work)
 	fnic->link_down_cnt = vnic_dev_link_down_cnt(fnic->vdev);
 
 	while (fnic->reset_in_progress == IN_PROGRESS) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "fnic reset in progress. Link event needs to wait\n");
 
 		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "waiting for reset completion\n");
 		wait_for_completion_timeout(&fnic->reset_completion_wait,
 									msecs_to_jiffies(5000));
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "woken up from reset completion wait\n");
 		spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
 
 		max_count++;
 		if (max_count >= MAX_RESET_WAIT_COUNT) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Rstth waited for too long. Skipping handle link event\n");
 			spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
 			return;
 		}
 	}
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Marking fnic reset in progress\n");
 	fnic->reset_in_progress = IN_PROGRESS;
 
 	if ((vnic_dev_get_intr_mode(fnic->vdev) != VNIC_DEV_INTR_MODE_MSI) ||
 		(fnic->link_status != old_link_status)) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "old link status: %d link status: %d\n",
 					 old_link_status, (int) fnic->link_status);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "old down count %d down count: %d\n",
 					 old_link_down_cnt, (int) fnic->link_down_cnt);
 	}
@@ -237,36 +241,36 @@ void fnic_handle_link(struct work_struct *work)
 		if (!fnic->link_status) {
 			/* DOWN -> DOWN */
 			spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "down->down\n");
 		} else {
 			if (old_link_down_cnt != fnic->link_down_cnt) {
 				/* UP -> DOWN -> UP */
 				spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "up->down. Link down\n");
 				fnic_fdls_link_status_change(fnic, 0);
 
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "down->up. Link up\n");
 				fnic_fdls_link_status_change(fnic, 1);
 			} else {
 				/* UP -> UP */
 				spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "up->up\n");
 			}
 		}
 	} else if (fnic->link_status) {
 		/* DOWN -> UP */
 		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "down->up. Link up\n");
 		fnic_fdls_link_status_change(fnic, 1);
 	} else {
 		/* UP -> DOWN */
 		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "up->down. Link down\n");
 		fnic_fdls_link_status_change(fnic, 0);
 	}
@@ -275,7 +279,7 @@ void fnic_handle_link(struct work_struct *work)
 	fnic->reset_in_progress = NOT_IN_PROGRESS;
 	complete(&fnic->reset_completion_wait);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Marking fnic reset completion\n");
 	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
 }
@@ -285,6 +289,7 @@ void fnic_handle_frame(struct work_struct *work)
 	struct fnic *fnic = container_of(work, struct fnic, frame_work);
 	struct fnic_frame_list *cur_frame, *next;
 	int fchdr_offset = 0;
+	struct fc_frame_header *fchdr;
 
 	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
 	list_for_each_entry_safe(cur_frame, next, &fnic->frame_queue, links) {
@@ -302,7 +307,7 @@ void fnic_handle_frame(struct work_struct *work)
 		 */
 		if (fnic->state != FNIC_IN_FC_MODE &&
 			fnic->state != FNIC_IN_ETH_MODE) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Cannot process frame in transitional state\n");
 			spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
 			return;
@@ -314,8 +319,14 @@ void fnic_handle_frame(struct work_struct *work)
 		fchdr_offset = (cur_frame->rx_ethhdr_stripped) ?
 			0 : FNIC_ETH_FCOE_HDRS_OFFSET;
 
-		fnic_fdls_recv_frame(&fnic->iport, cur_frame->fp,
-							 cur_frame->frame_len, fchdr_offset);
+		fchdr = (struct fc_frame_header *)((u8 *)cur_frame->fp + fchdr_offset);
+		if (IS_FNIC_NVME_INITIATOR(fnic) && fnic_is_nvme_frame(fchdr)) {
+			nvfnic_ls_rsp_recv(&fnic->iport, fchdr,
+					  cur_frame->frame_len - fchdr_offset);
+		} else {
+			fnic_fdls_recv_frame(&fnic->iport, cur_frame->fp,
+					     cur_frame->frame_len, fchdr_offset);
+		}
 
 		mempool_free(cur_frame->fp, fnic->frame_recv_pool);
 		mempool_free(cur_frame, fnic->frame_elem_pool);
@@ -328,7 +339,7 @@ void fnic_handle_fip_frame(struct work_struct *work)
 	struct fnic_frame_list *cur_frame, *next;
 	struct fnic *fnic = container_of(work, struct fnic, fip_frame_work);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Processing FIP frame\n");
 
 	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
@@ -407,7 +418,7 @@ void fnic_update_mac_locked(struct fnic *fnic, u8 *new)
 	if (ether_addr_equal(data, new))
 		return;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Update MAC: %u\n", *new);
 
 	if (!is_zero_ether_addr(data) && !ether_addr_equal(data, ctl))
@@ -477,7 +488,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
 
 		if (!fcs_ok) {
 			atomic64_inc(&fnic_stats->misc_stats.frame_errors);
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "fnic 0x%p fcs error.  Dropping packet.\n", fnic);
 			goto drop;
 		}
@@ -487,21 +498,21 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
 			if (fnic_import_rq_eth_pkt(fnic, fp))
 				return;
 
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "Dropping h_proto 0x%x",
 							 be16_to_cpu(eh->h_proto));
 			goto drop;
 		}
 	} else {
 		/* wrong CQ type */
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "fnic rq_cmpl wrong cq type x%x\n", type);
 		goto drop;
 	}
 
 	if (!fcs_ok || packet_error || !fcoe_fnic_crc_ok || fcoe_enc_error) {
 		atomic64_inc(&fnic_stats->misc_stats.frame_errors);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "fcoe %x fcsok %x pkterr %x ffco %x fee %x\n",
 			 fcoe, fcs_ok, packet_error,
 			 fcoe_fnic_crc_ok, fcoe_enc_error);
@@ -511,7 +522,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
 	spin_lock_irqsave(&fnic->fnic_lock, flags);
 	if (fnic->stop_rx_link_events) {
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "fnic->stop_rx_link_events: %d\n",
 					 fnic->stop_rx_link_events);
 		goto drop;
@@ -521,7 +532,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
 
 	frame_elem = mempool_alloc(fnic->frame_elem_pool, GFP_ATOMIC);
 	if (!frame_elem) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Failed to allocate memory for frame elem");
 		goto drop;
 	}
@@ -567,7 +578,7 @@ int fnic_rq_cmpl_handler(struct fnic *fnic, int rq_work_to_do)
 		if (cur_work_done && fnic->stop_rx_link_events != 1) {
 			err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame);
 			if (err)
-				shost_printk(KERN_ERR, fnic->host,
+				fnic_printk(KERN_ERR, fnic,
 					     "fnic_alloc_rq_frame can't alloc"
 					     " frame\n");
 		}
@@ -593,7 +604,7 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq)
 	len = FNIC_FRAME_HT_ROOM;
 	buf = mempool_alloc(fnic->frame_recv_pool, GFP_ATOMIC);
 	if (!buf) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Unable to allocate RQ buffer of size: %d\n", len);
 		return -ENOMEM;
 	}
@@ -601,7 +612,7 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq)
 	pa = dma_map_single(&fnic->pdev->dev, buf, len, DMA_FROM_DEVICE);
 	if (dma_mapping_error(&fnic->pdev->dev, pa)) {
 		ret = -ENOMEM;
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "PCI mapping failed with error %d\n", ret);
 		goto free_buf;
 	}
@@ -615,9 +626,13 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq)
 
 void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf)
 {
-	void *rq_buf = buf->os_buf;
+	void *rq_buf;
 	struct fnic *fnic = vnic_dev_priv(rq->vdev);
 
+	if (WARN_ON(!buf))
+		return;
+
+	rq_buf = buf->os_buf;
 	dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
 			 DMA_FROM_DEVICE);
 
@@ -642,7 +657,7 @@ static int fnic_send_frame(struct fnic *fnic, void *frame, int frame_len)
 	if ((fnic_fc_trace_set_data(fnic->fnic_num,
 				FNIC_FC_SEND | 0x80, (char *) frame,
 				frame_len)) != 0) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "fnic ctlr frame trace error");
 	}
 
@@ -650,9 +665,9 @@ static int fnic_send_frame(struct fnic *fnic, void *frame, int frame_len)
 
 	if (!vnic_wq_desc_avail(wq)) {
 		dma_unmap_single(&fnic->pdev->dev, pa, frame_len, DMA_TO_DEVICE);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "vnic work queue descriptor is not available");
-		ret = -1;
+		ret = -ENXIO;
 		goto fnic_send_frame_end;
 	}
 
@@ -686,7 +701,6 @@ fdls_send_fcoe_frame(struct fnic *fnic, void *frame, int frame_size,
 	struct fcoe_hdr *pfcoe_hdr;
 	struct fnic_frame_list *frame_elem;
 	int len = frame_size;
-	int ret;
 	struct fc_frame_header *fchdr = (struct fc_frame_header *) (frame +
 			FNIC_ETH_FCOE_HDRS_OFFSET);
 
@@ -706,13 +720,13 @@ fdls_send_fcoe_frame(struct fnic *fnic, void *frame, int frame_size,
 		&& (fnic->state != FNIC_IN_ETH_MODE)) {
 		frame_elem = mempool_alloc(fnic->frame_elem_pool, GFP_ATOMIC);
 		if (!frame_elem) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Failed to allocate memory for frame elem");
 			return -ENOMEM;
 		}
 		memset(frame_elem, 0, sizeof(struct fnic_frame_list));
 
-		FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			"Queueing FC frame: sid/did/type/oxid = 0x%x/0x%x/0x%x/0x%x\n",
 			ntoh24(fchdr->fh_s_id), ntoh24(fchdr->fh_d_id),
 			fchdr->fh_type, FNIC_STD_GET_OX_ID(fchdr));
@@ -724,11 +738,10 @@ fdls_send_fcoe_frame(struct fnic *fnic, void *frame, int frame_size,
 
 	fnic_debug_dump_fc_frame(fnic, fchdr, frame_size, "Outgoing");
 
-	ret = fnic_send_frame(fnic, frame, len);
-	return ret;
+	return fnic_send_frame(fnic, frame, len);
 }
 
-void fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame,
+int fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame,
 						 int frame_size)
 {
 	struct fnic *fnic = iport->fnic;
@@ -736,7 +749,7 @@ void fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame,
 
 	/* If module unload is in-progress, don't send */
 	if (fnic->in_remove)
-		return;
+		return -ESHUTDOWN;
 
 	if (iport->fabric.flags & FNIC_FDLS_FPMA_LEARNT) {
 		srcmac = iport->fpma;
@@ -746,7 +759,7 @@ void fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame,
 		dstmac = FCOE_ALL_FCF_MAC;
 	}
 
-	fdls_send_fcoe_frame(fnic, frame, frame_size, srcmac, dstmac);
+	return fdls_send_fcoe_frame(fnic, frame, frame_size, srcmac, dstmac);
 }
 
 int
@@ -778,7 +791,7 @@ void fnic_flush_tx(struct work_struct *work)
 	struct fc_frame *fp;
 	struct fnic_frame_list *cur_frame, *next;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Flush queued frames");
 
 	list_for_each_entry_safe(cur_frame, next, &fnic->tx_queue, links) {
@@ -797,7 +810,7 @@ fnic_fdls_register_portid(struct fnic_iport_s *iport, u32 port_id,
 	struct ethhdr *ethhdr;
 	int ret;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Setting port id: 0x%x fp: 0x%p fnic state: %d", port_id,
 				 fp, fnic->state);
 
@@ -810,7 +823,7 @@ fnic_fdls_register_portid(struct fnic_iport_s *iport, u32 port_id,
 	if (fnic->state == FNIC_IN_ETH_MODE || fnic->state == FNIC_IN_FC_MODE)
 		fnic->state = FNIC_IN_ETH_TRANS_FC_MODE;
 	else {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			 "Unexpected fnic state while processing FLOGI response\n");
 		return -1;
 	}
@@ -821,7 +834,7 @@ fnic_fdls_register_portid(struct fnic_iport_s *iport, u32 port_id,
 	 */
 	ret = fnic_flogi_reg_handler(fnic, port_id);
 	if (ret < 0) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "FLOGI registration error ret: %d fnic state: %d\n",
 					 ret, fnic->state);
 		if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE)
@@ -831,7 +844,7 @@ fnic_fdls_register_portid(struct fnic_iport_s *iport, u32 port_id,
 	}
 	iport->fabric.flags |= FNIC_FDLS_FPMA_LEARNT;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "FLOGI registration success\n");
 	return 0;
 }
@@ -873,6 +886,9 @@ static void fnic_wq_complete_frame_send(struct vnic_wq *wq,
 {
 	struct fnic *fnic = vnic_dev_priv(wq->vdev);
 
+	if (WARN_ON(!buf))
+		return;
+
 	dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
 			 DMA_TO_DEVICE);
 	mempool_free(buf->os_buf, fnic->frame_pool);
@@ -915,6 +931,9 @@ void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf)
 {
 	struct fnic *fnic = vnic_dev_priv(wq->vdev);
 
+	if (WARN_ON(!buf))
+		return;
+
 	dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len,
 			 DMA_TO_DEVICE);
 
@@ -931,7 +950,7 @@ fnic_fdls_add_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport,
 	struct fc_rport_identifiers ids;
 	struct rport_dd_data_s *rdd_data;
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Adding rport fcid: 0x%x", tport->fcid);
 
 	ids.node_name = tport->wwnn;
@@ -943,12 +962,12 @@ fnic_fdls_add_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport,
 	rport = fc_remote_port_add(fnic->host, 0, &ids);
 	spin_lock_irqsave(&fnic->fnic_lock, flags);
 	if (!rport) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Failed to add rport for tport: 0x%x", tport->fcid);
 		return;
 	}
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				 "Added rport fcid: 0x%x", tport->fcid);
 
 	/* Mimic these assignments in queuecommand to avoid timing issues */
@@ -987,7 +1006,7 @@ fnic_fdls_remove_tport(struct fnic_iport_s *iport,
 		fc_remote_port_delete(rport);
 
 		spin_lock_irqsave(&fnic->fnic_lock, flags);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 		 "Deregistered and freed tport fcid: 0x%x from scsi transport fc",
 		 tport->fcid);
 
@@ -1014,11 +1033,14 @@ void fnic_delete_fcp_tports(struct fnic *fnic)
 
 	spin_lock_irqsave(&fnic->fnic_lock, flags);
 	list_for_each_entry_safe(tport, next, &fnic->iport.tport_list, links) {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "removing fcp rport fcid: 0x%x", tport->fcid);
 		fdls_set_tport_state(tport, FDLS_TGT_STATE_OFFLINING);
 		fnic_del_tport_timer_sync(fnic, tport);
-		fnic_fdls_remove_tport(&fnic->iport, tport, flags);
+		if (IS_FNIC_FCP_INITIATOR(fnic))
+			fnic_fdls_remove_tport(&fnic->iport, tport, flags);
+		else if (IS_FNIC_NVME_INITIATOR(fnic))
+			nvfnic_delete_tport(&fnic->iport, tport, flags);
 	}
 	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 }
@@ -1041,36 +1063,40 @@ void fnic_tport_event_handler(struct work_struct *work)
 		tport = cur_evt->arg1;
 		switch (cur_evt->event) {
 		case TGT_EV_RPORT_ADD:
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "Add rport event");
 			if (tport->state == FDLS_TGT_STATE_READY) {
-				fnic_fdls_add_tport(&fnic->iport,
-					(struct fnic_tport_s *) cur_evt->arg1, flags);
+				if (IS_FNIC_FCP_INITIATOR(fnic))
+					fnic_fdls_add_tport(&fnic->iport, tport, flags);
+				else if (IS_FNIC_NVME_INITIATOR(fnic))
+					nvfnic_add_tport(fnic, tport, flags);
 			} else {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 					 "Target not ready. Add rport event dropped: 0x%x",
 					 tport->fcid);
 			}
 			break;
 		case TGT_EV_RPORT_DEL:
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "Remove rport event");
 			if (tport->state == FDLS_TGT_STATE_OFFLINING) {
-				fnic_fdls_remove_tport(&fnic->iport,
-					   (struct fnic_tport_s *) cur_evt->arg1, flags);
+				if (IS_FNIC_FCP_INITIATOR(fnic))
+					fnic_fdls_remove_tport(&fnic->iport, tport, flags);
+				else if (IS_FNIC_NVME_INITIATOR(fnic))
+					nvfnic_delete_tport(&fnic->iport, tport, flags);
 			} else {
-				FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+				FNIC_FCS_DBG(KERN_INFO, fnic,
 							 "remove rport event dropped tport fcid: 0x%x",
 							 tport->fcid);
 			}
 			break;
 		case TGT_EV_TPORT_DELETE:
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "Delete tport event");
 			fdls_delete_tport(tport->iport, tport);
 			break;
 		default:
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 						 "Unknown tport event");
 			break;
 		}
@@ -1110,12 +1136,15 @@ void fnic_reset_work_handler(struct work_struct *work)
 		spin_unlock_irqrestore(&reset_fnic_list_lock,
 							   reset_fnic_list_lock_flags);
 
-		dev_err(&cur_fnic->pdev->dev, "fnic: <%d>: issuing a host reset\n",
-			   cur_fnic->fnic_num);
-		host_reset_ret_code = fnic_host_reset(cur_fnic->host);
-		dev_err(&cur_fnic->pdev->dev,
-		   "fnic: <%d>: returned from host reset with status: %d\n",
-		   cur_fnic->fnic_num, host_reset_ret_code);
+		if (IS_FNIC_FCP_INITIATOR(cur_fnic)) {
+			dev_err(&cur_fnic->pdev->dev,
+				"fnic: <%d>: issuing a host reset\n",
+				cur_fnic->fnic_num);
+			host_reset_ret_code = fnic_host_reset(cur_fnic->host);
+			dev_err(&cur_fnic->pdev->dev,
+				"fnic: <%d>: returned from host reset with status: %d\n",
+				cur_fnic->fnic_num, host_reset_ret_code);
+		}
 
 		spin_lock_irqsave(&cur_fnic->fnic_lock, cur_fnic->lock_flags);
 		cur_fnic->pc_rscn_handling_status =
@@ -1142,7 +1171,7 @@ void fnic_fcpio_reset(struct fnic *fnic)
 	if (unlikely(fnic->state == FNIC_IN_FC_TRANS_ETH_MODE)) {
 		/* fw reset is in progress, poll for its completion */
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 			  "fnic is in unexpected state: %d for fw_reset\n",
 			  fnic->state);
 		return;
@@ -1155,7 +1184,7 @@ void fnic_fcpio_reset(struct fnic *fnic)
 	fnic->fw_reset_done = &fw_reset_done;
 	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
-	FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_FCS_DBG(KERN_INFO, fnic,
 				"Issuing fw reset\n");
 	if (fnic_fw_reset_handler(fnic)) {
 		spin_lock_irqsave(&fnic->fnic_lock, flags);
@@ -1163,14 +1192,14 @@ void fnic_fcpio_reset(struct fnic *fnic)
 			fnic->state = old_state;
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 	} else {
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					  "Waiting for fw completion\n");
 		time_remain = wait_for_completion_timeout(&fw_reset_done,
 						  msecs_to_jiffies(FNIC_FW_RESET_TIMEOUT));
-		FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_FCS_DBG(KERN_INFO, fnic,
 					  "Woken up after fw completion timeout\n");
 		if (time_remain == 0) {
-			FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_FCS_DBG(KERN_INFO, fnic,
 				  "FW reset completion timed out after %d ms\n",
 				  FNIC_FW_RESET_TIMEOUT);
 		}
diff --git a/drivers/scsi/fnic/fnic_fdls.h b/drivers/scsi/fnic/fnic_fdls.h
index e2959120c4f9..ce4b3aae77e3 100644
--- a/drivers/scsi/fnic/fnic_fdls.h
+++ b/drivers/scsi/fnic/fnic_fdls.h
@@ -78,14 +78,17 @@
 #define FNIC_FDLS_FPMA_LEARNT             0x2
 
 /* tport flags */
-#define FNIC_FDLS_TPORT_IN_GPN_FT_LIST 0x1
-#define FNIC_FDLS_TGT_ABORT_ISSUED     0x2
-#define FNIC_FDLS_TPORT_SEND_ADISC     0x4
-#define FNIC_FDLS_RETRY_FRAME          0x8
-#define FNIC_FDLS_TPORT_BUSY	       0x10
-#define FNIC_FDLS_TPORT_TERMINATING      0x20
-#define FNIC_FDLS_TPORT_DELETED        0x40
-#define FNIC_FDLS_SCSI_REGISTERED      0x200
+#define FNIC_FDLS_TPORT_IN_GPN_FT_LIST        0x1
+#define FNIC_FDLS_TGT_ABORT_ISSUED            0x2
+#define FNIC_FDLS_TPORT_SEND_ADISC            0x4
+#define FNIC_FDLS_RETRY_FRAME                 0x8
+#define FNIC_FDLS_TPORT_BUSY                  0x10
+#define FNIC_FDLS_TPORT_TERMINATING           0x20
+#define FNIC_FDLS_TPORT_DELETED               0x40
+#define FNIC_FDLS_NVME_REGISTERED             0x80
+#define FNIC_FDLS_NVME_TPORT_CLEANUP_PENDING  0x100
+#define FNIC_FDLS_SCSI_REGISTERED             0x200
+#define FNIC_TPORT_CAN_BE_FREED               0x400
 
 /* Retry supported by rport(returned by prli service parameters) */
 #define FDLS_FC_RP_FLAGS_RETRY 0x1
@@ -94,6 +97,7 @@
 #define fdls_get_state(_fdls_fabric)          ((_fdls_fabric)->state)
 
 #define FNIC_FDMI_ACTIVE    0x8
+#define FNIC_LPORT_NVME_REGISTERED 0x4
 #define FNIC_FIRST_LINK_UP    0x2
 
 #define fdls_set_tport_state(_tport, _state)    (_tport->state = _state)
@@ -119,6 +123,13 @@
 #define FNIC_FRAME_TYPE_TGT_PRLI	0x2800
 #define FNIC_FRAME_TYPE_TGT_ADISC	0x2A00
 #define FNIC_FRAME_TYPE_TGT_LOGO	0x2C00
+#define FNIC_FRAME_TYPE_NVME_LS		0x3000
+
+#define NVFNIC_FCPIO_TAG_POOL_SZ	(2048)
+#define NVFNIC_LS_REQ_OXID_POOL_SZ	(64)
+#define NVFNIC_LS_REQ_OXID_BASE		(0x2500)
+#define FNIC_LPORT_NVME_REGISTERED	0x4
+
 
 struct fnic_fip_fcf_s {
 	uint16_t vlan_id;
@@ -220,6 +231,8 @@ struct fnic_tport_s {
 	struct fc_rport *rport;
 	char str_wwpn[20];
 	char str_wwnn[20];
+	struct list_head ls_req_list;
+	struct nvme_fc_remote_port *nv_rport;
 };
 
 /* OXID pool related structures */
@@ -288,7 +301,6 @@ struct fnic_iport_s {
 	struct fnic_fdls_fip_s fip;
 	struct fnic_fdls_fabric_s fabric;
 	struct list_head tport_list;
-	struct list_head tport_list_pending_del;
 	/* list of tports for which we are yet to send PLOGO */
 	struct list_head inprocess_tport_list;
 	struct list_head deleted_tport_list;
@@ -308,6 +320,13 @@ struct fnic_iport_s {
 	struct fnic_iport_stats iport_stats;
 	char str_wwpn[20];
 	char str_wwnn[20];
+
+	 /* nvme */;
+	void *nvfnic_fcpio_tag[NVFNIC_FCPIO_TAG_POOL_SZ];
+	struct nvme_fc_local_port *nv_lport;
+	struct nvme_fc_port_template *nv_tmpl;
+	struct fnic_oxid_pool_s ls_req_oxid_pool;
+	struct list_head fcpio_list;
 };
 
 struct rport_dd_data_s {
@@ -336,6 +355,7 @@ enum fnic_recv_frame_type_e {
 	FNIC_TPORT_ADISC_RSP,
 	FNIC_TPORT_BLS_ABTS_RSP,
 	FNIC_TPORT_LOGO_RSP,
+	FNIC_LS_REQ_ABTS_RSP,
 
 	/* unsolicited requests */
 	FNIC_BLS_ABTS_REQ,
@@ -368,6 +388,12 @@ enum fnic_port_speeds {
 	DCEM_PORTSPEED_128G = 128000,
 };
 
+static inline bool fdls_tport_is_offline(struct fnic_tport_s *tport)
+{
+	return (tport->state == FDLS_TGT_STATE_OFFLINING ||
+		tport->state == FDLS_TGT_STATE_OFFLINE);
+}
+
 /* Function Declarations */
 /* fdls_disc.c */
 void fnic_fdls_disc_init(struct fnic_iport_s *iport);
@@ -381,6 +407,8 @@ uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport, int oxid_frame_type,
 	uint16_t *active_oxid);
 void fdls_free_oxid(struct fnic_iport_s *iport,
 	uint16_t oxid, uint16_t *active_oxid);
+int fdls_send_ls_req_abts(struct fnic_iport_s *iport,
+		struct fnic_tport_s *tport, unsigned int oxid);
 void fdls_tgt_logout(struct fnic_iport_s *iport,
 		     struct fnic_tport_s *tport);
 void fnic_del_fabric_timer_sync(struct fnic *fnic);
@@ -398,7 +426,7 @@ void fdls_fdmi_retry_plogi(struct fnic_iport_s *iport);
 
 /* fnic_fcs.c */
 void fnic_fdls_init(struct fnic *fnic, int usefip);
-void fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame,
+int fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame,
 	int frame_size);
 void fnic_fcoe_send_vlan_req(struct fnic *fnic);
 int fnic_send_fip_frame(struct fnic_iport_s *iport,
diff --git a/drivers/scsi/fnic/fnic_io.h b/drivers/scsi/fnic/fnic_io.h
index 0d974e040ab7..93a24efba26c 100644
--- a/drivers/scsi/fnic/fnic_io.h
+++ b/drivers/scsi/fnic/fnic_io.h
@@ -6,13 +6,19 @@
 #ifndef _FNIC_IO_H_
 #define _FNIC_IO_H_
 
+#include <scsi/fc/fc_fs.h>
 #include <scsi/fc/fc_fcp.h>
 #include "fnic_fdls.h"
+#include "fcpio.h"
+#include <linux/nvme-fc-driver.h>
 
 #define FNIC_DFLT_SG_DESC_CNT  32
 #define FNIC_MAX_SG_DESC_CNT        256     /* Maximum descriptors per sgl */
 #define FNIC_SG_DESC_ALIGN          16      /* Descriptor address alignment */
 
+#define NVME_STAT_ERROR 0x2
+#define NVME_STAT_TASK_SET_FULL 0x3
+
 struct host_sg_desc {
 	__le64 addr;
 	__le32 len;
@@ -39,6 +45,7 @@ enum fnic_ioreq_state {
 	FNIC_IOREQ_ABTS_PENDING,
 	FNIC_IOREQ_ABTS_COMPLETE,
 	FNIC_IOREQ_CMD_COMPLETE,
+	FNIC_IOREQ_RESET_TERM,
 };
 
 struct fnic_io_req {
@@ -50,12 +57,27 @@ struct fnic_io_req {
 	dma_addr_t sgl_list_pa;	/* dma address for sgl list */
 	u16 sgl_cnt;
 	u8 sgl_type; /* device DMA descriptor list type */
+	u8 sgl_mapped:1; /* set when sgl_list_pa is a valid DMA mapping */
 	u8 io_completed:1; /* set to 1 when fw completes IO */
 	u32 port_id; /* remote port DID */
 	unsigned long start_time; /* in jiffies */
+	u32 tag;
+	enum fnic_ioreq_state cmd_state;
+	u32 cmd_flags;
+	u32 abts_state;
 	struct completion *abts_done; /* completion for abts */
 	struct completion *dr_done; /* completion for device reset */
-	unsigned int tag;
 	struct scsi_cmnd *sc; /* midlayer's cmd pointer */
+
+	struct vnic_wq_copy *wq;
+	struct llist_node nvfnic_io_cmpl;
+	struct nvmefc_fcp_req *fcp_req;
+	void (*done)(struct fnic_io_req *io_req);
+	unsigned long waitq_start_time;	/* in jiffies */
+
+	struct timer_list admin_io_timer;
+	uint32_t status;
+	struct fnic_tag_t *tag_data;
+	struct fnic_io_event_s io_evt;
 };
 #endif /* _FNIC_IO_H_ */
diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c
index 7ed50b11afa6..02856745580f 100644
--- a/drivers/scsi/fnic/fnic_isr.c
+++ b/drivers/scsi/fnic/fnic_isr.c
@@ -222,7 +222,7 @@ int fnic_request_intr(struct fnic *fnic)
 							fnic->msix[i].devname,
 							fnic->msix[i].devid);
 			if (err) {
-				FNIC_ISR_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+				FNIC_ISR_DBG(KERN_ERR, fnic,
 							"request_irq failed with error: %d\n",
 							err);
 				fnic_free_intr(fnic);
@@ -250,10 +250,10 @@ int fnic_set_intr_mode_msix(struct fnic *fnic)
 	 * We need n RQs, m WQs, o Copy WQs, n+m+o CQs, and n+m+o+1 INTRs
 	 * (last INTR is used for WQ/RQ errors and notification area)
 	 */
-	FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_ISR_DBG(KERN_INFO, fnic,
 		"rq-array size: %d wq-array size: %d copy-wq array size: %d\n",
 		n, m, o);
-	FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_ISR_DBG(KERN_INFO, fnic,
 		"rq_count: %d raw_wq_count: %d wq_copy_count: %d cq_count: %d\n",
 		fnic->rq_count, fnic->raw_wq_count,
 		fnic->wq_copy_count, fnic->cq_count);
@@ -265,17 +265,17 @@ int fnic_set_intr_mode_msix(struct fnic *fnic)
 
 		vec_count = pci_alloc_irq_vectors(fnic->pdev, min_irqs, vecs,
 					PCI_IRQ_MSIX | PCI_IRQ_AFFINITY);
-		FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_ISR_DBG(KERN_INFO, fnic,
 					"allocated %d MSI-X vectors\n",
 					vec_count);
 
 		if (vec_count > 0) {
 			if (vec_count < vecs) {
-				FNIC_ISR_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+				FNIC_ISR_DBG(KERN_ERR, fnic,
 				"interrupts number mismatch: vec_count: %d vecs: %d\n",
 				vec_count, vecs);
 				if (vec_count < min_irqs) {
-					FNIC_ISR_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+					FNIC_ISR_DBG(KERN_ERR, fnic,
 								"no interrupts for copy wq\n");
 					return 1;
 				}
@@ -287,7 +287,7 @@ int fnic_set_intr_mode_msix(struct fnic *fnic)
 			fnic->wq_copy_count = vec_count - n - m - 1;
 			fnic->wq_count = fnic->raw_wq_count + fnic->wq_copy_count;
 			if (fnic->cq_count != vec_count - 1) {
-				FNIC_ISR_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+				FNIC_ISR_DBG(KERN_ERR, fnic,
 				"CQ count: %d does not match MSI-X vector count: %d\n",
 				fnic->cq_count, vec_count);
 				fnic->cq_count = vec_count - 1;
@@ -295,23 +295,23 @@ int fnic_set_intr_mode_msix(struct fnic *fnic)
 			fnic->intr_count = vec_count;
 			fnic->err_intr_offset = fnic->rq_count + fnic->wq_count;
 
-			FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_ISR_DBG(KERN_INFO, fnic,
 				"rq_count: %d raw_wq_count: %d copy_wq_base: %d\n",
 				fnic->rq_count,
 				fnic->raw_wq_count, fnic->copy_wq_base);
 
-			FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_ISR_DBG(KERN_INFO, fnic,
 				"wq_copy_count: %d wq_count: %d cq_count: %d\n",
 				fnic->wq_copy_count,
 				fnic->wq_count, fnic->cq_count);
 
-			FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
-				"intr_count: %d err_intr_offset: %u",
+			FNIC_ISR_DBG(KERN_INFO, fnic,
+				"intr_count: %d err_intr_offset: %u\n",
 				fnic->intr_count,
 				fnic->err_intr_offset);
 
 			vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSIX);
-			FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_ISR_DBG(KERN_INFO, fnic,
 					"fnic using MSI-X\n");
 			return 0;
 		}
@@ -351,7 +351,7 @@ int fnic_set_intr_mode(struct fnic *fnic)
 		fnic->intr_count = 1;
 		fnic->err_intr_offset = 0;
 
-		FNIC_ISR_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_ISR_DBG(KERN_DEBUG, fnic,
 			     "Using MSI Interrupts\n");
 		vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSI);
 
@@ -377,7 +377,7 @@ int fnic_set_intr_mode(struct fnic *fnic)
 		fnic->cq_count = 3;
 		fnic->intr_count = 3;
 
-		FNIC_ISR_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_ISR_DBG(KERN_DEBUG, fnic,
 			     "Using Legacy Interrupts\n");
 		vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX);
 
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index 24d62c0874ac..9b3025007075 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -42,12 +42,12 @@ static struct kmem_cache *fdls_frame_cache;
 static struct kmem_cache *fdls_frame_elem_cache;
 static struct kmem_cache *fdls_frame_recv_cache;
 static LIST_HEAD(fnic_list);
-static DEFINE_SPINLOCK(fnic_list_lock);
 static DEFINE_IDA(fnic_ida);
 
 struct work_struct reset_fnic_work;
 LIST_HEAD(reset_fnic_list);
 DEFINE_SPINLOCK(reset_fnic_list_lock);
+DEFINE_SPINLOCK(fnic_list_lock);
 
 /* Supported devices by fnic module */
 static const struct pci_device_id fnic_id_table[] = {
@@ -89,6 +89,15 @@ module_param(fnic_fc_trace_max_pages, uint, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(fnic_fc_trace_max_pages,
 		 "Total allocated memory pages for fc trace buffer");
 
+unsigned int nvme_dev_loss_tmo = 30;
+module_param_named(nvme_dev_loss_tmo, nvme_dev_loss_tmo, uint, 0644);
+MODULE_PARM_DESC(nvme_dev_loss_tmo, "configurable NVME dev loss timeout");
+
+unsigned int nvme_max_ios_to_process = 16;
+module_param(nvme_max_ios_to_process, uint, 0644);
+MODULE_PARM_DESC(nvme_max_ios_to_process,
+		"Maximum number of NVME IOs to process per work queue");
+
 static unsigned int fnic_max_qdepth = FNIC_DFLT_QUEUE_DEPTH;
 module_param(fnic_max_qdepth, uint, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(fnic_max_qdepth, "Queue depth to report for each LUN");
@@ -100,6 +109,7 @@ MODULE_PARM_DESC(pc_rscn_handling_feature_flag,
 
 struct workqueue_struct *reset_fnic_work_queue;
 struct workqueue_struct *fnic_fip_queue;
+struct workqueue_struct *fnic_cmpl_queue;
 
 static int fnic_sdev_init(struct scsi_device *sdev)
 {
@@ -185,7 +195,7 @@ static void fnic_get_host_speed(struct Scsi_Host *shost)
 	u32 port_speed = vnic_dev_port_speed(fnic->vdev);
 	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
 
-	FNIC_MAIN_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_MAIN_DBG(KERN_INFO, fnic,
 				  "port_speed: %d Mbps", port_speed);
 	atomic64_set(&fnic_stats->misc_stats.port_speed_in_mbps, port_speed);
 
@@ -235,7 +245,7 @@ static void fnic_get_host_speed(struct Scsi_Host *shost)
 		fc_host_speed(shost) = FC_PORTSPEED_128GBIT;
 		break;
 	default:
-		FNIC_MAIN_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_MAIN_DBG(KERN_INFO, fnic,
 					  "Unknown FC speed: %d Mbps", port_speed);
 		fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
 		break;
@@ -261,7 +271,7 @@ static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host)
 	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
 	if (ret) {
-		FNIC_MAIN_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_MAIN_DBG(KERN_DEBUG, fnic,
 					  "fnic: Get vnic stats failed: 0x%x", ret);
 		return stats;
 	}
@@ -287,64 +297,66 @@ static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host)
 void fnic_dump_fchost_stats(struct Scsi_Host *host,
 				struct fc_host_statistics *stats)
 {
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	struct fnic *fnic = *((struct fnic **) shost_priv(host));
+
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: seconds since last reset = %llu\n",
 			stats->seconds_since_last_reset);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: tx frames		= %llu\n",
 			stats->tx_frames);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: tx words		= %llu\n",
 			stats->tx_words);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: rx frames		= %llu\n",
 			stats->rx_frames);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: rx words		= %llu\n",
 			stats->rx_words);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: lip count		= %llu\n",
 			stats->lip_count);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: nos count		= %llu\n",
 			stats->nos_count);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: error frames		= %llu\n",
 			stats->error_frames);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: dumped frames	= %llu\n",
 			stats->dumped_frames);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: link failure count	= %llu\n",
 			stats->link_failure_count);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: loss of sync count	= %llu\n",
 			stats->loss_of_sync_count);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: loss of signal count	= %llu\n",
 			stats->loss_of_signal_count);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: prim seq protocol err count = %llu\n",
 			stats->prim_seq_protocol_err_count);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: invalid tx word count= %llu\n",
 			stats->invalid_tx_word_count);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: invalid crc count	= %llu\n",
 			stats->invalid_crc_count);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: fcp input requests	= %llu\n",
 			stats->fcp_input_requests);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: fcp output requests	= %llu\n",
 			stats->fcp_output_requests);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: fcp control requests	= %llu\n",
 			stats->fcp_control_requests);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: fcp input megabytes	= %llu\n",
 			stats->fcp_input_megabytes);
-	FNIC_MAIN_NOTE(KERN_NOTICE, host,
+	FNIC_MAIN_NOTE(KERN_NOTICE, fnic,
 			"fnic: fcp output megabytes	= %llu\n",
 			stats->fcp_output_megabytes);
 	return;
@@ -370,7 +382,7 @@ static void fnic_reset_host_stats(struct Scsi_Host *host)
 	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
 	if (ret) {
-		FNIC_MAIN_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_MAIN_DBG(KERN_DEBUG, fnic,
 				"fnic: Reset vnic stats failed"
 				" 0x%x", ret);
 		return;
@@ -547,6 +559,9 @@ static int fnic_cleanup(struct fnic *fnic)
 		vnic_wq_copy_clean(&fnic->hw_copy_wq[i],
 				   fnic_wq_copy_cleanup_handler);
 
+	if (IS_FNIC_NVME_INITIATOR(fnic))
+		nvfnic_flush_nvme_io_list(fnic);
+
 	for (i = 0; i < fnic->cq_count; i++)
 		vnic_cq_clean(&fnic->cq[i]);
 	for (i = 0; i < fnic->intr_count; i++)
@@ -676,6 +691,59 @@ static int fnic_scsi_drv_init(struct fnic *fnic)
 	return 0;
 }
 
+static int fnic_nvme_drv_init(struct fnic *fnic)
+{
+	int ret;
+	int hwq;
+
+	fnic->fnic_max_tag_id = NVFNIC_FCPIO_TAG_POOL_SZ;
+
+	for (hwq = 0; hwq < fnic->wq_copy_count; hwq++) {
+		fnic->sw_copy_wq[hwq].ioreq_table_size = fnic->fnic_max_tag_id;
+		fnic->sw_copy_wq[hwq].io_req_table =
+			kzalloc((fnic->sw_copy_wq[hwq].ioreq_table_size + 1) *
+					sizeof(struct fnic_io_req *), GFP_KERNEL);
+
+		if (!fnic->sw_copy_wq[hwq].io_req_table) {
+			fnic_free_ioreq_tables_mq(fnic);
+			return -ENOMEM;
+		}
+	}
+
+	dev_info(&fnic->pdev->dev, "fnic copy wqs: %d, Q0 ioreq table size: %d\n",
+			fnic->wq_copy_count, fnic->sw_copy_wq[0].ioreq_table_size);
+
+	if (sbitmap_init_node(&fnic->nvfnic_tag_map, NVFNIC_FCPIO_TAG_POOL_SZ,
+			      -1, GFP_KERNEL, NUMA_NO_NODE, false, true)) {
+		dev_err(&fnic->pdev->dev,
+			"Unable to allocate tag pool\n");
+		ret = -ENOMEM;
+		goto out_free_ioreq_tables;
+	}
+
+	fnic->io_req_pool = mempool_create_slab_pool(2, fnic_io_req_cache);
+	if (!fnic->io_req_pool) {
+		ret = -ENOMEM;
+		goto out_free_sbitmap;
+	}
+
+	init_llist_head(&fnic->nvme_io_event_llist);
+	INIT_WORK(&fnic->nvme_io_cmpl_work, nvfnic_nvme_iodone_work);
+
+	ret = nvfnic_add_lport(fnic);
+	if (ret)
+		goto out_free_req_pool;
+
+	return 0;
+out_free_req_pool:
+	mempool_destroy(fnic->io_req_pool);
+out_free_sbitmap:
+	sbitmap_free(&fnic->nvfnic_tag_map);
+out_free_ioreq_tables:
+	fnic_free_ioreq_tables_mq(fnic);
+	return ret;
+}
+
 void fnic_mq_map_queues_cpus(struct Scsi_Host *host)
 {
 	struct fnic *fnic = *((struct fnic **) shost_priv(host));
@@ -684,16 +752,16 @@ void fnic_mq_map_queues_cpus(struct Scsi_Host *host)
 	struct blk_mq_queue_map *qmap = &host->tag_set.map[HCTX_TYPE_DEFAULT];
 
 	if (intr_mode == VNIC_DEV_INTR_MODE_MSI || intr_mode == VNIC_DEV_INTR_MODE_INTX) {
-		FNIC_MAIN_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_MAIN_DBG(KERN_ERR, fnic,
 			"intr_mode is not msix\n");
 		return;
 	}
 
-	FNIC_MAIN_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_MAIN_DBG(KERN_INFO, fnic,
 			"qmap->nr_queues: %d\n", qmap->nr_queues);
 
 	if (l_pdev == NULL) {
-		FNIC_MAIN_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_MAIN_DBG(KERN_ERR, fnic,
 						"l_pdev is null\n");
 		return;
 	}
@@ -842,7 +910,7 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 		goto err_out_fnic_get_config;
 	}
 
-	switch (fnic->config.flags & 0xff0) {
+	switch (fnic->config.flags & FNIC_ROLE_CONFIG_MASK) {
 	case VFCF_FC_INITIATOR:
 		{
 			host =
@@ -861,8 +929,28 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 					fnic->fnic_num);
 		}
 		break;
+	case VFCF_FC_TARGET:
+		dev_info(&fnic->pdev->dev,
+			 "fnic: %d is scsi target\n",
+			 fnic->fnic_num);
+		err = -EOPNOTSUPP;
+		goto err_out_fnic_role;
+	case VFCF_FC_NVME_INITIATOR:
+		fnic_nvmef_debugfs_init(fnic);
+		fnic->role = FNIC_ROLE_NVME_INITIATOR;
+		dev_info(&fnic->pdev->dev, "fnic: %d is NVME initiator\n",
+			fnic->fnic_num);
+		break;
+	case VFCF_FC_NVME_TARGET:
+		dev_info(&fnic->pdev->dev,
+			 "fnic: %d is NVME target\n",
+			 fnic->fnic_num);
+		err = -EOPNOTSUPP;
+		goto err_out_fnic_role;
 	default:
-		dev_info(&fnic->pdev->dev, "fnic: %d has no role defined\n", fnic->fnic_num);
+		dev_info(&fnic->pdev->dev,
+			"fnic: %d has no role defined (0x%x)\n",
+			fnic->fnic_num, fnic->config.flags & FNIC_ROLE_CONFIG_MASK);
 		err = -EINVAL;
 		goto err_out_fnic_role;
 	}
@@ -995,11 +1083,16 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	iport->max_flogi_retries = fnic->config.flogi_retries;
 	iport->max_plogi_retries = fnic->config.plogi_retries;
 	iport->plogi_timeout = fnic->config.plogi_timeout;
-	iport->service_params =
-		(FNIC_FCP_SP_INITIATOR | FNIC_FCP_SP_RD_XRDY_DIS |
-		 FNIC_FCP_SP_CONF_CMPL);
-	if (fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR)
-		iport->service_params |= FNIC_FCP_SP_RETRY;
+	if (IS_FNIC_FCP_INITIATOR(fnic)) {
+		iport->service_params = (FNIC_FCP_SP_INITIATOR |
+				FNIC_FCP_SP_RD_XRDY_DIS | FNIC_FCP_SP_CONF_CMPL);
+		if (fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR)
+			iport->service_params |= FNIC_FCP_SP_RETRY;
+	} else if (IS_FNIC_NVME_INITIATOR(fnic)) {
+		iport->service_params = (FNIC_NVME_SP_INITIATOR);
+		if (fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR)
+			iport->service_params |= FNIC_NVME_SP_SLER;
+	}
 
 	iport->boot_time = jiffies;
 	iport->e_d_tov = fnic->config.ed_tov;
@@ -1031,6 +1124,7 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	INIT_LIST_HEAD(&fnic->frame_queue);
 	INIT_LIST_HEAD(&fnic->tx_queue);
 	INIT_LIST_HEAD(&fnic->tport_event_list);
+	init_llist_head(&fnic->nvme_io_event_llist);
 
 	INIT_DELAYED_WORK(&iport->oxid_pool.schedule_oxid_free_retry,
 	fdls_schedule_oxid_free_retry_work);
@@ -1061,9 +1155,15 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 	fnic_fdls_init(fnic, (fnic->config.flags & VFCF_FIP_CAPABLE));
 
-	err = fnic_scsi_drv_init(fnic);
-	if (err)
-		goto err_out_scsi_drv_init;
+	if (IS_FNIC_FCP_INITIATOR(fnic)) {
+		err = fnic_scsi_drv_init(fnic);
+		if (err)
+			goto err_out_scsi_drv_init;
+	} else if (IS_FNIC_NVME_INITIATOR(fnic)) {
+		err = fnic_nvme_drv_init(fnic);
+		if (err)
+			goto err_out_nvme_drv_init;
+	}
 
 	err = fnic_stats_debugfs_init(fnic);
 	if (err) {
@@ -1083,7 +1183,9 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 err_out_free_stats_debugfs:
 	fnic_stats_debugfs_remove(fnic);
 	fnic_free_ioreq_tables_mq(fnic);
-	scsi_remove_host(fnic->host);
+	if (IS_FNIC_FCP_INITIATOR(fnic))
+		scsi_remove_host(fnic->host);
+err_out_nvme_drv_init:
 err_out_scsi_drv_init:
 	fnic_free_intr(fnic);
 err_out_fnic_request_intr:
@@ -1109,7 +1211,10 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 err_out_fnic_alloc_vnic_res:
 	fnic_clear_intr_mode(fnic);
 err_out_fnic_set_intr_mode:
-	scsi_host_put(fnic->host);
+	if (IS_FNIC_NVME_INITIATOR(fnic))
+		fnic_nvmef_debugfs_remove(fnic);
+	if (IS_FNIC_FCP_INITIATOR(fnic))
+		scsi_host_put(fnic->host);
 err_out_fnic_role:
 err_out_scsi_host_alloc:
 err_out_fnic_get_config:
@@ -1158,7 +1263,10 @@ static void fnic_remove(struct pci_dev *pdev)
 	 */
 	flush_workqueue(fnic_event_queue);
 
-	fnic_scsi_unload(fnic);
+	if (IS_FNIC_FCP_INITIATOR(fnic))
+		fnic_scsi_unload(fnic);
+	else if (IS_FNIC_NVME_INITIATOR(fnic))
+		nvfnic_nvme_unload(fnic);
 
 	if (vnic_dev_get_intr_mode(fnic->vdev) == VNIC_DEV_INTR_MODE_MSI)
 		timer_delete_sync(&fnic->notify_timer);
@@ -1175,6 +1283,7 @@ static void fnic_remove(struct pci_dev *pdev)
 	if ((fnic_fdmi_support == 1) && (fnic->iport.fabric.fdmi_pending > 0))
 		timer_delete_sync(&fnic->iport.fabric.fdmi_timer);
 
+	fnic_nvmef_debugfs_remove(fnic);
 	fnic_stats_debugfs_remove(fnic);
 
 	/*
@@ -1184,6 +1293,11 @@ static void fnic_remove(struct pci_dev *pdev)
 	 */
 	fnic_cleanup(fnic);
 
+	if (IS_FNIC_NVME_INITIATOR(fnic)) {
+		sbitmap_free(&fnic->nvfnic_tag_map);
+		fnic_free_ioreq_tables_mq(fnic);
+	}
+
 	spin_lock_irqsave(&fnic_list_lock, flags);
 	list_del(&fnic->list);
 	spin_unlock_irqrestore(&fnic_list_lock, flags);
@@ -1202,8 +1316,10 @@ static void fnic_remove(struct pci_dev *pdev)
 	pci_disable_device(pdev);
 	pci_set_drvdata(pdev, NULL);
 	ida_free(&fnic_ida, fnic->fnic_num);
-	fnic_scsi_unload_cleanup(fnic);
-	scsi_host_put(fnic->host);
+	if (IS_FNIC_FCP_INITIATOR(fnic)) {
+		fnic_scsi_unload_cleanup(fnic);
+		scsi_host_put(fnic->host);
+	}
 	kfree(fnic);
 }
 
@@ -1323,6 +1439,15 @@ static int __init fnic_init_module(void)
 		goto err_create_fip_workq;
 	}
 
+	fnic_cmpl_queue =
+		alloc_workqueue("fnic_cmpl_wq",
+				WQ_PERCPU | WQ_HIGHPRI | WQ_MEM_RECLAIM, 0);
+	if (!fnic_cmpl_queue) {
+		pr_err("fnic completion work queue create failed\n");
+		err = -ENOMEM;
+		goto err_create_cmpl_workq;
+	}
+
 	if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON) {
 		reset_fnic_work_queue =
 			create_singlethread_workqueue("reset_fnic_work_queue");
@@ -1354,6 +1479,8 @@ static int __init fnic_init_module(void)
 err_pci_register:
 	fc_release_transport(fnic_fc_transport);
 err_fc_transport:
+	destroy_workqueue(fnic_cmpl_queue);
+err_create_cmpl_workq:
 	destroy_workqueue(fnic_fip_queue);
 err_create_fip_workq:
 	if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON)
@@ -1382,6 +1509,7 @@ static int __init fnic_init_module(void)
 static void __exit fnic_cleanup_module(void)
 {
 	pci_unregister_driver(&fnic_driver);
+	destroy_workqueue(fnic_cmpl_queue);
 	destroy_workqueue(fnic_event_queue);
 
 	if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON)
diff --git a/drivers/scsi/fnic/fnic_nvme.c b/drivers/scsi/fnic/fnic_nvme.c
new file mode 100644
index 000000000000..b237948dcafd
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_nvme.c
@@ -0,0 +1,2268 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2008 Cisco Systems, Inc.  All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc.  All rights reserved.
+ */
+#include <linux/mempool.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/scatterlist.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/kthread.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/delay.h>
+#include <linux/gfp.h>
+#include <linux/nvme.h>
+#include <linux/nvme-fc.h>
+#include "fnic.h"
+#include "fnic_trace.h"
+#include "fdls_fc.h"
+
+#if IS_REACHABLE(CONFIG_NVME_FC)
+
+static bool nvfnic_ls_req_cleanup(struct fnic_iport_s *iport,
+				  struct nvmefc_ls_req *lsreq,
+				  uint16_t oxid);
+
+int nvfnic_get_sg_count(struct fnic_io_req *io_req)
+{
+	return io_req->fcp_req->sg_cnt;
+}
+
+int nvfnic_dma_map_sgl(struct fnic *fnic, struct fnic_io_req *io_req,
+		       int sg_count)
+{
+	io_req->sgl_list_pa = dma_map_single(&fnic->pdev->dev,
+					     io_req->sgl_list,
+					     sizeof(io_req->sgl_list[0]) *
+					     sg_count, DMA_TO_DEVICE);
+	if (dma_mapping_error(&fnic->pdev->dev, io_req->sgl_list_pa)) {
+		dev_err(&fnic->pdev->dev, "DMA mapping failed\n");
+		io_req->sgl_list_pa = 0;
+		io_req->sgl_mapped = 0;
+		return -EBUSY;
+	}
+
+	io_req->sgl_mapped = 1;
+	return 0;
+}
+
+void nvfnic_dma_unmap_sgl(struct fnic *fnic, struct fnic_io_req *io_req)
+{
+	if (io_req->sgl_mapped) {
+		dma_unmap_single(&fnic->pdev->dev, io_req->sgl_list_pa,
+				 sizeof(io_req->sgl_list[0]) * io_req->sgl_cnt,
+				 DMA_TO_DEVICE);
+		io_req->sgl_mapped = 0;
+		io_req->sgl_list_pa = 0;
+	}
+}
+
+static void nvfnic_update_io_bytes(struct fnic *fnic,
+				   struct fnic_io_req *io_req, u8 opcode)
+{
+	if (opcode == nvme_cmd_read)
+		fnic->fcp_input_bytes += io_req->fcp_req->transferred_length;
+	else if (opcode == nvme_cmd_write)
+		fnic->fcp_output_bytes += io_req->fcp_req->transferred_length;
+}
+
+static void nvfnic_update_io_stats(struct fnic *fnic, u8 opcode)
+{
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+
+	switch (opcode) {
+	case nvme_cmd_read:
+		atomic64_inc(&fnic_stats->nvme_stats.nvme_input_requests);
+		break;
+	case nvme_cmd_write:
+		atomic64_inc(&fnic_stats->nvme_stats.nvme_output_requests);
+		break;
+	default:
+		atomic64_inc(&fnic_stats->nvme_stats.nvme_control_requests);
+		break;
+	}
+}
+
+static void nvfnic_update_cmpl_stats(struct fnic *fnic,
+				     struct fnic_io_req *io_req)
+{
+	struct io_path_stats *io_stats = &fnic->fnic_stats.io_stats;
+	atomic64_t *duration_stat;
+	unsigned long io_duration_time;
+
+	atomic64_dec(&io_stats->active_ios);
+	if (atomic64_read(&fnic->io_cmpl_skip))
+		atomic64_dec(&fnic->io_cmpl_skip);
+	else
+		atomic64_inc(&io_stats->io_completions);
+
+	io_duration_time = jiffies_to_msecs(jiffies - io_req->start_time);
+
+	if (io_duration_time <= 10)
+		duration_stat = &io_stats->io_btw_0_to_10_msec;
+	else if (io_duration_time <= 100)
+		duration_stat = &io_stats->io_btw_10_to_100_msec;
+	else if (io_duration_time <= 500)
+		duration_stat = &io_stats->io_btw_100_to_500_msec;
+	else if (io_duration_time <= 5000)
+		duration_stat = &io_stats->io_btw_500_to_5000_msec;
+	else if (io_duration_time <= 10000)
+		duration_stat = &io_stats->io_btw_5000_to_10000_msec;
+	else if (io_duration_time <= 30000)
+		duration_stat = &io_stats->io_btw_10000_to_30000_msec;
+	else {
+		duration_stat = &io_stats->io_greater_than_30000_msec;
+		if (io_duration_time >
+		    atomic64_read(&io_stats->current_max_io_time))
+			atomic64_set(&io_stats->current_max_io_time,
+				     io_duration_time);
+	}
+
+	atomic64_inc(duration_stat);
+}
+
+int
+nvfnic_alloc_fcpio_tag(struct fnic_iport_s *iport, struct fnic_io_req *io_req)
+{
+	struct fnic *fnic = iport->fnic;
+	int tag;
+
+	tag = sbitmap_get(&fnic->nvfnic_tag_map);
+	if (tag >= 0) {
+		WRITE_ONCE(io_req->tag, tag);
+		fnic->sw_copy_wq[0].io_req_table[tag] = io_req;
+		return tag;
+	}
+	return FNIC_NVME_NO_FREE_TAG;
+}
+
+void
+nvfnic_free_fcpio_tag(struct fnic_iport_s *iport, struct fnic_io_req *io_req)
+{
+	struct fnic *fnic = iport->fnic;
+	uint16_t tag = io_req->tag;
+
+	if (tag == FNIC_NVME_NO_FREE_TAG) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			    "Attempting to free invalid tag: 0x%x\n", tag);
+		return;
+	}
+	fnic->sw_copy_wq[0].io_req_table[tag] = NULL;
+	WRITE_ONCE(io_req->tag, FNIC_NVME_NO_FREE_TAG);
+	sbitmap_clear_bit(&fnic->nvfnic_tag_map, tag);
+}
+
+void
+nvfnic_reset_fcpio_tag_pool(struct fnic_iport_s *iport)
+{
+	WARN_ON(sbitmap_weight(&iport->fnic->nvfnic_tag_map));
+}
+
+struct fnic_io_req *
+nvfnic_find_io_req_by_tag(struct fnic *fnic, uint16_t tag)
+{
+	if (tag == FNIC_NVME_NO_FREE_TAG ||
+		!sbitmap_test_bit(&fnic->nvfnic_tag_map, tag))
+		return NULL;
+	return fnic->sw_copy_wq[0].io_req_table[tag];
+}
+
+/*
+ * Unmap the data buffer and sense buffer for an io_req,
+ * also unmap and free the device-private scatter/gather list.
+ */
+void nvfnic_release_nvme_ioreq_buf(struct fnic_iport_s *iport,
+					struct fnic_io_req *io_req)
+{
+	struct fnic *fnic = iport->fnic;
+
+	nvfnic_dma_unmap_sgl(fnic, io_req);
+
+	if (io_req->sgl_cnt)
+		mempool_free(io_req->sgl_list_alloc,
+			     fnic->io_sgl_pool[io_req->sgl_type]);
+}
+
+int nvfnic_get_nvmef_info(struct fnic *fnic, struct fnic_nvmef_info *info)
+{
+	int len = 0;
+	struct fnic_iport_s *iport = &fnic->iport;
+	int buf_size = info->buf_size;
+	struct fnic_tport_s *tport;
+	struct fnic_tport_s *next;
+	unsigned long flags;
+
+	if (buf_size <= 0)
+		return 0;
+
+	len += scnprintf(info->info_buffer + len, buf_size - len,
+			 "lport wwpn 0x%llx wwnn 0x%llx fcid 0x%06x\n",
+			 iport->wwpn, iport->wwnn, iport->fcid);
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+		if (len >= buf_size - 1)
+			break;
+
+		len += scnprintf(info->info_buffer + len, buf_size - len,
+				 "tport wwpn 0x%llx wwnn 0x%llx fcid 0x%06x\n",
+				 tport->wwpn, tport->wwnn, tport->fcid);
+	}
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+	return len;
+}
+
+inline int nvfnic_queue_wq_nvme_copy_desc(struct fnic *fnic,
+					       struct vnic_wq_copy *wq,
+					       struct fnic_io_req *io_req,
+					       int sg_count)
+{
+	struct scatterlist *sg;
+	struct fnic_tport_s *tport = io_req->tport;
+	struct host_sg_desc *desc;
+	struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats;
+	unsigned int i;
+	unsigned long intr_flags;
+	int flags;
+	u8 exch_flags;
+	struct scatterlist *sgl;
+	int idx;
+	int ret = 0;
+
+	if (sg_count) {
+		/* For each SGE, create a device desc entry */
+		desc = io_req->sgl_list;
+		sgl = io_req->fcp_req->first_sgl;
+		for_each_sg(sgl, sg, sg_count, i) {
+			desc->addr = cpu_to_le64(sg_dma_address(sg));
+			desc->len = cpu_to_le32(sg_dma_len(sg));
+			desc->_resvd = 0;
+			desc++;
+		}
+
+		ret = nvfnic_dma_map_sgl(fnic, io_req, sg_count);
+		if (ret)
+			return ret;
+	}
+
+	idx = (struct vnic_wq_copy *)wq - &fnic->hw_copy_wq[0];
+
+	/* Enqueue the descriptor in the Copy WQ */
+	spin_lock_irqsave(&fnic->wq_copy_lock[idx], intr_flags);
+
+	if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[idx])
+		free_wq_copy_descs(fnic, wq, idx);
+
+	if (unlikely(!vnic_wq_copy_desc_avail(wq))) {
+		spin_unlock_irqrestore(&fnic->wq_copy_lock[idx], intr_flags);
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			    "Enqueue failure: No descriptors\n");
+		atomic64_inc(&misc_stats->io_cpwq_alloc_failures);
+		return -EBUSY;
+	}
+
+	flags = 0;
+	if (io_req->fcp_req->io_dir == NVMEFC_FCP_READ)
+		flags = FCPIO_ICMND_RDDATA;
+	else if (io_req->fcp_req->io_dir == NVMEFC_FCP_WRITE)
+		flags = FCPIO_ICMND_WRDATA;
+
+	exch_flags = 0;
+
+	fnic_queue_wq_copy_desc_nvme_io(wq, io_req->tag,
+					exch_flags, io_req->sgl_cnt,
+					io_req->sgl_list_pa, flags,
+					io_req->fcp_req->cmdaddr,
+					io_req->fcp_req->cmdlen,
+					io_req->fcp_req->payload_length,
+					io_req->port_id,
+					tport->max_payload_size, tport->r_a_tov,
+					tport->e_d_tov);
+
+	atomic64_inc(&fnic->fnic_stats.fw_stats.active_fw_reqs);
+	if (atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs) >
+	    atomic64_read(&fnic->fnic_stats.fw_stats.max_fw_reqs))
+		atomic64_set(&fnic->fnic_stats.fw_stats.max_fw_reqs,
+		     atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs));
+
+	spin_unlock_irqrestore(&fnic->wq_copy_lock[idx], intr_flags);
+	return 0;
+}
+
+bool
+nvfnic_transport_ready(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+	struct fnic *fnic = iport->fnic;
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+
+	if (tport == NULL)
+		return false;
+
+	if (fdls_get_state(&iport->fabric) == FDLS_STATE_LINKDOWN ||
+	    iport->state != FNIC_IPORT_STATE_READY) {
+		atomic64_inc(&fnic_stats->misc_stats.iport_not_ready);
+		return false;
+	}
+
+	if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_IO_BLOCKED)))
+		return false;
+
+	if (fdls_tport_is_offline(tport)) {
+		atomic64_inc(&fnic_stats->misc_stats.tport_not_ready);
+		return false;
+	}
+
+	return true;
+}
+
+int nvfnic_queuecommand(struct fnic_io_req *io_req)
+{
+	struct fnic_iport_s *iport = io_req->iport;
+	struct fnic *fnic = iport->fnic;
+	struct fnic_tport_s *tport = io_req->tport;
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+	struct vnic_wq_copy *wq = io_req->wq;
+	int ret = 0;
+	int sg_count = 0;
+	unsigned long ptr;
+	unsigned char *lba;
+	u64 cmd_trace;
+	int idx;
+	struct nvme_fc_cmd_iu *cmdiu = io_req->fcp_req->cmdaddr;
+
+	io_req->cmd_state = FNIC_IOREQ_NOT_INITED;
+	io_req->cmd_flags = FNIC_NO_FLAGS;
+	/* Map the data buffer */
+	sg_count = nvfnic_get_sg_count(io_req);
+	if (sg_count < 0) {
+		FNIC_TRACE(nvfnic_queuecommand, fnic->fnic_num,
+			   io_req->tag, io_req, 0, io_req->fcp_req->io_dir,
+			   sg_count, io_req->cmd_state);
+		FNIC_NVME_DBG(KERN_INFO, fnic, "sg count is less-than-zero\n");
+		ret = -1;
+		goto out;
+	}
+
+	/* Determine the type of scatter/gather list we need */
+	io_req->sgl_cnt = sg_count;
+	io_req->sgl_type = FNIC_SGL_CACHE_DFLT;
+	if (sg_count > FNIC_DFLT_SG_DESC_CNT)
+		io_req->sgl_type = FNIC_SGL_CACHE_MAX;
+
+	if (sg_count) {
+		io_req->sgl_list =
+		    mempool_alloc(fnic->io_sgl_pool[io_req->sgl_type],
+				  GFP_ATOMIC);
+		if (!io_req->sgl_list) {
+			atomic64_inc(&fnic_stats->io_stats.alloc_failures);
+			FNIC_NVME_DBG(KERN_INFO, fnic,
+				      "Unable to alloc SGLs\n");
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		/* Cache sgl list allocated address before alignment */
+		io_req->sgl_list_alloc = io_req->sgl_list;
+		ptr = (unsigned long)io_req->sgl_list;
+		if (ptr % FNIC_SG_DESC_ALIGN) {
+			io_req->sgl_list = (struct host_sg_desc *)
+			    (((unsigned long)ptr + FNIC_SG_DESC_ALIGN - 1)
+			     & ~(FNIC_SG_DESC_ALIGN - 1));
+		}
+	}
+
+	io_req->port_id = tport->fcid;
+	io_req->start_time = jiffies;
+	io_req->cmd_state = FNIC_IOREQ_CMD_PENDING;
+	io_req->cmd_flags = FNIC_IO_INITIALIZED;
+
+	/* create copy wq desc and enqueue it */
+	idx = wq - &fnic->hw_copy_wq[0];
+	atomic64_inc(&fnic_stats->io_stats.ios[idx]);
+	ret = nvfnic_queue_wq_nvme_copy_desc(fnic, io_req->wq, io_req, sg_count);
+	if (ret) {
+		FNIC_NVME_DBG(KERN_ERR, fnic, "Unable to queue frame\n");
+		/*
+		 * In case another thread cancelled the request,
+		 * refetch the pointer under the lock.
+		 */
+		nvfnic_release_nvme_ioreq_buf(iport, io_req);
+		FNIC_TRACE(nvfnic_queuecommand, fnic->fnic_num,
+			   io_req->tag, io_req->fcp_req, 0, 0, 0,
+			   (((u64)io_req->cmd_flags << 32) | io_req->cmd_state));
+		return ret;
+	}
+
+	atomic64_inc(&fnic_stats->io_stats.active_ios);
+	atomic64_inc(&fnic_stats->io_stats.num_ios);
+	if (atomic64_read(&fnic_stats->io_stats.active_ios) >
+	    atomic64_read(&fnic_stats->io_stats.max_active_ios))
+		atomic64_set(&fnic_stats->io_stats.max_active_ios,
+		     atomic64_read(&fnic_stats->io_stats.active_ios));
+	io_req->cmd_flags |= FNIC_IO_ISSUED;
+ out:
+	lba = (char *)&cmdiu->sqe.rw.slba;
+	cmd_trace = ((u64) cmdiu->sqe.rw.opcode << 56 | (u64) lba[4] << 40 |
+		     (u64) lba[5] << 32 | (u64) lba[0] << 24 |
+		     (u64) lba[1] << 16 | (u64) lba[2] << 8 | lba[3]);
+
+	FNIC_TRACE(nvfnic_queuecommand, fnic->fnic_num,
+		   io_req->tag, 0, io_req,
+		   sg_count, cmd_trace,
+		   (((u64)io_req->cmd_flags >> 32) |
+		    io_req->cmd_state));
+
+	return ret;
+}
+
+int nvfnic_fcpio_send(struct nvme_fc_local_port *lport,
+		  struct nvme_fc_remote_port *rport, void *hw_queue_handle,
+		  struct nvmefc_fcp_req *fcp_req)
+{
+	struct fnic_iport_s *iport = lport->private;
+	struct fnic_io_req *io_req;
+	int ret;
+	struct fnic *fnic = iport->fnic;
+	unsigned long flags = 0;
+	struct fnic_tport_s *tport;
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+
+	tport = (struct fnic_tport_s *)rport->private;
+	atomic64_inc(&fnic_stats->io_stats.nvme_io_reqs_rcvd);
+	if (!nvfnic_transport_ready(iport, tport)) {
+		atomic64_inc(&fnic_stats->io_stats.nvme_io_rsps_sent);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		if (tport != NULL)
+			FNIC_NVME_DBG(KERN_INFO, fnic,
+				      "iport: 0x%x tport: 0x%x not ready\n",
+				      iport->fcid, tport->fcid);
+		else
+			FNIC_NVME_DBG(KERN_INFO, fnic,
+				      "iport: 0x%x tport not ready\n",
+				      iport->fcid);
+		return -ENODEV;
+	}
+	atomic_inc(&fnic->in_flight);
+
+	io_req = (struct fnic_io_req *) fcp_req->private;
+	io_req->iport = iport;
+	io_req->tport = (struct fnic_tport_s *)rport->private;
+	io_req->fcp_req = fcp_req;
+	io_req->done = nvfnic_fcpio_cmpl;
+	io_req->sgl_list = NULL;
+	io_req->sgl_list_alloc = NULL;
+	io_req->sgl_list_pa = 0;
+	io_req->sgl_cnt = 0;
+	io_req->sgl_mapped = 0;
+	io_req->wq = hw_queue_handle;
+	init_llist_node(&io_req->nvfnic_io_cmpl);
+
+	io_req->tag = nvfnic_alloc_fcpio_tag(iport, io_req);
+	if (io_req->tag == FNIC_NVME_NO_FREE_TAG) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			    "No free tag available. Failing IO\n");
+		atomic64_inc(&fnic_stats->io_stats.alloc_failures);
+		atomic_dec(&fnic->in_flight);
+		atomic64_inc(&fnic_stats->io_stats.nvme_io_rsps_sent);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return -EBUSY;
+	}
+
+	ret = nvfnic_queuecommand(io_req);
+	if (ret) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "Queuecommand failed tag: 0x%x\n",
+			      io_req->tag);
+		nvfnic_free_fcpio_tag(iport, io_req);
+	}
+
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+	atomic_dec(&fnic->in_flight);
+	return ret;
+}
+
+void nvfnic_fcpio_nvme_fast_cmpl_handler(struct fnic *fnic,
+				       struct fcpio_fw_req *desc)
+{
+	u8 type;
+	u8 hdr_status;
+	struct fcpio_tag ftag;
+	u32 id;
+	struct fnic_io_req *io_req;
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+	unsigned long start_time;
+	u64 cmd_trace;
+	char *lba;
+	struct nvme_fc_cmd_iu *cmdiu;
+	struct fnic_iport_s *iport;
+	unsigned int tag;
+
+	/* Decode the cmpl description to get the io_req id */
+	fcpio_header_dec(&desc->hdr, &type, &hdr_status, &ftag);
+	fcpio_tag_id_dec(&ftag, &id);
+	tag = id & FNIC_TAG_MASK;
+
+	if (tag >= fnic->fnic_max_tag_id) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			    "Tag out of range tag: 0x%x hdr status: %s\n", tag,
+			    fnic_fcpio_status_to_str(hdr_status));
+		return;
+	}
+	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+
+	io_req = nvfnic_find_io_req_by_tag(fnic, tag);
+
+	WARN_ON_ONCE(!io_req);
+	if (!io_req) {
+		atomic64_inc(&fnic_stats->io_stats.ioreq_null);
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "IO req null hdr: %s tag: 0x%x desc: 0x%p\n",
+			      fnic_fcpio_status_to_str(hdr_status), id, desc);
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			    "type: 0x%x status: 0x%x rsvd: 0x%x tag: 0x%x\n",
+			    desc->hdr.type, desc->hdr.status, desc->hdr._resvd,
+			    id);
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+
+	cmdiu = io_req->fcp_req->cmdaddr;
+	if (io_req->tag != tag) {
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "Tag mismatch tag:%d io:0x%x id:0x%x csn:%08x\n",
+			      tag, io_req->tag, id, be32_to_cpu(cmdiu->csn));
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+	iport = io_req->iport;
+
+	start_time = io_req->start_time;
+
+	/* firmware completed the io */
+	io_req->io_completed = 1;
+	if (io_req->cmd_state == FNIC_IOREQ_ABTS_PENDING) {
+		/*
+		 * set the FNIC_IO_DONE so that this doesn't get
+		 * flagged as 'out of order' if it was not aborted
+		 */
+		io_req->cmd_flags |= FNIC_IO_DONE;
+		io_req->cmd_flags |= FNIC_IO_ABTS_PENDING;
+		if (hdr_status == FCPIO_ABORTED)
+			io_req->cmd_flags |= FNIC_IO_ABORTED;
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "icmnd abts hdr:%d %s tag:0x%x io:%p",
+			      hdr_status, fnic_fcpio_status_to_str(hdr_status),
+			      id, io_req);
+		return;
+	}
+
+	if (io_req->cmd_state != FNIC_IOREQ_CMD_PENDING) {
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "IO freed id:%d tag:0x%x st:0x%x csn:%08x\n",
+			      id, io_req->tag, io_req->cmd_state,
+			      be32_to_cpu(cmdiu->csn));
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+
+	/* Mark the IO as complete */
+	io_req->cmd_state = FNIC_IOREQ_CMD_COMPLETE;
+	switch (hdr_status) {
+	case FCPIO_SUCCESS:
+		io_req->fcp_req->status = 0;
+		io_req->fcp_req->transferred_length =
+		    io_req->fcp_req->payload_length;
+		io_req->fcp_req->rcv_rsplen = 12;
+		break;
+	default:
+		FNIC_NVME_DBG(KERN_ERR, fnic, "HDR status is non-zero\n");
+		io_req->fcp_req->status = NVME_SC_INTERNAL;
+		break;
+	}
+
+	if (hdr_status != FCPIO_SUCCESS) {
+		atomic64_inc(&fnic_stats->io_stats.io_failures);
+		FNIC_NVME_DBG(KERN_INFO, fnic, "hdr status: %s\n",
+			    fnic_fcpio_status_to_str(hdr_status));
+	}
+
+	io_req->cmd_flags |= FNIC_IO_DONE;
+
+	cmdiu = io_req->fcp_req->cmdaddr;
+	lba = (char *)&cmdiu->sqe.rw.slba;
+	cmd_trace = ((u64) hdr_status << 56) |
+	    (u64) cmdiu->sqe.rw.opcode << 32 |
+	    (u64) lba[0] << 24 | (u64) lba[1] << 16 |
+	    (u64) lba[2] << 8 | lba[3];
+
+	FNIC_TRACE(nvfnic_fcpio_nvme_fast_cmpl_handler, fnic->fnic_num,
+			tag, io_req,
+		   jiffies_to_msecs(jiffies - start_time),
+		   desc, cmd_trace,
+		   (((u64) io_req->cmd_flags << 32) |
+		    io_req->cmd_state));
+
+	nvfnic_update_io_stats(fnic, cmdiu->sqe.rw.opcode);
+	nvfnic_update_io_bytes(fnic, io_req, cmdiu->sqe.rw.opcode);
+	nvfnic_update_cmpl_stats(fnic, io_req);
+
+	nvfnic_release_nvme_ioreq_buf(iport, io_req);
+	if (io_req->done)
+		io_req->done(io_req);
+
+	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+}
+
+void nvfnic_fcpio_ersp_cmpl_handler(struct fnic *fnic,
+				  struct fcpio_fw_req *desc, int sw_flag)
+{
+	u8 type;
+	u8 hdr_status;
+	struct fcpio_tag ftag;
+	u32 id;
+	struct fcpio_nvme_cmpl *nvme_cmpl;
+	struct fnic_io_req *io_req;
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+	unsigned long start_time;
+	uint32_t rsplen;
+	struct nvme_fc_ersp_iu *ersp;
+	struct nvme_fc_ersp_iu *nrsp;
+	struct nvme_fc_cmd_iu *cmdiu;
+	struct nvme_command *sqe;
+	struct nvme_completion *cqe;
+	u64 cmd_trace;
+	struct fnic_iport_s *iport;
+	unsigned int tag;
+	char *lba;
+	uint64_t tport_wwpn = 0;
+
+	/* Decode the cmpl description to get the io_req id */
+	fcpio_header_dec(&desc->hdr, &type, &hdr_status, &ftag);
+	fcpio_tag_id_dec(&ftag, &id);
+	nvme_cmpl = &desc->u.nvme_cmpl;
+	ersp = (struct nvme_fc_ersp_iu *) nvme_cmpl->resp_bytes;
+	tag = id & FNIC_TAG_MASK;
+
+	if (tag >= fnic->fnic_max_tag_id) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "Tag out of range tag:0x%x hdr:%s\n", tag,
+			      fnic_fcpio_status_to_str(hdr_status));
+		return;
+	}
+	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+
+	io_req = nvfnic_find_io_req_by_tag(fnic, tag);
+	if (!io_req) {
+		atomic64_inc(&fnic_stats->io_stats.ioreq_null);
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			    "IOREQ is null hdr status: %s tag: 0x%x desc: %p\n",
+			    fnic_fcpio_status_to_str(hdr_status), tag, desc);
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			    "type: 0x%x status: 0x%x rsvd: 0x%x\n",
+			    desc->hdr.type, desc->hdr.status, desc->hdr._resvd);
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+	iport = io_req->iport;
+	if (io_req->tport != NULL)
+		tport_wwpn = io_req->tport->wwpn;
+	cmdiu = io_req->fcp_req->cmdaddr;
+
+	if (io_req->tag != tag) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "Tag mismatch io:0x%x tag:0x%x id:0x%x\n",
+			      io_req->tag, tag, id);
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+
+	nrsp = (struct nvme_fc_ersp_iu *)io_req->fcp_req->rspaddr;
+	cmdiu = (struct nvme_fc_cmd_iu *)io_req->fcp_req->cmdaddr;
+	sqe = &cmdiu->sqe;
+	cqe = &nrsp->cqe;
+	start_time = io_req->start_time;
+
+	/* firmware completed the io */
+	io_req->io_completed = 1;
+
+	if (io_req->cmd_state == FNIC_IOREQ_ABTS_PENDING) {
+		/*
+		 * set the FNIC_IO_DONE so that this doesn't get
+		 * flagged as 'out of order' if it was not aborted
+		 */
+		io_req->cmd_flags |= FNIC_IO_DONE;
+		io_req->cmd_flags |= FNIC_IO_ABTS_PENDING;
+		if (hdr_status == FCPIO_ABORTED)
+			io_req->cmd_flags |= FNIC_IO_ABORTED;
+
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "ABTS pending hdr status: %s tag: 0x%x",
+			      fnic_fcpio_status_to_str(hdr_status), tag);
+		return;
+	}
+
+	if (io_req->cmd_state != FNIC_IOREQ_CMD_PENDING) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "IO already freed by abort. tag: 0x%x id: 0x%x\n",
+			      io_req->tag, id);
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+
+	/* Mark the IO as complete */
+	io_req->cmd_state = FNIC_IOREQ_CMD_COMPLETE;
+
+	switch (hdr_status) {
+	case FCPIO_SUCCESS:
+		io_req->fcp_req->status = 0;
+		if (!sw_flag) {
+			io_req->fcp_req->transferred_length =
+			    io_req->fcp_req->payload_length;
+			rsplen = 32;
+			nrsp->iu_len =
+			    cpu_to_be16(sizeof(struct nvme_fc_ersp_iu) / 4);
+			nrsp->xfrd_len =
+			    cpu_to_be32(io_req->fcp_req->payload_length);
+
+			nrsp->ersp_result = 0;
+			cqe->command_id = sqe->common.command_id;
+			cqe->status = 0;
+			cqe->result.u64 = 0;
+		} else {
+			io_req->fcp_req->transferred_length =
+			    be32_to_cpu(ersp->xfrd_len);
+			rsplen = be16_to_cpu(ersp->iu_len) * 4;
+			if (rsplen > sizeof(nvme_cmpl->resp_bytes) ||
+			    rsplen > io_req->fcp_req->rsplen) {
+				FNIC_NVME_DBG(KERN_ERR, fnic,
+					      "tport wwpn 0x%llx ERSP len %u desc %u req %u\n",
+					      tport_wwpn, rsplen,
+					      (u32)sizeof(nvme_cmpl->resp_bytes),
+					      io_req->fcp_req->rsplen);
+				io_req->fcp_req->status = NVME_SC_INTERNAL;
+				io_req->fcp_req->rcv_rsplen = 0;
+				break;
+			}
+			memcpy(io_req->fcp_req->rspaddr, ersp, rsplen);
+		}
+		atomic64_inc(&fnic_stats->nvme_stats.nvme_ersps);
+		io_req->fcp_req->rcv_rsplen = rsplen;
+		break;
+
+	default:
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+				"Unexpected header status: %d\n", hdr_status);
+		io_req->fcp_req->status = NVME_SC_INTERNAL;
+		break;
+	}
+
+	if (hdr_status != FCPIO_SUCCESS) {
+		atomic64_inc(&fnic_stats->io_stats.io_failures);
+		FNIC_NVME_DBG(KERN_ERR, fnic, "hdr status: %s tag: 0x%x\n",
+			    fnic_fcpio_status_to_str(hdr_status), tag);
+	}
+
+	io_req->cmd_flags |= FNIC_IO_DONE;
+
+	lba = (char *) &cmdiu->sqe.rw.slba;
+	cmd_trace = ((u64) hdr_status << 56) |
+	    (u64) ersp->ersp_result << 48 |
+	    (u64) cmdiu->sqe.rw.opcode << 32 |
+	    (u64) lba[0] << 24 | (u64) lba[1] << 16 |
+	    (u64) lba[2] << 8 | lba[3];
+
+	FNIC_TRACE(nvfnic_fcpio_ersp_cmpl_handler, fnic->fnic_num,
+		   tag, io_req,
+		   ((u64) nvme_cmpl->resvd[1] << 56 |
+		    (u64) nvme_cmpl->resvd[0] << 48 |
+		    jiffies_to_msecs(jiffies - start_time)),
+		   desc, cmd_trace,
+		   (((u64) io_req->cmd_flags << 32) |
+		    io_req->cmd_state));
+
+	nvfnic_update_io_stats(fnic, cmdiu->sqe.rw.opcode);
+	nvfnic_update_io_bytes(fnic, io_req, cmdiu->sqe.rw.opcode);
+	nvfnic_update_cmpl_stats(fnic, io_req);
+
+	nvfnic_release_nvme_ioreq_buf(iport, io_req);
+
+	/* Call NVME completion function to complete the IO */
+	if (io_req->done)
+		io_req->done(io_req);
+
+	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+}
+
+void nvfnic_fcpio_nvme_itmf_cmpl_handler(struct fnic *fnic,
+				       struct fcpio_fw_req *desc)
+{
+	u8 type;
+	u8 hdr_status;
+	struct fcpio_tag ftag;
+	u32 id;
+	unsigned int tag;
+	struct fnic_io_req *io_req;
+	struct nvme_fc_cmd_iu *cmd_iu;
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+	struct abort_stats *abts_stats = &fnic->fnic_stats.abts_stats;
+	struct terminate_stats *term_stats = &fnic->fnic_stats.term_stats;
+	struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats;
+	struct fnic_iport_s *iport;
+
+	fcpio_header_dec(&desc->hdr, &type, &hdr_status, &ftag);
+	fcpio_tag_id_dec(&ftag, &id);
+	tag = id & FNIC_TAG_MASK;
+
+	if (tag >= fnic->fnic_max_tag_id) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "Tag out of range id:0x%x tag:0x%x hdr:%s\n",
+			      id, tag, fnic_fcpio_status_to_str(hdr_status));
+		return;
+	}
+	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+
+	io_req = nvfnic_find_io_req_by_tag(fnic, tag);
+	WARN_ON_ONCE(!io_req);
+	if (!io_req) {
+		atomic64_inc(&fnic_stats->io_stats.ioreq_null);
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "IOREQ null hdr:%s tag:0x%x desc:%p\n",
+			      fnic_fcpio_status_to_str(hdr_status), tag, desc);
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "type: 0x%x status: 0x%x rsvd: 0x%x\n",
+			      desc->hdr.type, desc->hdr.status, desc->hdr._resvd);
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+
+	cmd_iu = io_req->fcp_req->cmdaddr;
+	FNIC_NVME_DBG(KERN_INFO, fnic,
+	      "Received ITMF completion tag: 0x%x hdr_status: %d csn: 0x%08x\n",
+	      tag, hdr_status, be32_to_cpu(cmd_iu->csn));
+
+	iport = io_req->iport;
+
+	/* Completion of abort cmd */
+	switch (hdr_status) {
+	case FCPIO_SUCCESS:
+		FNIC_NVME_DBG(KERN_DEBUG, fnic,
+				"Abort success received tag: 0x%x id: 0x%x\n",
+			      tag, id);
+		break;
+	case FCPIO_TIMEOUT:
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+				"Abort timeout received tag: 0x%x id: 0x%x\n",
+			      tag, id);
+		if (io_req->cmd_flags & FNIC_IO_ABTS_ISSUED)
+			atomic64_inc(&abts_stats->abort_fw_timeouts);
+		else
+			atomic64_inc(&term_stats->terminate_fw_timeouts);
+		break;
+	case FCPIO_ITMF_REJECTED:
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+				"Abort reject received tag: 0x%x id: 0x%x\n",
+			      tag, id);
+		break;
+
+	case FCPIO_IO_NOT_FOUND:
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "Abort IO not found tag:0x%x id:0x%x\n",
+			      tag, id);
+		if (io_req->cmd_flags & FNIC_IO_ABTS_ISSUED)
+			atomic64_inc(&abts_stats->abort_io_not_found);
+		else
+			atomic64_inc(&term_stats->terminate_io_not_found);
+		break;
+	default:
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+				"Abort unknown received tag: 0x%x id: 0x%x\n",
+			    tag, id);
+		if (io_req->cmd_flags & FNIC_IO_ABTS_ISSUED)
+			atomic64_inc(&abts_stats->abort_failures);
+		else
+			atomic64_inc(&term_stats->terminate_failures);
+		break;
+	}
+
+	if (io_req->cmd_state != FNIC_IOREQ_ABTS_PENDING) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "Abort late completion tag:0x%x id:0x%x\n",
+			      tag, id);
+		/* This is a late completion. Ignore it */
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+
+	io_req->abts_state = hdr_status;
+
+	/* If the status is IO not found consider it as success.
+	 * NVME sends abort even if rport is down in which case
+	 * we will get FCPIO_TIMEOUT. Consider this as success.
+	 */
+	if ((hdr_status == FCPIO_IO_NOT_FOUND) ||
+	    (hdr_status == FCPIO_TIMEOUT) ||
+	    (hdr_status == FCPIO_ITMF_REJECTED))
+		io_req->abts_state = FCPIO_SUCCESS;
+
+	io_req->cmd_flags |= FNIC_IO_ABT_TERM_DONE;
+
+	if (!(io_req->cmd_flags & (FNIC_IO_ABORTED | FNIC_IO_DONE)))
+		atomic64_inc(&misc_stats->no_icmnd_itmf_cmpls);
+
+	io_req->fcp_req->transferred_length = 0;
+	io_req->fcp_req->rcv_rsplen = 0;
+	if (io_req->abts_state == FCPIO_SUCCESS)
+		io_req->fcp_req->status = NVME_SC_ABORT_REQ;
+	else
+		io_req->fcp_req->status = NVME_SC_INTERNAL;
+
+	atomic64_dec(&fnic_stats->io_stats.active_ios);
+	if (atomic64_read(&fnic->io_cmpl_skip))
+		atomic64_dec(&fnic->io_cmpl_skip);
+	else
+		atomic64_inc(&fnic_stats->io_stats.io_completions);
+
+	nvfnic_release_nvme_ioreq_buf(iport, io_req);
+	if (io_req->done)
+		io_req->done(io_req);
+	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+}
+
+bool _cleanup_tport_io(struct sbitmap *map, unsigned int tag,
+			      void *data)
+{
+	struct fnic_tport_s *tport = data;
+	struct fnic_iport_s *iport = tport->iport;
+	struct fnic *fnic = iport->fnic;
+	struct fnic_io_req *io_req;
+	enum fnic_ioreq_state old_ioreq_state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	io_req = nvfnic_find_io_req_by_tag(fnic, tag);
+	if (!io_req || io_req->tport != tport) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return true;
+	}
+
+	if ((io_req->cmd_state == FNIC_IOREQ_ABTS_PENDING) ||
+	    (io_req->cmd_state == FNIC_DEV_RST_TERM_ISSUED)) {
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "Abort already pending 0x%x\n", io_req->tag);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return true;
+	}
+
+	FNIC_NVME_DBG(KERN_ERR, fnic,
+		      "io_req tag: 0x%x abort after unregister timeout\n",
+		      io_req->tag);
+
+	old_ioreq_state = io_req->cmd_state;
+	io_req->cmd_state = FNIC_IOREQ_ABTS_PENDING;
+	io_req->abts_state = FCPIO_INVALID_CODE;
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+	if (!nvfnic_queue_abort_io_req(fnic, io_req->tag,
+				      FCPIO_ITMF_ABT_TASK_TERM,
+				      io_req)) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			    "Failed to enqueue abort for ioreq tag: 0x%x\n",
+			    io_req->tag);
+		spin_lock_irqsave(&fnic->fnic_lock, flags);
+		io_req->cmd_state = old_ioreq_state;
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+	}
+	return true;
+}
+
+void
+nvfnic_cleanup_tport_io(struct fnic *fnic, struct fnic_tport_s *tport)
+{
+	unsigned long flags;
+	struct nvfnic_ls_req *nvfnic_ls_req, *next;
+	struct nvmefc_ls_req *lsreq;
+	uint16_t oxid;
+	LIST_HEAD(done_reqs);
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	list_for_each_entry_safe(nvfnic_ls_req, next,
+				 &(tport->ls_req_list), list) {
+		lsreq = nvfnic_ls_req->ls_req;
+		if (!lsreq || (lsreq->private == NULL)) {
+			FNIC_NVME_DBG(KERN_INFO, fnic,
+				"fnic_cleanup_tport_io lsreq NULL\n");
+			continue;
+		}
+		if (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_STARTED) {
+			FNIC_NVME_DBG(KERN_INFO, fnic,
+				"fnic_cleanup_tport_io lsreq abort started\n");
+			continue;
+		}
+		list_del_init(&nvfnic_ls_req->list);
+		lsreq->private = NULL;
+		oxid = nvfnic_ls_req->oxid;
+		fdls_free_oxid(&fnic->iport, oxid, &nvfnic_ls_req->oxid);
+		nvfnic_ls_req->state = FNIC_LS_REQ_CMD_COMPLETE;
+		list_add_tail(&nvfnic_ls_req->list, &done_reqs);
+	}
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+	list_for_each_entry_safe(nvfnic_ls_req, next, &done_reqs, list) {
+		list_del_init(&nvfnic_ls_req->list);
+		lsreq = nvfnic_ls_req->ls_req;
+		timer_delete_sync(&nvfnic_ls_req->ls_req_timer);
+		lsreq->done(lsreq, -ENXIO);
+	}
+
+	/* For link-down, IOs are freed by firmware reset completion */
+	if (fdls_get_state(&fnic->iport.fabric) == FDLS_STATE_LINKDOWN)
+		return;
+
+	sbitmap_for_each_set(&fnic->nvfnic_tag_map, _cleanup_tport_io, tport);
+}
+
+void
+nvfnic_terminate_tport_ls_reqs(struct fnic *fnic, struct fnic_tport_s *tport)
+{
+	struct nvmefc_ls_req *lsreq;
+	struct nvfnic_ls_req *nvfnic_ls_req, *next;
+	int count = 0;
+	uint16_t oxid;
+	LIST_HEAD(done_reqs);
+
+	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+	list_for_each_entry_safe(nvfnic_ls_req, next,
+		&(tport->ls_req_list), list) {
+
+		lsreq = nvfnic_ls_req->ls_req;
+		if (!lsreq || (lsreq->private == NULL)) {
+			FNIC_NVME_DBG(KERN_ERR, fnic,
+				"lsreq is NULL\n");
+			continue;
+		}
+		if (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_STARTED) {
+			FNIC_NVME_DBG(KERN_INFO, fnic,
+				"lsreq abort started\n");
+			continue;
+		}
+		oxid = nvfnic_ls_req->oxid;
+		list_del_init(&nvfnic_ls_req->list);
+		lsreq->private = NULL;
+		fdls_free_oxid(&fnic->iport, oxid, &nvfnic_ls_req->oxid);
+		nvfnic_ls_req->state = FNIC_LS_REQ_CMD_COMPLETE;
+		list_add_tail(&nvfnic_ls_req->list, &done_reqs);
+	}
+	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+
+	list_for_each_entry_safe(nvfnic_ls_req, next, &done_reqs, list) {
+		list_del_init(&nvfnic_ls_req->list);
+		lsreq = nvfnic_ls_req->ls_req;
+		timer_delete_sync(&nvfnic_ls_req->ls_req_timer);
+		lsreq->done(lsreq, -ENXIO);
+		count++;
+	}
+
+	FNIC_NVME_DBG(KERN_INFO, fnic,
+		"fnic_terminate_tport_lsreqs tport: 0x%x: freed lsreq: %d\n",
+		tport->fcid, count);
+}
+
+bool _terminate_tport_ios(struct sbitmap *map, unsigned int tag,
+				       void *data)
+{
+	struct fnic_tport_s *tport = data;
+	struct fnic_iport_s *iport = tport->iport;
+	struct fnic *fnic = iport->fnic;
+	struct fnic_io_req *io_req;
+	enum fnic_ioreq_state old_ioreq_state;
+	unsigned long flags;
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	io_req = fnic->sw_copy_wq[0].io_req_table[tag];
+	if (!io_req) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return true;
+	}
+
+	if (io_req->tport != tport) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return true;
+	}
+
+	if ((io_req->cmd_state == FNIC_IOREQ_ABTS_PENDING) ||
+	    (io_req->cmd_state == FNIC_DEV_RST_TERM_ISSUED)) {
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "Abort already pending 0x%x\n", io_req->tag);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return true;
+	}
+
+	FNIC_NVME_DBG(KERN_INFO, fnic,
+		      "Terminate tag: 0x%x (tport fcid 0x%x)\n",
+		      io_req->tag, io_req->tport->fcid);
+
+	old_ioreq_state = io_req->cmd_state;
+	io_req->cmd_state = FNIC_IOREQ_ABTS_PENDING;
+	io_req->abts_state = FCPIO_INVALID_CODE;
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+	if (!nvfnic_queue_abort_io_req(fnic, io_req->tag,
+					  FCPIO_ITMF_ABT_TASK_TERM, io_req)) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "Failed to enqueue abort for ioreq tag: 0x%x\n",
+			      io_req->tag);
+		spin_lock_irqsave(&fnic->fnic_lock, flags);
+		io_req->cmd_state = old_ioreq_state;
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+	}
+	return true;
+}
+
+void nvfnic_terminate_tport_ios(struct fnic *fnic,
+				     struct fnic_tport_s *tport)
+{
+	struct abort_stats *abts_stats = &fnic->fnic_stats.abts_stats;
+
+	sbitmap_for_each_set(&fnic->nvfnic_tag_map, _terminate_tport_ios, tport);
+
+	FNIC_NVME_DBG(KERN_INFO, fnic,
+		      "tport: 0x%x aborted %lld in_flight %d\n",
+		      tport->fcid, atomic64_read(&abts_stats->aborts),
+		      atomic_read(&fnic->in_flight));
+}
+
+bool _cleanup_all_nvme_io(struct sbitmap *map, unsigned int tag,
+				 void *data)
+{
+	struct fnic_iport_s *iport = data;
+	struct fnic_io_req *io_req;
+
+	io_req = iport->fnic->sw_copy_wq[0].io_req_table[tag];
+	if (!io_req)
+		return true;
+
+	io_req->cmd_state = FNIC_DEV_RST_TERM_ISSUED;
+	io_req->fcp_req->status = NVME_SC_INTERNAL;
+	io_req->fcp_req->transferred_length = 0;
+	io_req->fcp_req->rcv_rsplen = 0;
+	nvfnic_release_nvme_ioreq_buf(iport, io_req);
+	io_req->done(io_req);
+	return true;
+}
+
+void nvfnic_cleanup_all_nvme_ios(struct fnic *fnic)
+{
+	struct fnic_iport_s *iport = &fnic->iport;
+
+	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+	sbitmap_for_each_set(&fnic->nvfnic_tag_map, _cleanup_all_nvme_io,
+			     iport);
+	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+}
+
+void nvfnic_nvme_zero_devloss_tports(struct fnic *fnic)
+{
+	struct fnic_tport_s *tport, *next;
+
+	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+	list_for_each_entry_safe(tport, next, &fnic->iport.tport_list, links) {
+		if (tport->flags & FNIC_FDLS_NVME_REGISTERED) {
+			spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+			nvme_fc_set_remoteport_devloss(tport->nv_rport, 0);
+			spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+		}
+	}
+	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+}
+
+void nvfnic_nvme_unload(struct fnic *fnic)
+{
+	int ret = 0;
+	struct fnic_iport_s *iport = &fnic->iport;
+	unsigned long flags;
+	unsigned int time_wait =  FNIC_NVME_LPORT_REMOVE_WAIT;
+	unsigned int time_remain;
+	DECLARE_COMPLETION_ONSTACK(nvme_lport_unreg_done);
+
+	/* Mark iport state as INIT so that no IOs can be issued from this point */
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	fnic->in_remove = 1;
+	fnic->iport.state = FNIC_IPORT_STATE_LINK_WAIT;
+	fnic->nvme_lport_unreg_done = &nvme_lport_unreg_done;
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+	/*
+	 * If fnic is already processing link-down or fnic is held
+	 * in disabled state following a reboot we dont need to issue
+	 * firmware reset and unregister remote ports as it is already
+	 * done as part of link down handling.
+	 */
+	if (fdls_get_state(&iport->fabric) == FDLS_STATE_LINKDOWN) {
+		while (fnic->reset_in_progress == IN_PROGRESS) {
+			wait_for_completion_timeout(&fnic->reset_completion_wait,
+						    msecs_to_jiffies(5000));
+			FNIC_NVME_DBG(KERN_INFO, fnic,
+				      "rmmod waiting for reset %p\n", fnic);
+		}
+	} else if (fdls_get_state(&iport->fabric) != FDLS_STATE_INIT)
+		fnic_fcpio_reset(fnic);
+
+	/*
+	 * Mark state so that the workqueue thread stops forwarding
+	 * received frames and link events to the local port. ISR and
+	 * other threads that can queue work items will also stop
+	 * creating work items on the fnic workqueue
+	 */
+	nvfnic_nvme_zero_devloss_tports(fnic);
+	fnic_flush_tport_event_list(fnic);
+	fnic_delete_fcp_tports(fnic);
+
+	if (iport->flags & FNIC_LPORT_NVME_REGISTERED) {
+		ret = nvme_fc_unregister_localport(fnic->iport.nv_lport);
+		if (ret) {
+			FNIC_NVME_DBG(KERN_ERR, fnic,
+				"Unregister nvme localport failed: %d\n", ret);
+		} else {
+			time_remain = wait_for_completion_timeout(
+				fnic->nvme_lport_unreg_done,
+				msecs_to_jiffies(time_wait));
+			if (!time_remain) {
+				FNIC_NVME_DBG(KERN_ERR, fnic,
+					      "Local port removal timed out\n");
+				WARN_ON(1);
+			}
+			iport->flags &= ~FNIC_LPORT_NVME_REGISTERED;
+			kfree(iport->nv_tmpl);
+		}
+	}
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	fnic->nvme_lport_unreg_done = NULL;
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+	nvfnic_flush_nvme_io_list(fnic);
+}
+
+struct nvfnic_ls_req*
+nvfnic_find_ls_req(struct fnic_tport_s *tport, uint16_t oxid)
+{
+	struct nvfnic_ls_req *nvfnic_ls_req, *next;
+
+	list_for_each_entry_safe(nvfnic_ls_req, next, &(tport->ls_req_list), list) {
+		if (nvfnic_ls_req->oxid == oxid)
+			return nvfnic_ls_req;
+	}
+	return NULL;
+}
+
+void nvfnic_fcpio_cmpl(struct fnic_io_req *io_req)
+{
+	struct fnic *fnic = io_req->iport->fnic;
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+
+	nvfnic_free_fcpio_tag(io_req->iport, io_req);
+	atomic64_inc(&fnic_stats->io_stats.nvme_ios_queued_for_rsp);
+	io_req->waitq_start_time = jiffies;
+
+	llist_add(&io_req->nvfnic_io_cmpl, &fnic->nvme_io_event_llist);
+	atomic_inc(&fnic->nvme_io_event_queued);
+	atomic64_inc(&fnic_stats->io_stats.nvme_num_ios_in_waitq);
+
+	queue_work(fnic_cmpl_queue, &fnic->nvme_io_cmpl_work);
+}
+
+void nvfnic_process_ls_abts_rsp(struct fnic_iport_s *iport,
+		struct fc_frame_header *fchdr)
+{
+	uint32_t tport_fcid;
+	struct fnic_tport_s *tport;
+	struct nvfnic_ls_req *nvfnic_ls_req;
+	struct nvmefc_ls_req *lsreq;
+	uint8_t *fcid;
+	uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr);
+	struct fnic *fnic = iport->fnic;
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+
+	fcid = FNIC_STD_GET_S_ID(fchdr);
+	tport_fcid = ntoh24(fcid);
+
+	tport = fnic_find_tport_by_fcid(iport, tport_fcid);
+	if (tport == NULL) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "tport: 0x%x not found\n", tport_fcid);
+		return;
+	}
+
+	nvfnic_ls_req = nvfnic_find_ls_req(tport, oxid);
+	if (nvfnic_ls_req == NULL) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "tport: 0x%x lsreq oxid: 0x%x not found\n",
+			      tport_fcid, oxid);
+		return;
+	}
+
+	lsreq = nvfnic_ls_req->ls_req;
+	if ((lsreq == NULL) || (lsreq->private == NULL)) {
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "tport: 0x%x lsreq oxid: 0x%x already aborted\n",
+			      tport_fcid, oxid);
+		return;
+	}
+
+	atomic64_inc(&fnic_stats->nvme_stats.nvme_ls_abort_responses);
+	nvfnic_ls_req->state = FNIC_LS_REQ_ABTS_COMPLETE;
+
+	FNIC_NVME_DBG(KERN_DEBUG, fnic, "nvme_ls_requests: %lld\n",
+		      (u64) atomic64_read(&fnic_stats->nvme_stats.nvme_ls_requests));
+
+	list_del(&nvfnic_ls_req->list);
+	fdls_free_oxid(iport, oxid, &nvfnic_ls_req->oxid);
+	lsreq->private = NULL;
+	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+	timer_delete_sync(&nvfnic_ls_req->ls_req_timer);
+	lsreq->done(lsreq, NVME_SC_HOST_ABORTED_CMD);
+	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+}
+
+/**
+ * nvfnic_ls_rsp_recv - Handle received NVMe FC link service (LS) response
+ * @iport:  Pointer to the local FNIC port structure
+ * @fchdr:  Pointer to the Fibre Channel frame header for the
+ *          received response
+ * @len:    Length of the received frame
+ *
+ * This function processes link service (LS) responses received from
+ * NVMe Discovery Controllers or regular NVMe subsystems during
+ * association.
+ */
+void nvfnic_ls_rsp_recv(struct fnic_iport_s *iport,
+		struct fc_frame_header *fchdr, int len)
+{
+	uint8_t *fcid;
+	uint32_t tport_fcid;
+	struct fnic_tport_s *tport;
+	struct nvfnic_ls_req *nvfnic_ls_req;
+	struct nvmefc_ls_req *lsreq;
+	uint16_t oxid;
+	uint32_t rsp_len;
+	int sid_len = offsetof(struct fc_frame_header, fh_s_id) +
+		      sizeof(fchdr->fh_s_id);
+	int status = 0;
+	struct fnic *fnic = iport->fnic;
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+
+	if (len < (int)sizeof(*fchdr)) {
+		if (len >= sid_len) {
+			fcid = FNIC_STD_GET_S_ID(fchdr);
+			tport_fcid = ntoh24(fcid);
+			FNIC_NVME_DBG(KERN_ERR, fnic,
+				      "tport: 0x%x LS rsp len %d too short\n",
+				      tport_fcid, len);
+		} else {
+			FNIC_NVME_DBG(KERN_ERR, fnic,
+				      "LS response len %d too short\n", len);
+		}
+		return;
+	}
+	rsp_len = len - sizeof(*fchdr);
+
+	fcid = FNIC_STD_GET_S_ID(fchdr);
+	tport_fcid = ntoh24(fcid);
+
+	tport = fnic_find_tport_by_fcid(iport, tport_fcid);
+	if (!tport) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "tport: 0x%x not found\n", tport_fcid);
+		return;
+	}
+
+	oxid = FNIC_STD_GET_OX_ID(fchdr);
+	nvfnic_ls_req = nvfnic_find_ls_req(tport, oxid);
+	if (!nvfnic_ls_req) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "tport: 0x%x no nvfnic_lsreq for oxid: 0x%x\n",
+			      tport_fcid, oxid);
+		return;
+	}
+
+	lsreq = nvfnic_ls_req->ls_req;
+	if (!lsreq || (lsreq->private == NULL)) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "tport:0x%x lsreq:0x%x already done\n",
+			      tport_fcid, oxid);
+		return;
+	}
+	if (!lsreq->rspaddr) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "tport 0x%x lsreq 0x%x rspaddr NULL\n",
+			      tport_fcid, oxid);
+		return;
+	}
+
+	if ((nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_PENDING) ||
+	    (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_STARTED)) {
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "tport 0x%x lsreq oxid: 0x%x abts pending\n",
+			      tport_fcid, oxid);
+		return;
+	}
+
+	if (rsp_len > lsreq->rsplen) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "tport:0x%x lsreq:0x%x rsp %u > %u\n",
+			      tport_fcid, oxid, rsp_len, lsreq->rsplen);
+		status = -EOVERFLOW;
+	}
+
+	nvfnic_ls_req->state = FNIC_LS_REQ_CMD_COMPLETE;
+	atomic64_inc(&fnic_stats->nvme_stats.nvme_ls_responses);
+
+	list_del_init(&nvfnic_ls_req->list);
+	lsreq->private = NULL;
+	fdls_free_oxid(iport, oxid, &nvfnic_ls_req->oxid);
+
+	if (status == 0) {
+		FNIC_NVME_DBG(KERN_DEBUG, fnic,
+			      "tport:0x%x lsreq:0x%x completed\n",
+			      tport_fcid, oxid);
+
+		/* Copy the Response */
+		memcpy(lsreq->rspaddr, (uint8_t *)fchdr + sizeof(*fchdr),
+		       rsp_len);
+	}
+
+	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+	timer_delete_sync(&nvfnic_ls_req->ls_req_timer);
+	lsreq->done(lsreq, status);
+	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+}
+
+static bool nvfnic_ls_req_cleanup(struct fnic_iport_s *iport,
+				  struct nvmefc_ls_req *lsreq,
+				  uint16_t oxid)
+{
+	struct nvfnic_ls_req *nvfnic_ls_req = lsreq->private;
+
+	if (!nvfnic_ls_req)
+		return false;
+
+	lsreq->private = NULL;
+	list_del(&nvfnic_ls_req->list);
+	fdls_free_oxid(iport, oxid, &nvfnic_ls_req->oxid);
+	nvfnic_ls_req->state = FNIC_LS_REQ_CMD_COMPLETE;
+
+	return true;
+}
+
+void nvfnic_ls_req_timeout(struct timer_list *t)
+{
+	struct nvfnic_ls_req *nvfnic_ls_req = timer_container_of(nvfnic_ls_req,
+			t, ls_req_timer);
+	struct fnic *fnic = nvfnic_ls_req->fnic;
+	struct nvmefc_ls_req *ls_req = nvfnic_ls_req->ls_req;
+	struct fnic_iport_s *iport = &fnic->iport;
+	struct fnic_tport_s *tport = (struct fnic_tport_s *) nvfnic_ls_req->tport;
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+	uint16_t oxid = nvfnic_ls_req->oxid;
+	int timeout;
+
+	FNIC_NVME_DBG(KERN_INFO, fnic,
+		      "tport: 0x%x lsreq: 0x%x state: %d timeout\n",
+		      tport->fcid, nvfnic_ls_req->oxid,
+		      nvfnic_ls_req->state);
+	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+
+	if ((ls_req->private == NULL) ||
+	    (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_STARTED)) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "tport: 0x%x lsreq: 0x%x already aborted\n",
+			      tport->fcid, nvfnic_ls_req->oxid);
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+
+	if (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_PENDING) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "tport: 0x%x lsreq: 0x%x abort timeout\n",
+			      tport->fcid, nvfnic_ls_req->oxid);
+
+		ls_req = nvfnic_ls_req->ls_req;
+		nvfnic_ls_req_cleanup(iport, ls_req, oxid);
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		ls_req->done(ls_req, -ETIMEDOUT);
+		return;
+	} else if ((nvfnic_ls_req->state == FNIC_LS_REQ_CMD_PENDING) &&
+		   (nvfnic_transport_ready(iport, tport))) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "tport: 0x%x lsreq: 0x%x sending abort\n",
+			      tport->fcid, nvfnic_ls_req->oxid);
+		nvfnic_ls_req->state = FNIC_LS_REQ_CMD_ABTS_PENDING;
+		atomic64_inc(&fnic_stats->nvme_stats.nvme_ls_aborts);
+
+		if (fdls_send_ls_req_abts(iport, tport, nvfnic_ls_req->oxid) == 0) {
+			timeout = FNIC_LS_REQ_TMO_MSECS(ls_req->timeout);
+			mod_timer(&nvfnic_ls_req->ls_req_timer,
+				  round_jiffies(jiffies + msecs_to_jiffies(timeout)));
+			spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+			return;
+		}
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "tport: 0x%x lsreq: 0x%x cannot send abort\n",
+			      tport->fcid, oxid);
+	}
+
+	if (ls_req->private == NULL) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+
+	ls_req = nvfnic_ls_req->ls_req;
+	nvfnic_ls_req_cleanup(iport, ls_req, oxid);
+
+	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+	ls_req->done(ls_req, -ETIMEDOUT);
+}
+
+/**
+ * nvfnic_ls_req_send - Send NVMe FC link service (LS) request
+ * @lport:   Pointer to local NVMe FC port structure
+ * @rport:   Pointer to remote NVMe FC port structure
+ * @ls_req:  Pointer to the link service request structure
+ *
+ * This function is used to send link service (LS) commands to an NVMe
+ * Discovery Controller for discovery operations, as well as to regular
+ * NVMe subsystems during association. It encapsulates the logic for
+ * transmitting LS requests over the NVMe over Fabrics (NVMe-oF) FC
+ * transport.
+ *
+ * Returns: 0 on success, or a negative error code on failure.
+ */
+int nvfnic_ls_req_send(struct nvme_fc_local_port *lport,
+		  struct nvme_fc_remote_port *rport,
+		  struct nvmefc_ls_req *ls_req)
+{
+	int timeout;
+	uint8_t *frame;
+	uint8_t fcid[3];
+	unsigned long flags = 0;
+	struct fnic_iport_s *iport = lport->private;
+	uint8_t *ls_req_payload;
+	struct fnic *fnic = iport->fnic;
+	struct fc_frame_header *fchdr;
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+	struct nvfnic_ls_req *nvfnic_ls_req = ls_req->private;
+	uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET +
+			sizeof(struct fc_frame_header) + ls_req->rqstlen;
+	struct fnic_tport_s *tport;
+	int ret;
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+
+	tport = (struct fnic_tport_s *)rport->private;
+	INIT_LIST_HEAD(&nvfnic_ls_req->list);
+
+	if (!nvfnic_transport_ready(iport, tport)) {
+		if (tport != NULL)
+			FNIC_NVME_DBG(KERN_INFO, fnic,
+				      "iport: 0x%x tport: 0x%x transport not ready\n",
+				      iport->fcid, tport->fcid);
+		else
+			FNIC_NVME_DBG(KERN_INFO, fnic,
+				      "iport: 0x%x transport not ready\n",
+				      iport->fcid);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return -ENOLINK;
+	}
+
+	frame = fdls_alloc_frame(iport);
+	if (frame == NULL) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+		     "Failed to allocate frame to send NVME LS REQ");
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return -ENOMEM;
+	}
+
+	if (fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_NVME_LS,
+			&nvfnic_ls_req->oxid) == FNIC_UNASSIGNED_OXID) {
+		FNIC_FCS_DBG(KERN_INFO, fnic,
+		     "0x%x: Failed to allocate OXID to send NVME LS REQ",
+			 iport->fcid);
+		mempool_free(frame, fnic->frame_pool);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return -EAGAIN;
+	}
+
+	atomic64_inc(&fnic_stats->nvme_stats.nvme_ls_requests);
+	timer_setup(&nvfnic_ls_req->ls_req_timer, nvfnic_ls_req_timeout,
+		     0UL);
+
+	nvfnic_ls_req->fnic = fnic;
+	nvfnic_ls_req->tport = tport;
+	nvfnic_ls_req->state = FNIC_LS_REQ_CMD_INIT;
+	nvfnic_ls_req->ls_req = ls_req;
+
+	fchdr = (struct fc_frame_header *)(frame + FNIC_ETH_FCOE_HDRS_OFFSET);
+	*fchdr = (struct fc_frame_header) {
+		.fh_r_ctl = FC_RCTL_ELS4_REQ,
+		.fh_type = FC_TYPE_NVME,
+		.fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
+		.fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)
+	};
+
+	hton24(fcid, iport->fcid);
+	FNIC_STD_SET_S_ID(*fchdr, fcid);
+
+	hton24(fcid, tport->fcid);
+	FNIC_STD_SET_D_ID(*fchdr, fcid);
+
+	FNIC_STD_SET_OX_ID(*fchdr, nvfnic_ls_req->oxid);
+
+	ls_req_payload = frame + FNIC_ETH_FCOE_HDRS_OFFSET + sizeof(*fchdr);
+	memcpy(ls_req_payload, ls_req->rqstaddr, ls_req->rqstlen);
+
+	FNIC_NVME_DBG(KERN_INFO, fnic,
+		 "0x%x: NVME send ls req with oxid: 0x%x type: 0x%02x len: %d",
+		 iport->fcid, nvfnic_ls_req->oxid, *((uint8_t *) ls_req->rqstaddr),
+		 ls_req->rqstlen);
+
+	FNIC_NVME_DBG(KERN_INFO, fnic,
+		 "0x%x: ls_reqs count: %lld",
+		 iport->fcid,
+		 (u64) atomic64_read(&fnic_stats->nvme_stats.nvme_ls_requests));
+
+	list_add_tail(&nvfnic_ls_req->list, &tport->ls_req_list);
+	nvfnic_ls_req->state = FNIC_LS_REQ_CMD_PENDING;
+
+	ret = fnic_send_fcoe_frame(iport, frame, frame_size);
+	if (ret) {
+		nvfnic_ls_req_cleanup(iport, ls_req, nvfnic_ls_req->oxid);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		mempool_free(frame, fnic->frame_pool);
+		return ret;
+	}
+
+	timeout = FNIC_LS_REQ_TMO_MSECS(ls_req->timeout);
+	mod_timer(&nvfnic_ls_req->ls_req_timer,
+		  round_jiffies(jiffies + msecs_to_jiffies(timeout)));
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+	return 0;
+}
+
+void nvfnic_local_port_delete(struct nvme_fc_local_port *lport)
+{
+	struct fnic_iport_s *iport = (struct fnic_iport_s *) lport->private;
+	struct fnic *fnic = iport->fnic;
+	unsigned long flags = 0;
+
+	FNIC_NVME_DBG(KERN_INFO, fnic, "lport delete 0x%x\n",
+		      iport->fcid);
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	if (fnic->nvme_lport_unreg_done)
+		complete(fnic->nvme_lport_unreg_done);
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+}
+
+void nvfnic_remote_port_delete(struct nvme_fc_remote_port *rport)
+{
+	/*
+	 * Read rport->private without the lock only to find fnic.
+	 * Re-read it under fnic_lock to claim this delete callback, since
+	 * another callback may already have cleared it.
+	 */
+	struct fnic_tport_s *tport = (struct fnic_tport_s *)rport->private;
+	struct fnic_iport_s *iport;
+	struct fnic *fnic = NULL;
+	unsigned long flags = 0;
+
+	if (tport == NULL) {
+		pr_err("Attempt to delete already deleted tport\n");
+		return;
+	}
+
+	iport = tport->iport;
+	fnic = iport->fnic;
+	FNIC_NVME_DBG(KERN_INFO, fnic, "0x%x tport 0x%x\n",
+		      iport->fcid, tport->fcid);
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	tport = (struct fnic_tport_s *)rport->private;
+	if (tport == NULL) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "NVMe tport callback after NULL set %p\n",
+			      rport);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+
+	iport = tport->iport;
+	rport->private = NULL;
+
+	if (tport->timer_pending) {
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "tport: 0x%x canceling discovery timer\n",
+			      tport->fcid);
+		/*
+		 * The retry timer callback takes fnic_lock and dereferences
+		 * this tport. Set del_timer_inprogress before dropping the
+		 * lock so a callback that is already running observes teardown
+		 * and exits.
+		 *
+		 * timer_delete_sync() then waits until no callback can still
+		 * hold a reference before the remoteport-delete path completes
+		 * or frees the target port.
+		 */
+		tport->del_timer_inprogress = 1;
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		timer_delete_sync(&tport->retry_timer);
+		spin_lock_irqsave(&fnic->fnic_lock, flags);
+		tport->del_timer_inprogress = 0;
+		tport->timer_pending = 0;
+	}
+
+	if (tport->flags & FNIC_FDLS_NVME_TPORT_CLEANUP_PENDING) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "tport %8x waiting on clean pending\n",
+			      tport->fcid);
+	}
+
+	while (tport->flags & FNIC_FDLS_NVME_TPORT_CLEANUP_PENDING) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		msleep(2000);
+		spin_lock_irqsave(&fnic->fnic_lock, flags);
+	}
+
+	list_del(&tport->links);
+
+	if (tport->tport_del_done) {
+		tport->flags |= FNIC_TPORT_CAN_BE_FREED;
+		complete(tport->tport_del_done);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+	kfree(tport);
+}
+
+int nvfnic_create_queue(struct nvme_fc_local_port *lport,
+		    unsigned int idx, u16 size, void **handle)
+{
+	struct fnic_iport_s *iport = (struct fnic_iport_s *)lport->private;
+	struct fnic *fnic  = iport->fnic;
+
+	FNIC_NVME_DBG(KERN_DEBUG, fnic,
+		      "iport:0x%x queue:%d size:%d\n", iport->fcid, idx, size);
+
+	if (idx > fnic->wq_copy_count)
+		return -EINVAL;
+
+	if (idx == 0) {
+		/* Admin queue */
+		*handle = &fnic->hw_copy_wq[0];
+	} else {
+		/* IO queues */
+		*handle = &fnic->hw_copy_wq[idx-1];
+	}
+
+	return 0;
+}
+
+void nvfnic_ls_req_abort(struct nvme_fc_local_port *lport,
+		   struct nvme_fc_remote_port *rport,
+		   struct nvmefc_ls_req *lsreq)
+{
+	struct fnic_iport_s *iport = lport->private;
+	struct fnic *fnic = iport->fnic;
+	struct fnic_tport_s *tport;
+	struct nvfnic_ls_req *nvfnic_ls_req;
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+	uint16_t oxid;
+	int timeout;
+	int ret;
+
+	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+
+	tport = (struct fnic_tport_s *) rport->private;
+	/* find the request */
+	nvfnic_ls_req = lsreq->private;
+
+	if (nvfnic_ls_req == NULL) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "0x%x null lsreq already scheduled for abort\n",
+			      iport->fcid);
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+
+	if (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_PENDING) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "0x%x lsreq 0x%x already scheduled for abort\n",
+			      iport->fcid, nvfnic_ls_req->oxid);
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+
+	FNIC_NVME_DBG(KERN_INFO, fnic,
+		      "0x%x lsreq 0x%x abts\n",
+		      iport->fcid, nvfnic_ls_req->oxid);
+
+	nvfnic_ls_req->state = FNIC_LS_REQ_CMD_ABTS_STARTED;
+	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+	timer_delete_sync(&nvfnic_ls_req->ls_req_timer);
+
+	spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+	nvfnic_ls_req = lsreq->private;
+
+	if ((nvfnic_ls_req == NULL) ||
+	    (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_PENDING)) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "lsreq timeout raced with midlayer abort\n");
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+
+	/* Basic validations of the state */
+	if (!nvfnic_transport_ready(iport, tport)) {
+		/* If iport or tport offline, it will be handled from that event */
+		oxid = nvfnic_ls_req->oxid;
+		lsreq->private = NULL;
+		list_del(&nvfnic_ls_req->list);
+		fdls_free_oxid(iport, oxid, &nvfnic_ls_req->oxid);
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		lsreq->done(lsreq, -ENXIO);
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+				"nvfnic_lsreq_abort transport not ready\n");
+		return;
+	}
+
+	/* Mark the state and flags */
+	nvfnic_ls_req->state = FNIC_LS_REQ_CMD_ABTS_PENDING;
+	atomic64_inc(&fnic_stats->nvme_stats.nvme_ls_aborts);
+	oxid = nvfnic_ls_req->oxid;
+
+	ret = fdls_send_ls_req_abts(iport, tport, oxid);
+	if (!ret) {
+		timeout = FNIC_LS_REQ_TMO_MSECS(lsreq->timeout);
+		mod_timer(&nvfnic_ls_req->ls_req_timer,
+			  round_jiffies(jiffies + msecs_to_jiffies(timeout)));
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+
+	if (!nvfnic_ls_req_cleanup(iport, lsreq, oxid)) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+		return;
+	}
+
+	spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+	lsreq->done(lsreq, -EAGAIN);
+}
+
+bool nvfnic_queue_abort_io_req(struct fnic *fnic, int tag,
+			 u32 task_req, struct fnic_io_req *io_req)
+{
+	int idx;
+	unsigned long flags;
+	struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats;
+
+	idx = io_req->wq - &fnic->hw_copy_wq[0];
+
+	atomic_inc(&fnic->in_flight);
+
+	spin_lock_irqsave(&fnic->wq_copy_lock[idx], flags);
+
+	if (vnic_wq_copy_desc_avail(io_req->wq) <= fnic->wq_copy_desc_low[idx])
+		free_wq_copy_descs(fnic, io_req->wq, idx);
+
+	if (!vnic_wq_copy_desc_avail(io_req->wq)) {
+		spin_unlock_irqrestore(&fnic->wq_copy_lock[idx], flags);
+		atomic_dec(&fnic->in_flight);
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+				"tag 0x%x failure: no descriptors\n", tag);
+		atomic64_inc(&misc_stats->abts_cpwq_alloc_failures);
+		return false;
+	}
+	fnic_queue_wq_copy_desc_itmf(io_req->wq, tag | FNIC_TAG_ABORT,
+				     0, task_req, tag, NULL, io_req->port_id,
+				     fnic->config.ra_tov, fnic->config.ed_tov);
+
+	atomic64_inc(&fnic->fnic_stats.fw_stats.active_fw_reqs);
+	if (atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs) >
+	    atomic64_read(&fnic->fnic_stats.fw_stats.max_fw_reqs))
+		atomic64_set(&fnic->fnic_stats.fw_stats.max_fw_reqs,
+			     atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs));
+
+	spin_unlock_irqrestore(&fnic->wq_copy_lock[idx], flags);
+	atomic_dec(&fnic->in_flight);
+
+	return true;
+}
+
+void nvfnic_fcpio_abort(struct nvme_fc_local_port *lport,
+			struct nvme_fc_remote_port *rport,
+			void *hw_queue_handle, struct nvmefc_fcp_req *fcp_req)
+{
+	struct fnic_iport_s *iport = lport->private;
+	struct fnic *fnic = iport->fnic;
+	struct nvme_fc_cmd_iu *cmd_iu = fcp_req->cmdaddr;
+	struct fnic_io_req *io_req = (struct fnic_io_req *)fcp_req->private;
+	unsigned int tag = io_req->tag;
+	struct fnic_stats *fnic_stats = &fnic->fnic_stats;
+	struct abort_stats *abts_stats;
+	unsigned long flags = 0;
+	unsigned long abt_issued_time;
+	unsigned int task_req;
+	enum fnic_ioreq_state old_ioreq_state;
+	unsigned long num_ios_waitq, waitq_2sec, waitq_max_time;
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+
+	if (io_req->tag == FNIC_NVME_NO_FREE_TAG) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			      "tag: (0x%x) tport_fcid: 0x%x\n",
+			      io_req->tag, io_req->tport->fcid);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+
+	if (io_req != nvfnic_find_io_req_by_tag(fnic, io_req->tag)) {
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "cmd tag freed or not issued:0x%x sn:0x%08x\n",
+			      io_req->tag, be32_to_cpu(cmd_iu->csn));
+		num_ios_waitq =
+		    atomic64_read(&fnic_stats->io_stats.nvme_num_ios_in_waitq);
+		waitq_2sec =
+		    atomic64_read(&fnic_stats->io_stats.nvme_ios_in_waitq_3000_msec);
+		waitq_max_time =
+		    atomic64_read(&fnic_stats->io_stats.nvme_ios_in_waitq_max_time);
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "waitq:%ld waitq_2sec:%ld max_wait:%ld\n",
+			      num_ios_waitq, waitq_2sec, waitq_max_time);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+
+	if (io_req->cmd_state == FNIC_IOREQ_CMD_COMPLETE) {
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "IO already completed before abort: 0x%x\n", tag);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+
+	if ((io_req->cmd_state == FNIC_IOREQ_ABTS_PENDING) ||
+	    (io_req->cmd_state == FNIC_DEV_RST_TERM_ISSUED)) {
+		FNIC_NVME_DBG(KERN_INFO, fnic, "abort already pending %d\n",
+			      tag);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+
+	if (io_req->cmd_state != FNIC_IOREQ_CMD_PENDING) {
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "io_req completed or aborted for tag:0x%x\n",
+			      tag);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+
+	FNIC_NVME_DBG(KERN_INFO, fnic, "in abort cmd_sn:%08x %llx tag: 0x%x\n",
+		      be32_to_cpu(cmd_iu->csn),
+		      le64_to_cpu(cmd_iu->sqe.rw.slba), io_req->tag);
+
+	if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_IO_BLOCKED))) {
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "abort tag:0x%x returned during fw reset\n",
+			      io_req->tag);
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+	atomic_inc(&fnic->in_flight);
+
+	if (fdls_tport_is_offline(io_req->tport) ||
+	    (io_req->cmd_state == FNIC_IOREQ_RESET_TERM)) {
+		task_req = FCPIO_ITMF_ABT_TASK_TERM;
+	} else {
+		task_req = FCPIO_ITMF_ABT_TASK;
+	}
+
+	abts_stats = &fnic->fnic_stats.abts_stats;
+	atomic64_inc(&abts_stats->aborts);
+
+	abt_issued_time = jiffies_to_msecs(jiffies - io_req->start_time);
+	if (abt_issued_time <= 6000)
+		atomic64_inc(&abts_stats->abort_issued_btw_0_to_6_sec);
+	else if (abt_issued_time > 6000 && abt_issued_time <= 20000)
+		atomic64_inc(&abts_stats->abort_issued_btw_6_to_20_sec);
+	else if (abt_issued_time > 20000 && abt_issued_time <= 30000)
+		atomic64_inc(&abts_stats->abort_issued_btw_20_to_30_sec);
+	else if (abt_issued_time > 30000 && abt_issued_time <= 40000)
+		atomic64_inc(&abts_stats->abort_issued_btw_30_to_40_sec);
+	else if (abt_issued_time > 40000 && abt_issued_time <= 50000)
+		atomic64_inc(&abts_stats->abort_issued_btw_40_to_50_sec);
+	else if (abt_issued_time > 50000 && abt_issued_time <= 60000)
+		atomic64_inc(&abts_stats->abort_issued_btw_50_to_60_sec);
+	else
+		atomic64_inc(&abts_stats->abort_issued_greater_than_60_sec);
+
+	old_ioreq_state = io_req->cmd_state;
+	io_req->cmd_state = FNIC_IOREQ_ABTS_PENDING;
+	io_req->abts_state = FCPIO_INVALID_CODE;
+
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+	if (!nvfnic_queue_abort_io_req(fnic, io_req->tag, task_req, io_req)) {
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "Abort io req queue failed\n");
+		spin_lock_irqsave(&fnic->fnic_lock, flags);
+		io_req->cmd_state = old_ioreq_state;
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+	}
+	atomic_dec(&fnic->in_flight);
+}
+
+struct
+nvme_fc_port_template nvfnic_port = {
+	.localport_delete = nvfnic_local_port_delete,
+	.remoteport_delete = nvfnic_remote_port_delete,
+	.create_queue = nvfnic_create_queue,
+	.delete_queue = NULL,
+	.ls_req = nvfnic_ls_req_send,
+	.ls_abort = nvfnic_ls_req_abort,
+	.fcp_io = nvfnic_fcpio_send,
+	.fcp_abort = nvfnic_fcpio_abort,
+	.max_hw_queues = 1,
+	.max_sgl_segments = 256,
+	.max_dif_sgl_segments = 64,
+	.dma_boundary = 0xFFFFFFFF,
+	.local_priv_sz = sizeof(struct fnic_iport_s *),
+	.remote_priv_sz = sizeof(struct fnic_tport_s *),
+	.lsrqst_priv_sz = sizeof(struct nvfnic_ls_req),
+	.fcprqst_priv_sz = sizeof(struct fnic_io_req),
+};
+
+void nvfnic_flush_nvme_io_list(struct fnic *fnic)
+{
+	queue_work(fnic_cmpl_queue, &fnic->nvme_io_cmpl_work);
+	flush_work(&fnic->nvme_io_cmpl_work);
+}
+
+void nvfnic_nvme_iodone_work(struct work_struct *work)
+{
+	struct fnic *fnic = container_of(work, struct fnic, nvme_io_cmpl_work);
+	struct llist_node *llnode;
+	struct fnic_io_req *io_req, *tmp;
+
+	llnode = llist_del_all(&fnic->nvme_io_event_llist);
+	llist_for_each_entry_safe(io_req, tmp, llnode, nvfnic_io_cmpl) {
+		atomic_dec(&fnic->nvme_io_event_queued);
+		atomic64_dec(&fnic->fnic_stats.io_stats.nvme_num_ios_in_waitq);
+		io_req->fcp_req->done(io_req->fcp_req);
+	}
+}
+
+void nvfnic_exch_reset(struct fnic_iport_s *iport, struct fnic_tport_s *tport)
+{
+	FNIC_NVME_DBG(KERN_DEBUG, iport->fnic,
+			"0x%x: Exchange reset scheduled for tport: 0x%x\n",
+		    iport->fcid, tport->fcid);
+
+	nvfnic_terminate_tport_ls_reqs(iport->fnic, tport);
+	nvfnic_terminate_tport_ios(iport->fnic, tport);
+}
+
+void nvfnic_delete_tport(struct fnic_iport_s *iport,
+						struct fnic_tport_s *tport,
+						unsigned long flags)
+{
+	struct fnic *fnic = iport->fnic;
+	int ret;
+	unsigned int time_wait = FNIC_NVME_TPORT_REMOVE_WAIT;
+	unsigned int time_remain;
+	DECLARE_COMPLETION_ONSTACK(tm_done);
+	unsigned int fcid;
+	int count = 0;
+
+	if (!tport)
+		return;
+
+	fcid = tport->fcid;
+	fdls_set_tport_state(tport, FDLS_TGT_STATE_OFFLINE);
+
+	FNIC_NVME_DBG(KERN_DEBUG, fnic,
+			"0x%x: scheduled deletion for tport: 0x%x\n",
+		    iport->fcid, tport->fcid);
+
+	if (!(tport->flags & FNIC_FDLS_NVME_REGISTERED)) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			"0x%x: tport: 0x%x not registered. Freeing\n",
+		    iport->fcid, tport->fcid);
+		list_del(&tport->links);
+		kfree(tport);
+		return;
+	}
+
+	tport->tport_del_done = &tm_done;
+
+	tport->flags |= FNIC_FDLS_TPORT_DELETED;
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+	ret = nvme_fc_unregister_remoteport(tport->nv_rport);
+	if (ret) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			    "tport: 0x%x unregister failed %d\n",
+			    tport->fcid, ret);
+
+		nvfnic_terminate_tport_ls_reqs(fnic, tport);
+
+		spin_lock_irqsave(&fnic->fnic_lock, flags);
+		tport->tport_del_done = NULL;
+		if (tport->nv_rport && tport->nv_rport->private == tport)
+			tport->nv_rport->private = NULL;
+		list_del(&tport->links);
+		kfree(tport);
+		return;
+	}
+	time_remain = wait_for_completion_timeout(tport->tport_del_done,
+				msecs_to_jiffies(time_wait));
+
+	FNIC_NVME_DBG(KERN_DEBUG, fnic,
+		      "tport: 0x%x wait for deletion done\n",
+		      tport->fcid);
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	tport->tport_del_done = NULL;
+
+	if (!time_remain) {
+		FNIC_NVME_DBG(KERN_ERR, fnic,
+			    "tport: 0x%x nvme midlayer completion timed out\n",
+			    tport->fcid);
+
+		if (tport->flags & FNIC_TPORT_CAN_BE_FREED) {
+			kfree(tport);
+			FNIC_NVME_DBG(KERN_INFO, fnic,
+			      "tport: 0x%x delete complete\n", fcid);
+			return;
+		}
+
+		tport->flags |= FNIC_FDLS_NVME_TPORT_CLEANUP_PENDING;
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		nvfnic_cleanup_tport_io(fnic, tport);
+		spin_lock_irqsave(&fnic->fnic_lock, flags);
+		tport->flags &= ~FNIC_FDLS_NVME_TPORT_CLEANUP_PENDING;
+		return;
+	}
+	while (!(tport->flags & FNIC_TPORT_CAN_BE_FREED) &&
+	       (count < FNIC_TPORT_CLEANUP_WAIT_COUNT)) {
+		count++;
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		msleep(2000);
+		spin_lock_irqsave(&fnic->fnic_lock, flags);
+	}
+	if (tport->flags & FNIC_TPORT_CAN_BE_FREED)
+		kfree(tport);
+
+	FNIC_NVME_DBG(KERN_INFO, fnic,
+		      "tport: 0x%x delete complete\n", fcid);
+}
+
+int nvfnic_add_tport(struct fnic *fnic, struct fnic_tport_s *tport,
+		     unsigned long flags)
+{
+	struct fnic_iport_s *iport = &fnic->iport;
+	struct nvme_fc_port_info pinfo;
+	int ret = 0;
+
+	FNIC_NVME_DBG(KERN_INFO, fnic,
+		      "Adding tport to nvme wwpn: 0x%llx\n",
+		      tport->wwpn);
+
+	memset(&pinfo, 0, sizeof(struct nvme_fc_port_info));
+
+	pinfo.port_name = tport->wwpn;
+	pinfo.node_name = tport->wwnn;
+	pinfo.port_role = FC_PORT_ROLE_NVME_DISCOVERY | FC_PORT_ROLE_NVME_TARGET;
+	pinfo.port_id = tport->fcid;
+	pinfo.dev_loss_tmo = nvme_dev_loss_tmo;
+
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+	ret = nvme_fc_register_remoteport(iport->nv_lport, &pinfo,
+					  &tport->nv_rport);
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	if (ret) {
+		FNIC_NVME_DBG(KERN_INFO, fnic,
+			    "Failed to register tport wwpn: 0x%llx ret: %d\n",
+			    tport->wwpn, ret);
+		return ret;
+	}
+	tport->flags |= FNIC_FDLS_NVME_REGISTERED;
+	tport->nv_rport->private = tport;
+
+	snprintf(tport->str_wwpn, sizeof(tport->str_wwpn), "0x%llx", tport->wwpn);
+	snprintf(tport->str_wwnn, sizeof(tport->str_wwnn), "0x%llx", tport->wwnn);
+	return ret;
+}
+
+int nvfnic_add_lport(struct fnic *fnic)
+{
+	struct nvme_fc_port_info pinfo;
+	struct fnic_iport_s *iport = &fnic->iport;
+	int ret = 0;
+
+	FNIC_NVME_DBG(KERN_INFO, fnic,
+		      "Adding lport nvme wwpn: 0x%llx\n",
+		      iport->wwpn);
+
+	pinfo.node_name = iport->wwnn;
+	pinfo.port_name = iport->wwpn;
+	pinfo.port_role = FC_PORT_ROLE_NVME_INITIATOR;
+	pinfo.port_id = iport->fcid;
+
+	nvfnic_reset_fcpio_tag_pool(iport);
+
+	if (!(iport->flags & FNIC_LPORT_NVME_REGISTERED)) {
+		iport->nv_tmpl = kzalloc_obj(struct nvme_fc_port_template, GFP_ATOMIC);
+		if (!iport->nv_tmpl) {
+			FNIC_FCS_DBG(KERN_INFO, fnic,
+				     "iport:0x%x NVMe tmpl alloc failed\n",
+				     iport->fcid);
+			return -ENOMEM;
+		}
+		memcpy(iport->nv_tmpl, &nvfnic_port,
+		       sizeof(struct nvme_fc_port_template));
+		iport->nv_tmpl->max_hw_queues = fnic->wq_copy_count;
+
+		ret = nvme_fc_register_localport(&pinfo, iport->nv_tmpl,
+						 &fnic->pdev->dev, &iport->nv_lport);
+		if (ret) {
+			FNIC_NVME_DBG(KERN_ERR, fnic,
+					"Failed to add wwpn: 0x%llx ret: %d\n",
+					iport->wwpn, ret);
+			kfree(iport->nv_tmpl);
+			return ret;
+		}
+		iport->flags |= FNIC_LPORT_NVME_REGISTERED;
+		iport->nv_lport->private = iport;
+	}
+
+	sprintf(iport->str_wwpn, "0x%llx", iport->wwpn);
+	sprintf(iport->str_wwnn, "0x%llx", iport->wwnn);
+
+	FNIC_NVME_DBG(KERN_INFO, fnic,
+		      "Successfully added lport wwpn: 0x%llx\n",
+		      iport->wwpn);
+	return 0;
+}
+
+#endif
diff --git a/drivers/scsi/fnic/fnic_nvme.h b/drivers/scsi/fnic/fnic_nvme.h
new file mode 100644
index 000000000000..c05908b68e36
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_nvme.h
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2008 Cisco Systems, Inc.  All rights reserved.
+ * Copyright 2007 Nuova Systems, Inc.  All rights reserved.
+ */
+#ifndef _FNIC_NVME_H
+#define _FNIC_NVME_H
+
+#include "fdls_fc.h"
+#include "fnic_fdls.h"
+
+#define FNIC_NVME_TPORT_REMOVE_WAIT (5 * 1000)
+#define FNIC_NVME_TPORT_LIST_EMPTY_WAIT (FNIC_NVME_TPORT_REMOVE_WAIT * 2)
+#define FNIC_TPORT_CLEANUP_WAIT_COUNT 8
+#define FNIC_NVME_LPORT_REMOVE_WAIT (2 * 60 * 1000)
+
+#define FNIC_LS_REQ_FLAGS_NONE      0x0
+#define FNIC_LS_REQ_FLAGS_ABORTED   0x1
+#define FNIC_LS_REQ_FLAGS_DONE      0x2
+#define FNIC_LS_REQ_ABORT_COMPLETED 0x4
+#define FNIC_STATUS_LS_REQ_ABORTED 0x1
+
+#define FNIC_LS_REQ_MIN_TMO_SECS (2)
+#define FNIC_LS_REQ_MAX_TMO_SECS (5)
+#define FNIC_NVME_ADMIN_IO_TIMEOUT 30000	/* mSec */
+#define FNIC_NVME_NO_FREE_TAG     (0xFFFF)
+
+#define FNIC_LS_REQ_TMO_MSECS(tmo)    (((tmo >= FNIC_LS_REQ_MIN_TMO_SECS) && \
+					(tmo <= FNIC_LS_REQ_MAX_TMO_SECS)) ? \
+					(tmo * 1000) : (FNIC_LS_REQ_MIN_TMO_SECS * 1000))
+
+#define IS_ADMIN_IO(_io_req)     \
+	(NVME_CMD_FLAGS(_io_req) & FNIC_NVME_ADMIN_IO_TIMER_PENDING)
+
+extern unsigned int nvme_max_ios_to_process;
+extern unsigned int nvme_dev_loss_tmo;
+extern spinlock_t fnic_list_lock;
+
+enum nvfnic_lsreq_state_e {
+	FNIC_LS_REQ_CMD_INIT = 0,
+	FNIC_LS_REQ_CMD_PENDING,
+	FNIC_LS_REQ_CMD_ABTS_PENDING,
+	FNIC_LS_REQ_CMD_COMPLETE,
+	FNIC_LS_REQ_ABTS_COMPLETE,
+	FNIC_LS_REQ_CMD_ABTS_STARTED,
+};
+
+struct fnic_nvme_tag {
+	struct list_head free_list;
+	int tag_id;
+};
+
+struct nvfnic_ls_req {
+	struct list_head list;
+	struct nvmefc_ls_req *ls_req;
+	uint16_t oxid;
+	struct timer_list ls_req_timer;
+	struct fnic *fnic;
+	struct fnic_tport_s *tport;
+	int state;
+	unsigned int flags;
+};
+
+#if IS_REACHABLE(CONFIG_NVME_FC)
+int nvfnic_nvme_io_done_handler(void *arg);
+struct fnic_io_req *nvfnic_find_io_req_by_tag(struct fnic *fnic, uint16_t tag);
+void nvfnic_reset_fcpio_tag_pool(struct fnic_iport_s *iport);
+int nvfnic_add_lport(struct fnic *fnic);
+void nvfnic_fcpio_abort(struct nvme_fc_local_port *lport,
+			struct nvme_fc_remote_port *rport,
+			void *hw_queue_handle, struct nvmefc_fcp_req *fcp_req);
+void nvfnic_remote_port_delete(struct nvme_fc_remote_port *rport);
+void nvfnic_local_port_delete(struct nvme_fc_local_port *lport);
+int nvfnic_dma_map_sgl(struct fnic *fnic, struct fnic_io_req *io_req,
+		       int sg_count);
+void nvfnic_dma_unmap_sgl(struct fnic *fnic, struct fnic_io_req *io_req);
+int nvfnic_get_sg_count(struct fnic_io_req *io_req);
+void nvfnic_release_nvme_ioreq_buf(struct fnic_iport_s *iport,
+				   struct fnic_io_req *io_req);
+void nvfnic_dump_nvcmd(struct fnic_io_req *io_req, uint8_t flags);
+bool _cleanup_tport_io(struct sbitmap *map, unsigned int tag, void *data);
+void nvfnic_flush_nvme_io_list(struct fnic *fnic);
+void nvfnic_fcpio_cmpl(struct fnic_io_req *io_req);
+bool nvfnic_transport_ready(struct fnic_iport_s *iport,
+			    struct fnic_tport_s *tport);
+int nvfnic_alloc_fcpio_tag(struct fnic_iport_s *iport,
+				struct fnic_io_req *io_req);
+int nvfnic_queuecommand(struct fnic_io_req *io_req);
+void nvfnic_free_fcpio_tag(struct fnic_iport_s *iport,
+			   struct fnic_io_req *io_req);
+void nvfnic_delete_lport(struct fnic_iport_s *iport);
+int nvfnic_add_tport(struct fnic *fnic, struct fnic_tport_s *tport,
+		     unsigned long flags);
+void nvfnic_cleanup_all_nvme_ios(struct fnic *fnic);
+void nvfnic_delete_tport_work(struct work_struct *work);
+void nvfnic_delete_tport(struct fnic_iport_s *iport,
+			 struct fnic_tport_s *tport, unsigned long flags);
+void nvfnic_fcpio_nvme_fast_cmpl_handler(struct fnic *fnic,
+					 struct fcpio_fw_req *desc);
+void nvfnic_ls_rsp_recv(struct fnic_iport_s *iport,
+			struct fc_frame_header *fchdr, int len);
+void nvfnic_process_ls_abts_rsp(struct fnic_iport_s *iport,
+				struct fc_frame_header *fchdr);
+void nvfnic_admin_io_timeout(struct timer_list *t);
+void nvfnic_fcpio_nvme_itmf_cmpl_handler(struct fnic *fnic,
+					 struct fcpio_fw_req *desc);
+void nvfnic_nvme_zero_devloss_tports(struct fnic *fnic);
+bool nvfnic_queue_abort_io_req(struct fnic *fnic, int tag, u32 task_req,
+			       struct fnic_io_req *io_req);
+void nvfnic_ls_req_abort(struct nvme_fc_local_port *lport,
+			 struct nvme_fc_remote_port *rport,
+			 struct nvmefc_ls_req *lsreq);
+int nvfnic_create_queue(struct nvme_fc_local_port *lport, unsigned int idx,
+			u16 size, void **handle);
+int nvfnic_ls_req_send(struct nvme_fc_local_port *lport,
+		       struct nvme_fc_remote_port *rport,
+		       struct nvmefc_ls_req *ls_req);
+void nvfnic_ls_req_timeout(struct timer_list *t);
+uint16_t nvfnic_alloc_ls_req_oxid(struct fnic_iport_s *iport);
+struct nvfnic_ls_req *nvfnic_find_ls_req(struct fnic_tport_s *tport,
+					 uint16_t oxid);
+void nvfnic_terminate_tport_ios(struct fnic *fnic, struct fnic_tport_s *tport);
+bool _terminate_tport_ios(struct sbitmap *map, unsigned int tag, void *data);
+bool _cleanup_all_nvme_io(struct sbitmap *map, unsigned int tag, void *data);
+void nvfnic_cleanup_all_nvme_ios(struct fnic *fnic);
+int nvfnic_fcpio_send(struct nvme_fc_local_port *lport,
+		      struct nvme_fc_remote_port *rport, void *hw_queue_handle,
+		      struct nvmefc_fcp_req *fcp_req);
+void nvfnic_fcpio_ersp_cmpl_handler(struct fnic *fnic,
+				    struct fcpio_fw_req *desc, int sw_flag);
+void nvfnic_terminate_tport_ls_reqs(struct fnic *fnic,
+				    struct fnic_tport_s *tport);
+void nvfnic_terminate_tport_admin_ios(struct fnic *fnic,
+				      struct fnic_tport_s *tport);
+void nvfnic_cleanup_tport_io(struct fnic *fnic, struct fnic_tport_s *tport);
+void nvfnic_nvme_unload(struct fnic *fnic);
+int nvfnic_get_nvmef_info(struct fnic *fnic, struct fnic_nvmef_info *info);
+void nvfnic_exch_reset(struct fnic_iport_s *iport, struct fnic_tport_s *tport);
+void nvfnic_nvme_iodone_work(struct work_struct *work);
+#else
+static inline int nvfnic_add_lport(struct fnic *fnic)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void nvfnic_nvme_unload(struct fnic *fnic)
+{
+}
+
+static inline void nvfnic_flush_nvme_io_list(struct fnic *fnic)
+{
+}
+
+static inline void nvfnic_nvme_iodone_work(struct work_struct *work)
+{
+}
+
+static inline void nvfnic_exch_reset(struct fnic_iport_s *iport,
+				     struct fnic_tport_s *tport)
+{
+}
+
+static inline void nvfnic_process_ls_abts_rsp(struct fnic_iport_s *iport,
+					      struct fc_frame_header *fchdr)
+{
+}
+
+static inline void nvfnic_ls_rsp_recv(struct fnic_iport_s *iport,
+				      struct fc_frame_header *fchdr, int len)
+{
+}
+
+static inline int nvfnic_add_tport(struct fnic *fnic,
+				   struct fnic_tport_s *tport,
+				   unsigned long flags)
+{
+	return 0;
+}
+
+static inline void nvfnic_delete_tport(struct fnic_iport_s *iport,
+				       struct fnic_tport_s *tport,
+				       unsigned long flags)
+{
+}
+
+static inline void nvfnic_cleanup_all_nvme_ios(struct fnic *fnic)
+{
+}
+
+static inline void nvfnic_fcpio_nvme_fast_cmpl_handler(struct fnic *fnic,
+						       struct fcpio_fw_req *desc)
+{
+}
+
+static inline void nvfnic_fcpio_ersp_cmpl_handler(struct fnic *fnic,
+						  struct fcpio_fw_req *desc,
+						  int sw_flag)
+{
+}
+
+static inline void nvfnic_fcpio_nvme_itmf_cmpl_handler(struct fnic *fnic,
+						       struct fcpio_fw_req *desc)
+{
+}
+
+static inline int nvfnic_get_nvmef_info(struct fnic *fnic,
+					struct fnic_nvmef_info *info)
+{
+	return 0;
+}
+#endif
+
+extern const char *fnic_fcpio_status_to_str(unsigned int status);
+#endif				/* _FNIC_NVME_H */
diff --git a/drivers/scsi/fnic/fnic_res.c b/drivers/scsi/fnic/fnic_res.c
index 9801e5fbb0dd..d75d7046c7f6 100644
--- a/drivers/scsi/fnic/fnic_res.c
+++ b/drivers/scsi/fnic/fnic_res.c
@@ -22,6 +22,7 @@
 int fnic_get_vnic_config(struct fnic *fnic)
 {
 	struct vnic_fc_config *c = &fnic->config;
+	u32 role;
 	int err;
 
 #define GET_CONFIG(m) \
@@ -56,11 +57,35 @@ int fnic_get_vnic_config(struct fnic *fnic)
 	GET_CONFIG(port_down_io_retries);
 	GET_CONFIG(luns_per_tgt);
 	GET_CONFIG(intr_mode);
+	GET_CONFIG(lun_queue_depth);
+	GET_CONFIG(io_timeout_retry);
 	GET_CONFIG(wq_copy_count);
 
-	if ((c->flags & (VFCF_FC_INITIATOR)) == 0) {
-		dev_info(&fnic->pdev->dev, "vNIC role not defined (def role: FC Init)\n");
+	role = c->flags & FNIC_ROLE_CONFIG_MASK;
+	switch (role) {
+	case 0:
+		dev_info(&fnic->pdev->dev,
+			 "vNIC role not defined (def role: FC Init)\n");
 		c->flags |= VFCF_FC_INITIATOR;
+		break;
+	case VFCF_FC_INITIATOR:
+	case VFCF_FC_NVME_INITIATOR:
+		break;
+	case VFCF_FC_TARGET:
+		dev_info(&fnic->pdev->dev,
+			 "vNIC role is FC Target (unsupported)\n");
+		break;
+	case VFCF_FC_NVME_TARGET:
+		dev_info(&fnic->pdev->dev,
+			 "vNIC role is FC-NVMe Target (unsupported)\n");
+		break;
+	default:
+		dev_info(&fnic->pdev->dev,
+			 "vNIC role not supported (0x%x), defaulting to FC Init\n",
+			 role);
+		c->flags &= ~FNIC_ROLE_CONFIG_MASK;
+		c->flags |= VFCF_FC_INITIATOR;
+		break;
 	}
 
 	c->wq_enet_desc_count =
@@ -163,6 +188,8 @@ int fnic_get_vnic_config(struct fnic *fnic)
 		     c->port_down_io_retries, c->port_down_timeout);
 	dev_info(&fnic->pdev->dev, "fNIC wq_copy_count: %d\n", c->wq_copy_count);
 	dev_info(&fnic->pdev->dev, "fNIC intr mode: %d\n", c->intr_mode);
+	dev_info(&fnic->pdev->dev, "fNIC role flags: 0x%x\n",
+			(c->flags & FNIC_ROLE_CONFIG_MASK));
 
 	return 0;
 }
diff --git a/drivers/scsi/fnic/fnic_res.h b/drivers/scsi/fnic/fnic_res.h
index 92a2fcfd3ea9..12a2a356dc13 100644
--- a/drivers/scsi/fnic/fnic_res.h
+++ b/drivers/scsi/fnic/fnic_res.h
@@ -126,7 +126,8 @@ static inline void fnic_queue_wq_copy_desc_itmf(struct vnic_wq_copy *wq,
 	desc->u.itmf.tm_req = tm_req;       /* SCSI Task Management request */
 	desc->u.itmf.t_tag = tm_id;         /* tag of fcpio to be aborted */
 	desc->u.itmf._resvd = 0;
-	memcpy(desc->u.itmf.lun, lun, LUN_ADDRESS);  /* LUN address */
+	if (lun)
+		memcpy(desc->u.itmf.lun, lun, LUN_ADDRESS);  /* LUN address */
 	desc->u.itmf._resvd1 = 0;
 	hton24(desc->u.itmf.d_id, d_id);    /* FC vNIC only: Target D_ID */
 	desc->u.itmf.r_a_tov = r_a_tov;     /* FC vNIC only: R_A_TOV in msec */
@@ -222,6 +223,35 @@ static inline void fnic_queue_rq_desc(struct vnic_rq *rq,
 	vnic_rq_post(rq, os_buf, 0, dma_addr, len);
 }
 
+static inline void fnic_queue_wq_copy_desc_nvme_io(struct vnic_wq_copy *wq,
+						   u32 req_id,
+						   u8 spl_flags,
+						   u32 sgl_cnt,
+						   u64 sgl_addr,
+						   u8 flags, u8 *nvme_cmd_iu,
+						   u16 cmd_len,
+						   u32 data_len,
+						   u32 d_id, u32 mss,
+						   u32 ratov, u32 edtov)
+{
+	struct fcpio_host_req *desc = vnic_wq_copy_next_desc(wq);
+
+	desc->hdr.type = FCPIO_NVME_CMD; /* enum fcpio_type */
+	desc->hdr.status = 0; /* header status entry */
+	desc->hdr._resvd = 0; /* reserved */
+	desc->hdr.tag.u.req_id = req_id; /* id for this request */
+	desc->u.nvcmnd.sgl_cnt = sgl_cnt; /* scatter-gather list count */
+	desc->u.nvcmnd.sgl_addr = sgl_addr; /* scatter-gather list addr */
+	desc->u.nvcmnd._resvd1 = 0; /* reserved: should be 0 */
+	desc->u.nvcmnd.flags = flags; /* command flags */
+	memset(desc->u.nvcmnd.nvme_cmnd, 0, NVME_CMD_SZ);
+	memcpy(desc->u.nvcmnd.nvme_cmnd, nvme_cmd_iu, cmd_len); /* SCSI CDB */
+	desc->u.nvcmnd.cmd_len = cmd_len;
+	desc->u.nvcmnd.data_len = data_len; /* length of data expected */
+	hton24(desc->u.nvcmnd.d_id, d_id); /* FC vNIC only: Target D_ID */
+
+	vnic_wq_copy_post(wq);
+}
 
 struct fnic;
 
diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c
index 6ee3c559e129..9607684bc610 100644
--- a/drivers/scsi/fnic/fnic_scsi.c
+++ b/drivers/scsi/fnic/fnic_scsi.c
@@ -96,7 +96,7 @@ static const char *fnic_ioreq_state_to_str(unsigned int state)
 	return fnic_ioreq_state_str[state];
 }
 
-static const char *fnic_fcpio_status_to_str(unsigned int status)
+const char *fnic_fcpio_status_to_str(unsigned int status)
 {
 	if (status >= ARRAY_SIZE(fcpio_status_str) || !fcpio_status_str[status])
 		return "unknown";
@@ -145,10 +145,11 @@ unsigned int fnic_count_ioreqs(struct fnic *fnic, u32 portid)
 {
 	unsigned int count = 0;
 
-	fnic_scsi_io_iter(fnic, fnic_count_portid_ioreqs_iter,
-				&portid, &count);
+	if (IS_FNIC_FCP_INITIATOR(fnic))
+		fnic_scsi_io_iter(fnic, fnic_count_portid_ioreqs_iter,
+					&portid, &count);
 
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 		      "portid = 0x%x count = %u\n", portid, count);
 	return count;
 }
@@ -180,13 +181,13 @@ fnic_count_lun_ioreqs(struct fnic *fnic, struct scsi_device *scsi_device)
 	fnic_scsi_io_iter(fnic, fnic_count_lun_ioreqs_iter,
 				scsi_device, &count);
 
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 		      "lun = %p count = %u\n", scsi_device, count);
 	return count;
 }
 
 /* Free up Copy Wq descriptors. Called with copy_wq lock held */
-static int free_wq_copy_descs(struct fnic *fnic, struct vnic_wq_copy *wq, unsigned int hwq)
+int free_wq_copy_descs(struct fnic *fnic, struct vnic_wq_copy *wq, unsigned int hwq)
 {
 	/* if no Ack received from firmware, then nothing to clean */
 	if (!fnic->fw_ack_recd[hwq])
@@ -268,7 +269,7 @@ int fnic_fw_reset_handler(struct fnic *fnic)
 	if (!vnic_wq_copy_desc_avail(wq))
 		ret = -EAGAIN;
 	else {
-		FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 			  "ioreq_count: %u\n", ioreq_count);
 		fnic_queue_wq_copy_desc_fw_reset(wq, SCSI_NO_TAG);
 		atomic64_inc(&fnic->fnic_stats.fw_stats.active_fw_reqs);
@@ -283,11 +284,11 @@ int fnic_fw_reset_handler(struct fnic *fnic)
 
 	if (!ret) {
 		atomic64_inc(&fnic->fnic_stats.reset_stats.fw_resets);
-		FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 				"Issued fw reset\n");
 	} else {
 		fnic_clear_state_flags(fnic, FNIC_FLAGS_FWRESET);
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 				"Failed to issue fw reset\n");
 	}
 
@@ -326,13 +327,13 @@ int fnic_flogi_reg_handler(struct fnic *fnic, u32 fc_id)
 						fc_id, gw_mac,
 						fnic->iport.fpma,
 						iport->r_a_tov, iport->e_d_tov);
-		FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 			      "FLOGI FIP reg issued fcid: 0x%x src %p dest %p\n",
 				  fc_id, fnic->iport.fpma, gw_mac);
 	} else {
 		fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG,
 						  format, fc_id, gw_mac);
-		FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 			"FLOGI reg issued fcid 0x%x dest %p\n",
 			fc_id, gw_mac);
 	}
@@ -413,7 +414,7 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic,
 		free_wq_copy_descs(fnic, wq, hwq);
 
 	if (unlikely(!vnic_wq_copy_desc_avail(wq))) {
-		FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 			  "fnic_queue_wq_copy_desc failure - no descriptors\n");
 		atomic64_inc(&misc_stats->io_cpwq_alloc_failures);
 		return SCSI_MLQUEUE_HOST_BUSY;
@@ -478,7 +479,7 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost,
 
 	rport = starget_to_rport(scsi_target(sc->device));
 	if (!rport) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 				"returning DID_NO_CONNECT for IO as rport is NULL\n");
 		sc->result = DID_NO_CONNECT << 16;
 		done(sc);
@@ -487,7 +488,7 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost,
 
 	ret = fc_remote_port_chkready(rport);
 	if (ret) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 				"rport is not ready\n");
 		atomic64_inc(&fnic_stats->misc_stats.tport_not_ready);
 		sc->result = ret;
@@ -501,7 +502,7 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost,
 
 	if (iport->state != FNIC_IPORT_STATE_READY) {
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 					  "returning DID_NO_CONNECT for IO as iport state: %d\n",
 					  iport->state);
 		sc->result = DID_NO_CONNECT << 16;
@@ -515,13 +516,13 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost,
 	rdd_data = rport->dd_data;
 	tport = rdd_data->tport;
 	if (!tport || (rdd_data->iport != iport)) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 					  "dd_data not yet set in SCSI for rport portid: 0x%x\n",
 					  rport->port_id);
 		tport = fnic_find_tport_by_fcid(iport, rport->port_id);
 		if (!tport) {
 			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-			FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+			FNIC_SCSI_DBG(KERN_ERR, fnic,
 						  "returning DID_BUS_BUSY for IO as tport not found for: 0x%x\n",
 						  rport->port_id);
 			sc->result = DID_BUS_BUSY << 16;
@@ -544,7 +545,7 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost,
 	if ((tport->state != FDLS_TGT_STATE_READY)
 		&& (tport->state != FDLS_TGT_STATE_ADISC)) {
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 					  "returning DID_NO_CONNECT for IO as tport state: %d\n",
 					  tport->state);
 		sc->result = DID_NO_CONNECT << 16;
@@ -564,7 +565,7 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost,
 
 	if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_FWRESET))) {
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 		  "fnic flags FW reset: 0x%lx. Returning SCSI_MLQUEUE_HOST_BUSY\n",
 		  fnic->state_flags);
 		return SCSI_MLQUEUE_HOST_BUSY;
@@ -704,7 +705,7 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost,
 	atomic_dec(&tport->in_flight);
 
 	if (lun0_delay) {
-		FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 					  "LUN0 delay\n");
 		mdelay(LUN0_DELAY_TIME);
 	}
@@ -732,7 +733,10 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
 	atomic64_inc(&reset_stats->fw_reset_completions);
 
 	/* Clean up all outstanding io requests */
-	fnic_cleanup_io(fnic, SCSI_NO_TAG);
+	if (IS_FNIC_FCP_INITIATOR(fnic))
+		fnic_cleanup_io(fnic, SCSI_NO_TAG);
+	else if (IS_FNIC_NVME_INITIATOR(fnic))
+		nvfnic_cleanup_all_nvme_ios(fnic);
 
 	atomic64_set(&fnic->fnic_stats.fw_stats.active_fw_reqs, 0);
 	atomic64_set(&fnic->fnic_stats.io_stats.active_ios, 0);
@@ -744,12 +748,12 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
 	if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE) {
 		/* Check status of reset completion */
 		if (!hdr_status) {
-			FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_SCSI_DBG(KERN_INFO, fnic,
 					"reset cmpl success\n");
 			/* Ready to send flogi out */
 			fnic->state = FNIC_IN_ETH_MODE;
 		} else {
-			FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+			FNIC_SCSI_DBG(KERN_ERR, fnic,
 				"reset failed with header status: %s\n",
 				fnic_fcpio_status_to_str(hdr_status));
 
@@ -758,7 +762,7 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
 			ret = -1;
 		}
 	} else {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"Unexpected state while processing reset completion: %s\n",
 			fnic_state_to_str(fnic->state));
 		atomic64_inc(&reset_stats->fw_reset_failures);
@@ -810,19 +814,18 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
 
 		/* Check flogi registration completion status */
 		if (!hdr_status) {
-			FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+			FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 				      "FLOGI reg succeeded\n");
 			fnic->state = FNIC_IN_FC_MODE;
 		} else {
-			FNIC_SCSI_DBG(KERN_DEBUG,
-				      fnic->host, fnic->fnic_num,
+			FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 				      "fnic flogi reg failed: %s\n",
 				      fnic_fcpio_status_to_str(hdr_status));
 			fnic->state = FNIC_IN_ETH_MODE;
 			ret = -1;
 		}
 	} else {
-		FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 			      "Unexpected fnic state %s while"
 			      " processing flogi reg completion\n",
 			      fnic_state_to_str(fnic->state));
@@ -895,7 +898,7 @@ static inline void fnic_fcpio_ack_handler(struct fnic *fnic,
 
 	spin_unlock_irqrestore(&fnic->wq_copy_lock[wq_index], flags);
 	FNIC_TRACE(fnic_fcpio_ack_handler,
-		  fnic->host->host_no, 0, 0, ox_id_tag[2], ox_id_tag[3],
+		  fnic->fnic_num, 0, 0, ox_id_tag[2], ox_id_tag[3],
 		  ox_id_tag[4], ox_id_tag[5]);
 }
 
@@ -933,19 +936,19 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic, unsigned int cq_ind
 	hwq = blk_mq_unique_tag_to_hwq(mqtag);
 
 	if (hwq != cq_index) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ",
 			hwq, mqtag, tag, cq_index);
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hdr status: %s icmnd completion on the wrong queue\n",
 			fnic_fcpio_status_to_str(hdr_status));
 	}
 
 	if (tag >= fnic->fnic_max_tag_id) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ",
 			hwq, mqtag, tag, cq_index);
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hdr status: %s Out of range tag\n",
 			fnic_fcpio_status_to_str(hdr_status));
 		return;
@@ -957,7 +960,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic, unsigned int cq_ind
 	if (!sc) {
 		atomic64_inc(&fnic_stats->io_stats.sc_null);
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
-		shost_printk(KERN_ERR, fnic->host,
+		fnic_printk(KERN_ERR, fnic,
 			  "icmnd_cmpl sc is null - "
 			  "hdr status = %s tag = 0x%x desc = 0x%p\n",
 			  fnic_fcpio_status_to_str(hdr_status), id, desc);
@@ -985,7 +988,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic, unsigned int cq_ind
 		atomic64_inc(&fnic_stats->io_stats.ioreq_null);
 		fnic_priv(sc)->flags |= FNIC_IO_REQ_NULL;
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
-		shost_printk(KERN_ERR, fnic->host,
+		fnic_printk(KERN_ERR, fnic,
 			  "icmnd_cmpl io_req is null - "
 			  "hdr status = %s tag = 0x%x sc 0x%p\n",
 			  fnic_fcpio_status_to_str(hdr_status), id, sc);
@@ -1012,7 +1015,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic, unsigned int cq_ind
 		if(FCPIO_ABORTED == hdr_status)
 			fnic_priv(sc)->flags |= FNIC_IO_ABORTED;
 
-		FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 			"icmnd_cmpl abts pending "
 			  "hdr status = %s tag = 0x%x sc = 0x%p "
 			  "scsi_status = %x residual = %d\n",
@@ -1044,7 +1047,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic, unsigned int cq_ind
 		if (icmnd_cmpl->scsi_status == SAM_STAT_TASK_SET_FULL)
 			atomic64_inc(&fnic_stats->misc_stats.queue_fulls);
 
-		FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 				"xfer_len: %llu", xfer_len);
 		break;
 
@@ -1107,7 +1110,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic, unsigned int cq_ind
 
 	if (hdr_status != FCPIO_SUCCESS) {
 		atomic64_inc(&fnic_stats->io_stats.io_failures);
-		shost_printk(KERN_ERR, fnic->host, "hdr status = %s\n",
+		fnic_printk(KERN_ERR, fnic, "hdr status = %s\n",
 			     fnic_fcpio_status_to_str(hdr_status));
 	}
 
@@ -1200,27 +1203,27 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde
 	hwq = blk_mq_unique_tag_to_hwq(id & FNIC_TAG_MASK);
 
 	if (hwq != cq_index) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ",
 			hwq, mqtag, tag, cq_index);
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hdr status: %s ITMF completion on the wrong queue\n",
 			fnic_fcpio_status_to_str(hdr_status));
 	}
 
 	if (tag > fnic->fnic_max_tag_id) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ",
 			hwq, mqtag, tag, cq_index);
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hdr status: %s Tag out of range\n",
 			fnic_fcpio_status_to_str(hdr_status));
 		return;
 	}  else if ((tag == fnic->fnic_max_tag_id) && !(id & FNIC_TAG_DEV_RST)) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ",
 			hwq, mqtag, tag, cq_index);
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hdr status: %s Tag out of range\n",
 			fnic_fcpio_status_to_str(hdr_status));
 		return;
@@ -1243,7 +1246,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde
 	if (!sc) {
 		atomic64_inc(&fnic_stats->io_stats.sc_null);
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
-		shost_printk(KERN_ERR, fnic->host,
+		fnic_printk(KERN_ERR, fnic,
 			  "itmf_cmpl sc is null - hdr status = %s tag = 0x%x\n",
 			  fnic_fcpio_status_to_str(hdr_status), tag);
 		return;
@@ -1255,7 +1258,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde
 		atomic64_inc(&fnic_stats->io_stats.ioreq_null);
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
 		fnic_priv(sc)->flags |= FNIC_IO_ABT_TERM_REQ_NULL;
-		shost_printk(KERN_ERR, fnic->host,
+		fnic_printk(KERN_ERR, fnic,
 			  "itmf_cmpl io_req is null - "
 			  "hdr status = %s tag = 0x%x sc 0x%p\n",
 			  fnic_fcpio_status_to_str(hdr_status), tag, sc);
@@ -1266,7 +1269,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde
 	if ((id & FNIC_TAG_ABORT) && (id & FNIC_TAG_DEV_RST)) {
 		/* Abort and terminate completion of device reset req */
 		/* REVISIT : Add asserts about various flags */
-		FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 			"hwq: %d mqtag: 0x%x tag: 0x%x hst: %s Abt/term completion received\n",
 			hwq, mqtag, tag,
 			fnic_fcpio_status_to_str(hdr_status));
@@ -1278,7 +1281,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
 	} else if (id & FNIC_TAG_ABORT) {
 		/* Completion of abort cmd */
-		shost_printk(KERN_DEBUG, fnic->host,
+		fnic_printk(KERN_DEBUG, fnic,
 			"hwq: %d mqtag: 0x%x tag: 0x%x Abort header status: %s\n",
 			hwq, mqtag, tag,
 			fnic_fcpio_status_to_str(hdr_status));
@@ -1293,7 +1296,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde
 					&term_stats->terminate_fw_timeouts);
 			break;
 		case FCPIO_ITMF_REJECTED:
-			FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+			FNIC_SCSI_DBG(KERN_INFO, fnic,
 				"abort reject recd. id %d\n",
 				(int)(id & FNIC_TAG_MASK));
 			break;
@@ -1328,7 +1331,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde
 		if (!(fnic_priv(sc)->flags & (FNIC_IO_ABORTED | FNIC_IO_DONE)))
 			atomic64_inc(&misc_stats->no_icmnd_itmf_cmpls);
 
-		FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 			      "abts cmpl recd. id %d status %s\n",
 			      (int)(id & FNIC_TAG_MASK),
 			      fnic_fcpio_status_to_str(hdr_status));
@@ -1341,11 +1344,11 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde
 		if (io_req->abts_done) {
 			complete(io_req->abts_done);
 			spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
-			shost_printk(KERN_INFO, fnic->host,
+			fnic_printk(KERN_INFO, fnic,
 					"hwq: %d mqtag: 0x%x tag: 0x%x Waking up abort thread\n",
 					hwq, mqtag, tag);
 		} else {
-			FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+			FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 				"hwq: %d mqtag: 0x%x tag: 0x%x hst: %s Completing IO\n",
 				hwq, mqtag,
 				tag, fnic_fcpio_status_to_str(hdr_status));
@@ -1376,7 +1379,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde
 		}
 	} else if (id & FNIC_TAG_DEV_RST) {
 		/* Completion of device reset */
-		shost_printk(KERN_INFO, fnic->host,
+		fnic_printk(KERN_INFO, fnic,
 			"hwq: %d mqtag: 0x%x tag: 0x%x DR hst: %s\n",
 			hwq, mqtag,
 			tag, fnic_fcpio_status_to_str(hdr_status));
@@ -1388,7 +1391,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde
 				  sc->device->host->host_no, id, sc,
 				  jiffies_to_msecs(jiffies - start_time),
 				  desc, 0, fnic_flags_and_state(sc));
-			FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+			FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 				"hwq: %d mqtag: 0x%x tag: 0x%x hst: %s Terminate pending\n",
 				hwq, mqtag,
 				tag, fnic_fcpio_status_to_str(hdr_status));
@@ -1401,7 +1404,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde
 				  sc->device->host->host_no, id, sc,
 				  jiffies_to_msecs(jiffies - start_time),
 				  desc, 0, fnic_flags_and_state(sc));
-			FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+			FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 				"dev reset cmpl recd after time out. "
 				"id %d status %s\n",
 				(int)(id & FNIC_TAG_MASK),
@@ -1410,7 +1413,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde
 		}
 		fnic_priv(sc)->state = FNIC_IOREQ_CMD_COMPLETE;
 		fnic_priv(sc)->flags |= FNIC_DEV_RST_DONE;
-		FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 			"hwq: %d mqtag: 0x%x tag: 0x%x hst: %s DR completion received\n",
 			hwq, mqtag,
 			tag, fnic_fcpio_status_to_str(hdr_status));
@@ -1419,7 +1422,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
 
 	} else {
-		shost_printk(KERN_ERR, fnic->host,
+		fnic_printk(KERN_ERR, fnic,
 			"%s: Unexpected itmf io state: hwq: %d tag 0x%x %s\n",
 			__func__, hwq, id, fnic_ioreq_state_to_str(fnic_priv(sc)->state));
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
@@ -1443,6 +1446,7 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev,
 	case FCPIO_FLOGI_REG_CMPL: /* fw completed flogi_reg */
 	case FCPIO_FLOGI_FIP_REG_CMPL: /* fw completed flogi_fip_reg */
 	case FCPIO_RESET_CMPL: /* fw completed reset */
+	case FCPIO_NVME_ERSP_HW_CMPL: /* fw completed NVMe ERSP */
 		atomic64_dec(&fnic->fnic_stats.fw_stats.active_fw_reqs);
 		break;
 	default:
@@ -1457,11 +1461,22 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev,
 		break;
 
 	case FCPIO_ICMND_CMPL: /* fw completed a command */
-		fnic_fcpio_icmnd_cmpl_handler(fnic, cq_index, desc);
+		if (IS_FNIC_FCP_INITIATOR(fnic))
+			fnic_fcpio_icmnd_cmpl_handler(fnic, cq_index, desc);
+		else if (IS_FNIC_NVME_INITIATOR(fnic))
+			nvfnic_fcpio_nvme_fast_cmpl_handler(fnic, desc);
+		break;
+
+	case FCPIO_NVME_ERSP_HW_CMPL: /* fw completed NVMe ERSP */
+		if (IS_FNIC_NVME_INITIATOR(fnic))
+			nvfnic_fcpio_ersp_cmpl_handler(fnic, desc, 1);
 		break;
 
 	case FCPIO_ITMF_CMPL: /* fw completed itmf (abort cmd, lun reset)*/
-		fnic_fcpio_itmf_cmpl_handler(fnic, cq_index, desc);
+		if (IS_FNIC_FCP_INITIATOR(fnic))
+			fnic_fcpio_itmf_cmpl_handler(fnic, cq_index, desc);
+		else if (IS_FNIC_NVME_INITIATOR(fnic))
+			nvfnic_fcpio_nvme_itmf_cmpl_handler(fnic, desc);
 		break;
 
 	case FCPIO_FLOGI_REG_CMPL: /* fw completed flogi_reg */
@@ -1474,7 +1489,7 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev,
 		break;
 
 	default:
-		FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 			      "firmware completion type %d\n",
 			      desc->hdr.type);
 		break;
@@ -1535,7 +1550,7 @@ static bool fnic_cleanup_io_iter(struct scsi_cmnd *sc, void *data)
 	io_req = fnic_priv(sc)->io_req;
 	if (!io_req) {
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hwq: %d mqtag: 0x%x tag: 0x%x flags: 0x%x No ioreq. Returning\n",
 			hwq, mqtag, tag, fnic_priv(sc)->flags);
 		return true;
@@ -1573,7 +1588,7 @@ static bool fnic_cleanup_io_iter(struct scsi_cmnd *sc, void *data)
 	mempool_free(io_req, fnic->io_req_pool);
 
 	sc->result = DID_TRANSPORT_DISRUPTED << 16;
-	FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_ERR, fnic,
 	"mqtag: 0x%x tag: 0x%x sc: 0x%p duration = %lu DID_TRANSPORT_DISRUPTED\n",
 		mqtag, tag, sc, (jiffies - start_time));
 
@@ -1605,7 +1620,7 @@ static void fnic_cleanup_io(struct fnic *fnic, int exclude_id)
 	struct scsi_cmnd *sc = NULL;
 
 	io_count = fnic_count_all_ioreqs(fnic);
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 				  "Outstanding ioreq count: %d active io count: %lld Waiting\n",
 				  io_count,
 				  atomic64_read(&fnic->fnic_stats.io_stats.active_ios));
@@ -1630,7 +1645,7 @@ static void fnic_cleanup_io(struct fnic *fnic, int exclude_id)
 	spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
 
 	while ((io_count = fnic_count_all_ioreqs(fnic))) {
-		FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 		  "Outstanding ioreq count: %d active io count: %lld Waiting\n",
 		  io_count,
 		  atomic64_read(&fnic->fnic_stats.io_stats.active_ios));
@@ -1650,6 +1665,15 @@ void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
 	unsigned long start_time = 0;
 	uint16_t hwq;
 
+	/*
+	 * Clean up outstanding NVMe requests if firmware reset did not
+	 * complete them before WQ copy cleanup.
+	 */
+	if (IS_FNIC_NVME_INITIATOR(fnic)) {
+		nvfnic_cleanup_all_nvme_ios(fnic);
+		return;
+	}
+
 	/* get the tag reference */
 	fcpio_tag_id_dec(&desc->hdr.tag, &id);
 	id &= FNIC_TAG_MASK;
@@ -1686,7 +1710,7 @@ void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
 
 wq_copy_cleanup_scsi_cmd:
 	sc->result = DID_NO_CONNECT << 16;
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, "wq_copy_cleanup_handler:"
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic, "wq_copy_cleanup_handler:"
 		      " DID_NO_CONNECT\n");
 
 	FNIC_TRACE(fnic_wq_copy_cleanup_handler,
@@ -1730,7 +1754,7 @@ static inline int fnic_queue_abort_io_req(struct fnic *fnic, int tag,
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
 		atomic_dec(&fnic->in_flight);
 		atomic_dec(&tport->in_flight);
-		FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 			"fnic_queue_abort_io_req: failure: no descriptors\n");
 		atomic64_inc(&misc_stats->abts_cpwq_alloc_failures);
 		return 1;
@@ -1775,7 +1799,7 @@ static bool fnic_rport_abort_io_iter(struct scsi_cmnd *sc, void *data)
 	hwq = blk_mq_unique_tag_to_hwq(abt_tag);
 
 	if (!sc) {
-		FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 					  "sc is NULL abt_tag: 0x%x hwq: %d\n", abt_tag, hwq);
 		return true;
 	}
@@ -1789,7 +1813,7 @@ static bool fnic_rport_abort_io_iter(struct scsi_cmnd *sc, void *data)
 
 	if ((fnic_priv(sc)->flags & FNIC_DEVICE_RESET) &&
 	    !(fnic_priv(sc)->flags & FNIC_DEV_RST_ISSUED)) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hwq: %d abt_tag: 0x%x flags: 0x%x Device reset is not pending\n",
 			hwq, abt_tag, fnic_priv(sc)->flags);
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
@@ -1806,16 +1830,16 @@ static bool fnic_rport_abort_io_iter(struct scsi_cmnd *sc, void *data)
 	}
 
 	if (io_req->abts_done) {
-		shost_printk(KERN_ERR, fnic->host,
+		fnic_printk(KERN_ERR, fnic,
 			"fnic_rport_exch_reset: io_req->abts_done is set state is %s\n",
 			fnic_ioreq_state_to_str(fnic_priv(sc)->state));
 	}
 
 	if (!(fnic_priv(sc)->flags & FNIC_IO_ISSUED)) {
-		shost_printk(KERN_ERR, fnic->host,
+		fnic_printk(KERN_ERR, fnic,
 			"rport_exch_reset IO not yet issued %p abt_tag 0x%x",
 			sc, abt_tag);
-		shost_printk(KERN_ERR, fnic->host,
+		fnic_printk(KERN_ERR, fnic,
 			"flags %x state %d\n", fnic_priv(sc)->flags,
 			fnic_priv(sc)->state);
 	}
@@ -1826,13 +1850,13 @@ static bool fnic_rport_abort_io_iter(struct scsi_cmnd *sc, void *data)
 	if (fnic_priv(sc)->flags & FNIC_DEVICE_RESET) {
 		atomic64_inc(&reset_stats->device_reset_terminates);
 		abt_tag |= FNIC_TAG_DEV_RST;
-		FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 					  "dev reset sc 0x%p\n", sc);
 	}
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 		      "fnic_rport_exch_reset: dev rst sc 0x%p\n", sc);
 	WARN_ON_ONCE(io_req->abts_done);
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 		      "fnic_rport_reset_exch: Issuing abts\n");
 
 	spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
@@ -1850,7 +1874,7 @@ static bool fnic_rport_abort_io_iter(struct scsi_cmnd *sc, void *data)
 		 * lun reset
 		 */
 		spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hwq: %d abt_tag: 0x%x flags: 0x%x Queuing abort failed\n",
 			hwq, abt_tag, fnic_priv(sc)->flags);
 		if (fnic_priv(sc)->state == FNIC_IOREQ_ABTS_PENDING)
@@ -1881,7 +1905,7 @@ void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id)
 		.term_cnt = 0,
 	};
 
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 				  "fnic rport exchange reset for tport: 0x%06x\n",
 				  port_id);
 
@@ -1889,7 +1913,7 @@ void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id)
 		return;
 
 	io_count = fnic_count_ioreqs(fnic, port_id);
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 				  "Starting terminates: rport:0x%x  portid-io-count: %d active-io-count: %lld\n",
 				  port_id, io_count,
 				  atomic64_read(&fnic->fnic_stats.io_stats.active_ios));
@@ -1915,7 +1939,7 @@ void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id)
 	while ((io_count = fnic_count_ioreqs(fnic, port_id)))
 		schedule_timeout(msecs_to_jiffies(1000));
 
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 				  "rport: 0x%x remaining portid-io-count: %d ",
 				  port_id, io_count);
 }
@@ -2040,10 +2064,10 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 	tport = rdd_data->tport;
 
 	if (!tport) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			  "Abort cmd called after tport delete! rport fcid: 0x%x",
 			  rport->port_id);
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			  "lun: %llu hwq: 0x%x mqtag: 0x%x Op: 0x%x flags: 0x%x\n",
 			  sc->device->lun, hwq, mqtag,
 			  sc->cmnd[0], fnic_priv(sc)->flags);
@@ -2052,18 +2076,18 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 		goto fnic_abort_cmd_end;
 	}
 
-	FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_INFO, fnic,
 	  "Abort cmd called rport fcid: 0x%x lun: %llu hwq: 0x%x mqtag: 0x%x",
 	  rport->port_id, sc->device->lun, hwq, mqtag);
 
-	FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_INFO, fnic,
 				  "Op: 0x%x flags: 0x%x\n",
 				  sc->cmnd[0],
 				  fnic_priv(sc)->flags);
 
 	if (iport->state != FNIC_IPORT_STATE_READY) {
 		atomic64_inc(&fnic_stats->misc_stats.iport_not_ready);
-		FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 					  "iport NOT in READY state");
 		ret = FAILED;
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
@@ -2072,7 +2096,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 
 	if ((tport->state != FDLS_TGT_STATE_READY) &&
 		(tport->state != FDLS_TGT_STATE_ADISC)) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 					  "tport state: %d\n", tport->state);
 		ret = FAILED;
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
@@ -2123,7 +2147,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 	else
 		atomic64_inc(&abts_stats->abort_issued_greater_than_60_sec);
 
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 		"CDB Opcode: 0x%02x Abort issued time: %lu msec\n",
 		sc->cmnd[0], abt_issued_time);
 	/*
@@ -2214,7 +2238,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 
 	if (!(fnic_priv(sc)->flags & (FNIC_IO_ABORTED | FNIC_IO_DONE))) {
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			      "Issuing host reset due to out of order IO\n");
 
 		ret = FAILED;
@@ -2262,7 +2286,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 		  (u64)sc->cmnd[4] << 8 | sc->cmnd[5]),
 		  fnic_flags_and_state(sc));
 
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 		      "Returning from abort cmd type %x %s\n", task_req,
 		      (ret == SUCCESS) ?
 		      "SUCCESS" : "FAILED");
@@ -2303,7 +2327,7 @@ static inline int fnic_queue_dr_io_req(struct fnic *fnic,
 		free_wq_copy_descs(fnic, wq, hwq);
 
 	if (!vnic_wq_copy_desc_avail(wq)) {
-		FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 			  "queue_dr_io_req failure - no descriptors\n");
 		atomic64_inc(&misc_stats->devrst_cpwq_alloc_failures);
 		ret = -EAGAIN;
@@ -2371,7 +2395,7 @@ static bool fnic_pending_aborts_iter(struct scsi_cmnd *sc, void *data)
 	 * Found IO that is still pending with firmware and
 	 * belongs to the LUN that we are resetting
 	 */
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 		      "Found IO in %s on lun\n",
 		      fnic_ioreq_state_to_str(fnic_priv(sc)->state));
 
@@ -2381,14 +2405,14 @@ static bool fnic_pending_aborts_iter(struct scsi_cmnd *sc, void *data)
 	}
 	if ((fnic_priv(sc)->flags & FNIC_DEVICE_RESET) &&
 	    (!(fnic_priv(sc)->flags & FNIC_DEV_RST_ISSUED))) {
-		FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 			      "dev rst not pending sc 0x%p\n", sc);
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
 		return true;
 	}
 
 	if (io_req->abts_done)
-		shost_printk(KERN_ERR, fnic->host,
+		fnic_printk(KERN_ERR, fnic,
 			     "%s: io_req->abts_done is set state is %s\n",
 			     __func__, fnic_ioreq_state_to_str(fnic_priv(sc)->state));
 	old_ioreq_state = fnic_priv(sc)->state;
@@ -2404,7 +2428,7 @@ static bool fnic_pending_aborts_iter(struct scsi_cmnd *sc, void *data)
 	BUG_ON(io_req->abts_done);
 
 	if (fnic_priv(sc)->flags & FNIC_DEVICE_RESET) {
-		FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 			      "dev rst sc 0x%p\n", sc);
 	}
 
@@ -2426,7 +2450,7 @@ static bool fnic_pending_aborts_iter(struct scsi_cmnd *sc, void *data)
 			fnic_priv(sc)->state = old_ioreq_state;
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
 		iter_data->ret = FAILED;
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 			"hwq: %d abt_tag: 0x%lx Abort could not be queued\n",
 			hwq, abt_tag);
 		return false;
@@ -2518,7 +2542,7 @@ static int fnic_clean_pending_aborts(struct fnic *fnic,
 		ret = 1;
 
 clean_pending_aborts_end:
-	FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_INFO, fnic,
 			"exit status: %d\n", ret);
 	return ret;
 }
@@ -2568,7 +2592,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	rport = starget_to_rport(scsi_target(sc->device));
 
 	spin_lock_irqsave(&fnic->fnic_lock, flags);
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 		"fcid: 0x%x lun: %llu hwq: %d mqtag: 0x%x flags: 0x%x Device reset\n",
 		rport->port_id, sc->device->lun, hwq, mqtag,
 		fnic_priv(sc)->flags);
@@ -2576,7 +2600,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	rdd_data = rport->dd_data;
 	tport = rdd_data->tport;
 	if (!tport) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 		  "Dev rst called after tport delete! rport fcid: 0x%x lun: %llu\n",
 		  rport->port_id, sc->device->lun);
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
@@ -2585,7 +2609,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 
 	if (iport->state != FNIC_IPORT_STATE_READY) {
 		atomic64_inc(&fnic_stats->misc_stats.iport_not_ready);
-		FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_INFO, fnic,
 					  "iport NOT in READY state");
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 		goto fnic_device_reset_end;
@@ -2593,7 +2617,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 
 	if ((tport->state != FDLS_TGT_STATE_READY) &&
 		(tport->state != FDLS_TGT_STATE_ADISC)) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 					  "tport state: %d\n", tport->state);
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 		goto fnic_device_reset_end;
@@ -2656,7 +2680,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	fnic_priv(sc)->lr_status = FCPIO_INVALID_CODE;
 	spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
 
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, "TAG %x\n", mqtag);
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic, "TAG %x\n", mqtag);
 
 	/*
 	 * issue the device reset, if enqueue failed, clean up the ioreq
@@ -2707,13 +2731,13 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	io_req = fnic_priv(sc)->io_req;
 	if (!io_req) {
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags);
-		FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 				"io_req is null mqtag 0x%x sc 0x%p\n", mqtag, sc);
 		goto fnic_device_reset_end;
 	}
 
 	if (exit_dr) {
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 					  "Host reset called for fnic. Exit device reset\n");
 		io_req->dr_done = NULL;
 		goto fnic_device_reset_clean;
@@ -2728,7 +2752,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	 */
 	if (status == FCPIO_INVALID_CODE) {
 		atomic64_inc(&reset_stats->device_reset_timeouts);
-		FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 			      "Device reset timed out\n");
 		fnic_priv(sc)->flags |= FNIC_DEV_RST_TIMED_OUT;
 		int_to_scsilun(sc->device->lun, &fc_lun);
@@ -2740,8 +2764,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	/* Completed, but not successful, clean up the io_req, return fail */
 	if (status != FCPIO_SUCCESS) {
 		spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
-		FNIC_SCSI_DBG(KERN_DEBUG,
-			      fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 			      "Device reset completed - failed\n");
 		io_req = fnic_priv(sc)->io_req;
 		goto fnic_device_reset_clean;
@@ -2757,7 +2780,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	if (fnic_clean_pending_aborts(fnic, sc, new_sc)) {
 		spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags);
 		io_req = fnic_priv(sc)->io_req;
-		FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 					  "Device reset failed: Cannot abort all IOs\n");
 		goto fnic_device_reset_clean;
 	}
@@ -2811,13 +2834,13 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 			ret = FAILED;
 			break;
 		}
-		FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+		FNIC_SCSI_DBG(KERN_ERR, fnic,
 					  "Cannot clean up all IOs for the LUN\n");
 		schedule_timeout(msecs_to_jiffies(1000));
 		count++;
 	}
 
-	FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic,
 		      "Returning from device reset %s\n",
 		      (ret == SUCCESS) ?
 		      "SUCCESS" : "FAILED");
@@ -2852,13 +2875,13 @@ void fnic_reset(struct Scsi_Host *shost)
 	fnic = *((struct fnic **) shost_priv(shost));
 	reset_stats = &fnic->fnic_stats.reset_stats;
 
-	FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_INFO, fnic,
 				  "Issuing fnic reset\n");
 
 	atomic64_inc(&reset_stats->fnic_resets);
 	fnic_post_flogo_linkflap(fnic);
 
-	FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_INFO, fnic,
 				  "Returning from fnic reset");
 
 	atomic64_inc(&reset_stats->fnic_reset_completions);
@@ -2869,7 +2892,7 @@ int fnic_issue_fc_host_lip(struct Scsi_Host *shost)
 	int ret = 0;
 	struct fnic *fnic = *((struct fnic **) shost_priv(shost));
 
-	FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_INFO, fnic,
 				  "FC host lip issued");
 
 	ret = fnic_host_reset(shost);
@@ -2895,7 +2918,7 @@ int fnic_host_reset(struct Scsi_Host *shost)
 		spin_lock_irqsave(&fnic->fnic_lock, flags);
 		if (fnic->reset_in_progress == IN_PROGRESS) {
 			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-			FNIC_SCSI_DBG(KERN_WARNING, fnic->host, fnic->fnic_num,
+			FNIC_SCSI_DBG(KERN_WARNING, fnic,
 			  "Firmware reset in progress. Skipping another host reset\n");
 			return SUCCESS;
 		}
@@ -2933,7 +2956,7 @@ int fnic_host_reset(struct Scsi_Host *shost)
 	}
 	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
-	FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_INFO, fnic,
 				  "host reset return status: %d\n", ret);
 	return ret;
 }
@@ -2973,7 +2996,7 @@ static bool fnic_abts_pending_iter(struct scsi_cmnd *sc, void *data)
 	 * Found IO that is still pending with firmware and
 	 * belongs to the LUN that we are resetting
 	 */
-	FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_INFO, fnic,
 		"hwq: %d tag: 0x%x Found IO in state: %s on lun\n",
 		hwq, tag,
 		fnic_ioreq_state_to_str(fnic_priv(sc)->state));
@@ -3027,7 +3050,7 @@ int fnic_eh_host_reset_handler(struct scsi_cmnd *sc)
 	struct Scsi_Host *shost = sc->device->host;
 	struct fnic *fnic = *((struct fnic **) shost_priv(shost));
 
-	FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
+	FNIC_SCSI_DBG(KERN_ERR, fnic,
 				  "SCSI error handling: fnic host reset");
 
 	ret = fnic_host_reset(shost);
diff --git a/drivers/scsi/fnic/fnic_stats.h b/drivers/scsi/fnic/fnic_stats.h
index 8ddd20401a59..a3ddd7b55729 100644
--- a/drivers/scsi/fnic/fnic_stats.h
+++ b/drivers/scsi/fnic/fnic_stats.h
@@ -29,6 +29,15 @@ struct io_path_stats {
 	atomic64_t io_greater_than_30000_msec;
 	atomic64_t current_max_io_time;
 	atomic64_t ios[FNIC_MQ_MAX_QUEUES];
+
+	atomic64_t nvme_io_reqs_rcvd;
+	atomic64_t nvme_ios_queued_for_rsp;
+	atomic64_t nvme_io_rsps_unqueued;
+	atomic64_t nvme_io_rsps_sending;
+	atomic64_t nvme_io_rsps_sent;
+	atomic64_t nvme_num_ios_in_waitq;
+	atomic64_t nvme_ios_in_waitq_3000_msec;
+	atomic64_t nvme_ios_in_waitq_max_time;
 };
 
 struct abort_stats {
@@ -151,6 +160,17 @@ struct fnic_iport_stats {
 	atomic64_t unsupported_frames_dropped;
 };
 
+struct nvme_host_statistics {
+	atomic64_t nvme_input_requests;
+	atomic64_t nvme_output_requests;
+	atomic64_t nvme_control_requests;
+	atomic64_t nvme_ersps;
+	atomic64_t nvme_ls_requests;
+	atomic64_t nvme_ls_responses;
+	atomic64_t nvme_ls_aborts;
+	atomic64_t nvme_ls_abort_responses;
+};
+
 struct fnic_stats {
 	struct stats_timestamps stats_timestamps;
 	struct io_path_stats io_stats;
@@ -161,6 +181,7 @@ struct fnic_stats {
 	struct vlan_stats vlan_stats;
 	struct fc_host_statistics host_stats;
 	struct misc_stats misc_stats;
+	struct nvme_host_statistics nvme_stats;
 };
 
 struct stats_debug_info {
@@ -170,6 +191,13 @@ struct stats_debug_info {
 	int buffer_len;
 };
 
+struct fnic_nvmef_info {
+	char *info_buffer;
+	void *i_private;
+	int buf_size;
+	int buffer_len;
+};
+
 int fnic_get_stats_data(struct stats_debug_info *, struct fnic_stats *);
 const char *fnic_role_to_str(unsigned int role);
 #endif /* _FNIC_STATS_H_ */
diff --git a/drivers/scsi/fnic/fnic_trace.c b/drivers/scsi/fnic/fnic_trace.c
index 4ed57ea1f854..a3f583703664 100644
--- a/drivers/scsi/fnic/fnic_trace.c
+++ b/drivers/scsi/fnic/fnic_trace.c
@@ -31,7 +31,8 @@ int fnic_fc_trace_cleared = 1;
 static DEFINE_SPINLOCK(fnic_fc_trace_lock);
 
 static const char * const fnic_role_str[] = {
-	[FNIC_ROLE_FCP_INITIATOR] = "FCP_Initiator",
+	[FNIC_ROLE_FCP_INITIATOR]  = "FCP_Initiator",
+	[FNIC_ROLE_NVME_INITIATOR] = "NVMeF_Initiator",
 };
 
 const char *fnic_role_to_str(unsigned int role)
diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c
index 991c86eb5aff..0e5ae0540d85 100644
--- a/drivers/scsi/fnic/vnic_dev.c
+++ b/drivers/scsi/fnic/vnic_dev.c
@@ -381,7 +381,7 @@ static int vnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
 		if (result->color == color) {
 			if (result->error) {
 				err = -(int) result->error;
-				if (err != ERR_ECMDUNKNOWN ||
+				if (err != -ERR_ECMDUNKNOWN ||
 						cmd != CMD_CAPABILITY)
 					pr_err("%s:Error %d devcmd %d\n",
 						pci_name(vdev->pdev),
diff --git a/drivers/scsi/fnic/vnic_devcmd.h b/drivers/scsi/fnic/vnic_devcmd.h
index f876d223b2b4..20878a3e9e26 100644
--- a/drivers/scsi/fnic/vnic_devcmd.h
+++ b/drivers/scsi/fnic/vnic_devcmd.h
@@ -430,7 +430,7 @@ struct vnic_devcmd_notify {
 struct vnic_devcmd_provinfo {
 	u8 oui[3];
 	u8 type;
-	u8 data[];
+	u8 data[0];
 };
 
 /*
diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c
index 270eae7ac427..41731a7304dd 100644
--- a/drivers/scsi/g_NCR5380.c
+++ b/drivers/scsi/g_NCR5380.c
@@ -739,7 +739,7 @@ static struct isa_driver generic_NCR5380_isa_driver = {
 #ifdef CONFIG_PNP
 static const struct pnp_device_id generic_NCR5380_pnp_ids[] = {
 	{ .id = "DTC436e", .driver_data = BOARD_DTC3181E },
-	{ .id = "" }
+	{ }
 };
 MODULE_DEVICE_TABLE(pnp, generic_NCR5380_pnp_ids);
 
diff --git a/drivers/scsi/gvp11.c b/drivers/scsi/gvp11.c
index 0420bfe9bd42..79bd64e12adc 100644
--- a/drivers/scsi/gvp11.c
+++ b/drivers/scsi/gvp11.c
@@ -442,14 +442,15 @@ static void gvp11_remove(struct zorro_dev *z)
 	 */
 
 static struct zorro_device_id gvp11_zorro_tbl[] = {
-	{ ZORRO_PROD_GVP_COMBO_030_R3_SCSI,	~0x00ffffff },
-	{ ZORRO_PROD_GVP_SERIES_II,		~0x00ffffff },
-	{ ZORRO_PROD_GVP_GFORCE_030_SCSI,	~0x01ffffff },
-	{ ZORRO_PROD_GVP_A530_SCSI,		~0x01ffffff },
-	{ ZORRO_PROD_GVP_COMBO_030_R4_SCSI,	~0x01ffffff },
-	{ ZORRO_PROD_GVP_A1291,			~0x07ffffff },
-	{ ZORRO_PROD_GVP_GFORCE_040_SCSI_1,	~0x07ffffff },
-	{ 0 }
+	/* .driver_data specifies the DMA mask */
+	{ .id = ZORRO_PROD_GVP_COMBO_030_R3_SCSI,	.driver_data = ~0x00ffffff },
+	{ .id = ZORRO_PROD_GVP_SERIES_II,		.driver_data = ~0x00ffffff },
+	{ .id = ZORRO_PROD_GVP_GFORCE_030_SCSI,		.driver_data = ~0x01ffffff },
+	{ .id = ZORRO_PROD_GVP_A530_SCSI,		.driver_data = ~0x01ffffff },
+	{ .id = ZORRO_PROD_GVP_COMBO_030_R4_SCSI,	.driver_data = ~0x01ffffff },
+	{ .id = ZORRO_PROD_GVP_A1291,			.driver_data = ~0x07ffffff },
+	{ .id = ZORRO_PROD_GVP_GFORCE_040_SCSI_1,	.driver_data = ~0x07ffffff },
+	{ }
 };
 MODULE_DEVICE_TABLE(zorro, gvp11_zorro_tbl);
 
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index e047747d4ecf..d512080268af 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -252,10 +252,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
 
 	shost->dma_dev = dma_dev;
 
-	if (dma_dev->dma_mask) {
-		shost->max_sectors = min_t(unsigned int, shost->max_sectors,
-				dma_max_mapping_size(dma_dev) >> SECTOR_SHIFT);
-	}
+	shost->max_sectors = min_not_zero(shost->max_sectors,
+			dma_max_mapping_size(dma_dev) >> SECTOR_SHIFT);
 
 	error = scsi_mq_setup_tags(shost);
 	if (error)
diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile
index 5eb1cb1a0028..9408c7f4cdee 100644
--- a/drivers/scsi/ibmvscsi/Makefile
+++ b/drivers/scsi/ibmvscsi/Makefile
@@ -1,3 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
+ibmvfc-objs := ibmvfc-core.o ibmvfc-nvme.o
+
 obj-$(CONFIG_SCSI_IBMVSCSI)	+= ibmvscsi.o
 obj-$(CONFIG_SCSI_IBMVFC)	+= ibmvfc.o
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
similarity index 87%
rename from drivers/scsi/ibmvscsi/ibmvfc.c
rename to drivers/scsi/ibmvscsi/ibmvfc-core.c
index 3dd2adda195e..93b9f699c2e5 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -1,10 +1,11 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
 /*
  * ibmvfc.c -- driver for IBM Power Virtual Fibre Channel Adapter
  *
  * Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
  *
- * Copyright (C) IBM Corporation, 2008
+ * Copyright (C) IBM Corporation, 2008-2026
  */
 
 #include <linux/module.h>
@@ -40,15 +41,19 @@ static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT;
 static u16 max_sectors = IBMVFC_MAX_SECTORS;
 static u16 scsi_qdepth = IBMVFC_SCSI_QDEPTH;
 static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS;
-static unsigned int ibmvfc_debug = IBMVFC_DEBUG;
 static unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL;
 static unsigned int cls3_error = IBMVFC_CLS3_ERROR;
 static unsigned int mq_enabled = IBMVFC_MQ;
 static unsigned int nr_scsi_hw_queues = IBMVFC_SCSI_HW_QUEUES;
 static unsigned int nr_scsi_channels = IBMVFC_SCSI_CHANNELS;
+static unsigned int nvme_enabled = IBMVFC_NVME;
+static unsigned int nr_nvme_hw_queues = IBMVFC_NVME_HW_QUEUES;
+static unsigned int nr_nvme_channels = IBMVFC_NVME_CHANNELS;
 static unsigned int mig_channels_only = IBMVFC_MIG_NO_SUB_TO_CRQ;
 static unsigned int mig_no_less_channels = IBMVFC_MIG_NO_N_TO_M;
 
+unsigned int ibmvfc_debug = IBMVFC_DEBUG;
+
 static LIST_HEAD(ibmvfc_head);
 static DEFINE_SPINLOCK(ibmvfc_driver_lock);
 static struct scsi_transport_template *ibmvfc_transport_template;
@@ -74,6 +79,16 @@ module_param_named(mig_no_less_channels, mig_no_less_channels, uint, S_IRUGO);
 MODULE_PARM_DESC(mig_no_less_channels, "Prevent migration to system with less channels. "
 		 "[Default=" __stringify(IBMVFC_MIG_NO_N_TO_M) "]");
 
+module_param_named(nvme, nvme_enabled, uint, S_IRUGO);
+MODULE_PARM_DESC(nvme, "Enable NVMe over FC support. "
+		 "[Default=" __stringify(IBMVFC_NVME) "]");
+module_param_named(nvme_host_queues, nr_nvme_hw_queues, uint, S_IRUGO);
+MODULE_PARM_DESC(nvme_host_queues, "Number of NVMeoF Host submission queues. "
+		 "[Default=" __stringify(IBMVFC_NVME_HW_QUEUES) "]");
+module_param_named(nvme_hw_channels, nr_nvme_channels, uint, S_IRUGO);
+MODULE_PARM_DESC(nvme_hw_channels, "Number of hw NVMeoF channels to request. "
+		 "[Default=" __stringify(IBMVFC_NVME_CHANNELS) "]");
+
 module_param_named(init_timeout, init_timeout, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds. "
 		 "[Default=" __stringify(IBMVFC_INIT_TIMEOUT) "]");
@@ -159,6 +174,11 @@ static const struct {
 	{ IBMVFC_FC_SCSI_ERROR, IBMVFC_COMMAND_FAILED, DID_ERROR, 0, 1, "PRLI to device failed." },
 };
 
+static const char *proto_type[] = {
+	"SCSI",
+	"NVMe",
+};
+
 static void ibmvfc_npiv_login(struct ibmvfc_host *);
 static void ibmvfc_tgt_send_prli(struct ibmvfc_target *);
 static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *);
@@ -186,17 +206,18 @@ static long h_reg_sub_crq(unsigned long unit_address, unsigned long ioba,
 	return rc;
 }
 
-static int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags)
+static int ibmvfc_nvme_active(struct ibmvfc_host *vhost)
 {
-	u64 host_caps = be64_to_cpu(vhost->login_buf->resp.capabilities);
-
-	return (host_caps & cap_flags) ? 1 : 0;
+	return (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF) &&
+		vhost->nvme_scrqs.active_queues);
 }
 
 static struct ibmvfc_fcp_cmd_iu *ibmvfc_get_fcp_iu(struct ibmvfc_host *vhost,
 						   struct ibmvfc_cmd *vfc_cmd)
 {
-	if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
+	if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF))
+		return &vfc_cmd->v3scsi.iu;
+	else if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
 		return &vfc_cmd->v2.iu;
 	else
 		return &vfc_cmd->v1.iu;
@@ -205,7 +226,9 @@ static struct ibmvfc_fcp_cmd_iu *ibmvfc_get_fcp_iu(struct ibmvfc_host *vhost,
 static struct ibmvfc_fcp_rsp *ibmvfc_get_fcp_rsp(struct ibmvfc_host *vhost,
 						 struct ibmvfc_cmd *vfc_cmd)
 {
-	if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
+	if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF))
+		return &vfc_cmd->v3scsi.rsp;
+	else if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
 		return &vfc_cmd->v2.rsp;
 	else
 		return &vfc_cmd->v1.rsp;
@@ -324,7 +347,7 @@ static int ibmvfc_get_err_index(u16 status, u16 error)
  * Return value:
  *	error description string
  **/
-static const char *ibmvfc_get_cmd_error(u16 status, u16 error)
+const char *ibmvfc_get_cmd_error(u16 status, u16 error)
 {
 	int rc = ibmvfc_get_err_index(status, error);
 	if (rc >= 0)
@@ -468,6 +491,7 @@ static const struct {
 	{ IBMVFC_FABRIC_BUSY, "fabric busy" },
 	{ IBMVFC_PORT_BUSY, "port busy" },
 	{ IBMVFC_BASIC_REJECT, "basic reject" },
+	{ IBMVFC_FC4_LS_REJECT, "fc4 ls reject" },
 };
 
 static const char *unknown_fc_type = "unknown fc type";
@@ -682,7 +706,9 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost,
 
 	ENTER;
 	scsi_block_requests(vhost->host);
-	list_for_each_entry(tgt, &vhost->targets, queue)
+	list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
+		ibmvfc_del_tgt(tgt);
+	list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
 		ibmvfc_del_tgt(tgt);
 	ibmvfc_set_host_state(vhost, state);
 	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
@@ -715,7 +741,13 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost)
 		memset(vhost->async_crq.msgs.async, 0, PAGE_SIZE);
 		vhost->async_crq.cur = 0;
 
-		list_for_each_entry(tgt, &vhost->targets, queue) {
+		list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+			if (vhost->client_migrated)
+				tgt->need_login = 1;
+			else
+				ibmvfc_del_tgt(tgt);
+		}
+		list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
 			if (vhost->client_migrated)
 				tgt->need_login = 1;
 			else
@@ -930,6 +962,7 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
 	unsigned long flags;
 
 	ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs);
+	ibmvfc_dereg_sub_crqs(vhost, &vhost->nvme_scrqs);
 
 	/* Re-enable the CRQ */
 	do {
@@ -945,10 +978,13 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
 	spin_lock(vhost->crq.q_lock);
 	vhost->do_enquiry = 1;
 	vhost->using_channels = 0;
+	vhost->do_scsi_login = 0;
+	vhost->do_nvme_login = 0;
 	spin_unlock(vhost->crq.q_lock);
 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
 
 	ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs);
+	ibmvfc_reg_sub_crqs(vhost, &vhost->nvme_scrqs);
 
 	return rc;
 }
@@ -968,6 +1004,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
 	struct ibmvfc_queue *crq = &vhost->crq;
 
 	ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs);
+	ibmvfc_dereg_sub_crqs(vhost, &vhost->nvme_scrqs);
 
 	/* Close the CRQ */
 	do {
@@ -982,6 +1019,8 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
 	vhost->logged_in = 0;
 	vhost->do_enquiry = 1;
 	vhost->using_channels = 0;
+	vhost->do_scsi_login = 0;
+	vhost->do_nvme_login = 0;
 
 	/* Clean out the queue */
 	memset(crq->msgs.crq, 0, PAGE_SIZE);
@@ -1001,6 +1040,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
 
 	ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs);
+	ibmvfc_reg_sub_crqs(vhost, &vhost->nvme_scrqs);
 
 	return rc;
 }
@@ -1029,7 +1069,7 @@ static int ibmvfc_valid_event(struct ibmvfc_event_pool *pool,
  * @evt:	ibmvfc_event to be freed
  *
  **/
-static void ibmvfc_free_event(struct ibmvfc_event *evt)
+void ibmvfc_free_event(struct ibmvfc_event *evt)
 {
 	struct ibmvfc_event_pool *pool = &evt->queue->evt_pool;
 	unsigned long flags;
@@ -1052,19 +1092,29 @@ static void ibmvfc_free_event(struct ibmvfc_event *evt)
 }
 
 /**
- * ibmvfc_scsi_eh_done - EH done function for queuecommand commands
+ * ibmvfc_vfc_eh_done - EH done function for queued IO
  * @evt:	ibmvfc event struct
  *
- * This function does not setup any error status, that must be done
- * before this function gets called.
+ * This function does not setup any error status for scsi commands, that must be
+ * done before this function gets called.
  **/
-static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
+static void ibmvfc_vfc_eh_done(struct ibmvfc_event *evt)
 {
 	struct scsi_cmnd *cmnd = evt->cmnd;
+	struct nvmefc_ls_req *ls_req = evt->ls_req;
+	struct nvmefc_fcp_req *fcp_req = evt->fcp_req;
 
 	if (cmnd) {
 		scsi_dma_unmap(cmnd);
 		scsi_done(cmnd);
+	} else if (fcp_req) {
+		fcp_req->rcv_rsplen = 0;
+		fcp_req->transferred_length = 0;
+		fcp_req->status = NVME_SC_INTERNAL;
+		fcp_req->done(fcp_req);
+	} else if (ls_req) {
+		ls_req->done(ls_req, -EIO);
+		kref_put(&evt->tgt->kref, ibmvfc_release_tgt);
 	}
 
 	ibmvfc_free_event(evt);
@@ -1106,8 +1156,10 @@ static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
 	BUG_ON(!atomic_dec_and_test(&evt->active));
 	if (evt->cmnd) {
 		evt->cmnd->result = (error_code << 16);
-		evt->done = ibmvfc_scsi_eh_done;
-	} else
+		evt->done = ibmvfc_vfc_eh_done;
+	} else if (evt->fcp_req || evt->ls_req)
+		evt->done = ibmvfc_vfc_eh_done;
+	else
 		evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_DRIVER_FAILED);
 
 	timer_delete(&evt->timer);
@@ -1124,13 +1176,16 @@ static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
 static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
 {
 	struct ibmvfc_event *evt, *pos;
-	struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs;
+	struct ibmvfc_queue *scsi_q = vhost->scsi_scrqs.scrqs;
+	struct ibmvfc_queue *nvme_q = vhost->nvme_scrqs.scrqs;
 	unsigned long flags;
-	int hwqs = 0;
+	int shwqs, nhwqs = 0;
 	int i;
 
-	if (vhost->using_channels)
-		hwqs = vhost->scsi_scrqs.active_queues;
+	if (vhost->using_channels) {
+		shwqs = vhost->scsi_scrqs.active_queues;
+		nhwqs = vhost->nvme_scrqs.active_queues;
+	}
 
 	ibmvfc_dbg(vhost, "Purging all requests\n");
 	spin_lock_irqsave(&vhost->crq.l_lock, flags);
@@ -1139,14 +1194,24 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
 	list_splice_init(&vhost->crq.sent, &vhost->purge);
 	spin_unlock_irqrestore(&vhost->crq.l_lock, flags);
 
-	for (i = 0; i < hwqs; i++) {
-		spin_lock_irqsave(queues[i].q_lock, flags);
-		spin_lock(&queues[i].l_lock);
-		list_for_each_entry_safe(evt, pos, &queues[i].sent, queue_list)
+	for (i = 0; i < shwqs; i++) {
+		spin_lock_irqsave(scsi_q[i].q_lock, flags);
+		spin_lock(&scsi_q[i].l_lock);
+		list_for_each_entry_safe(evt, pos, &scsi_q[i].sent, queue_list)
 			ibmvfc_fail_request(evt, error_code);
-		list_splice_init(&queues[i].sent, &vhost->purge);
-		spin_unlock(&queues[i].l_lock);
-		spin_unlock_irqrestore(queues[i].q_lock, flags);
+		list_splice_init(&scsi_q[i].sent, &vhost->purge);
+		spin_unlock(&scsi_q[i].l_lock);
+		spin_unlock_irqrestore(scsi_q[i].q_lock, flags);
+	}
+
+	for (i = 0; i < nhwqs; i++) {
+		spin_lock_irqsave(nvme_q[i].q_lock, flags);
+		spin_lock(&nvme_q[i].l_lock);
+		list_for_each_entry_safe(evt, pos, &nvme_q[i].sent, queue_list)
+			ibmvfc_fail_request(evt, error_code);
+		list_splice_init(&nvme_q[i].sent, &vhost->purge);
+		spin_unlock(&nvme_q[i].l_lock);
+		spin_unlock_irqrestore(nvme_q[i].q_lock, flags);
 	}
 }
 
@@ -1232,7 +1297,7 @@ static struct ibmvfc_target *__ibmvfc_get_target(struct scsi_target *starget)
 	struct ibmvfc_host *vhost = shost_priv(shost);
 	struct ibmvfc_target *tgt;
 
-	list_for_each_entry(tgt, &vhost->targets, queue)
+	list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
 		if (tgt->target_id == starget->id) {
 			kref_get(&tgt->kref);
 			return tgt;
@@ -1363,10 +1428,10 @@ static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
  * @kref:		kref struct
  *
  **/
-static void ibmvfc_release_tgt(struct kref *kref)
+void ibmvfc_release_tgt(struct kref *kref)
 {
 	struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref);
-	kfree(tgt);
+	mempool_free(tgt, tgt->vhost->tgt_pool);
 }
 
 /**
@@ -1493,13 +1558,13 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
 	max_cmds = scsi_qdepth + IBMVFC_NUM_INTERNAL_REQ;
 	if (mq_enabled)
 		max_cmds += (scsi_qdepth + IBMVFC_NUM_INTERNAL_SUBQ_REQ) *
-			vhost->scsi_scrqs.desired_queues;
+			(vhost->scsi_scrqs.desired_queues + vhost->nvme_scrqs.desired_queues);
 
 	memset(login_info, 0, sizeof(*login_info));
 
 	login_info->ostype = cpu_to_be32(IBMVFC_OS_LINUX);
 	login_info->max_dma_len = cpu_to_be64(max_sectors << 9);
-	login_info->max_payload = cpu_to_be32(sizeof(struct ibmvfc_fcp_cmd_iu));
+	login_info->max_payload = cpu_to_be32(sizeof(struct nvme_fc_cmd_iu));
 	login_info->max_response = cpu_to_be32(sizeof(struct ibmvfc_fcp_rsp));
 	login_info->partition_num = cpu_to_be32(vhost->partition_number);
 	login_info->vfc_frame_version = cpu_to_be32(1);
@@ -1511,8 +1576,14 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
 	login_info->max_cmds = cpu_to_be32(max_cmds);
 	login_info->capabilities = cpu_to_be64(IBMVFC_CAN_MIGRATE | IBMVFC_CAN_SEND_VF_WWPN);
 
-	if (vhost->mq_enabled || vhost->using_channels)
+	if (vhost->mq_enabled || vhost->using_channels) {
 		login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_CHANNELS);
+		if (vhost->nvme_enabled) {
+			login_info->capabilities |= cpu_to_be64(IBMVFC_YES_NVMEOF);
+			login_info->capabilities |= cpu_to_be64(IBMVFC_YES_SCSI);
+			login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_WWPN_ALL);
+		}
+	}
 
 	login_info->async.va = cpu_to_be64(vhost->async_crq.msg_token);
 	login_info->async.len = cpu_to_be32(async_crq->size *
@@ -1535,7 +1606,7 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
  *
  * Returns a free event from the pool.
  **/
-static struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved)
+struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved)
 {
 	struct ibmvfc_event *evt = NULL;
 	unsigned long flags;
@@ -1559,9 +1630,6 @@ static struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int r
 	return evt;
 }
 
-#define ibmvfc_get_event(queue) __ibmvfc_get_event(queue, 0)
-#define ibmvfc_get_reserved_event(queue) __ibmvfc_get_event(queue, 1)
-
 /**
  * ibmvfc_locked_done - Calls evt completion with host_lock held
  * @evt:	ibmvfc evt to complete
@@ -1586,10 +1654,12 @@ static void ibmvfc_locked_done(struct ibmvfc_event *evt)
  * @done:	Routine to call when the event is responded to
  * @format:	SRP or MAD format
  **/
-static void ibmvfc_init_event(struct ibmvfc_event *evt,
+void ibmvfc_init_event(struct ibmvfc_event *evt,
 			      void (*done) (struct ibmvfc_event *), u8 format)
 {
 	evt->cmnd = NULL;
+	evt->fcp_req = NULL;
+	evt->ls_req = NULL;
 	evt->sync_iu = NULL;
 	evt->eh_comp = NULL;
 	evt->crq.format = format;
@@ -1711,7 +1781,7 @@ static void ibmvfc_timeout(struct timer_list *t)
  *
  * Returns the value returned from ibmvfc_send_crq(). (Zero for success)
  **/
-static int ibmvfc_send_event(struct ibmvfc_event *evt,
+int ibmvfc_send_event(struct ibmvfc_event *evt,
 			     struct ibmvfc_host *vhost, unsigned long timeout)
 {
 	__be64 *crq_as_u64 = (__be64 *) &evt->crq;
@@ -1773,9 +1843,13 @@ static int ibmvfc_send_event(struct ibmvfc_event *evt,
 		dev_err(vhost->dev, "Send error (rc=%d)\n", rc);
 		if (evt->cmnd) {
 			evt->cmnd->result = DID_ERROR << 16;
-			evt->done = ibmvfc_scsi_eh_done;
-		} else
+			evt->done = ibmvfc_vfc_eh_done;
+		} else if (evt->fcp_req || evt->ls_req) {
+			evt->done = ibmvfc_vfc_eh_done;
+		} else {
 			evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_CRQ_ERROR);
+			evt->done = evt->_done;
+		}
 
 		evt->done(evt);
 	} else {
@@ -1824,7 +1898,7 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt)
  * @sdev:	scsi device struct
  *
  **/
-static void ibmvfc_relogin(struct scsi_device *sdev)
+static void ibmvfc_scsi_relogin(struct scsi_device *sdev)
 {
 	struct ibmvfc_host *vhost = shost_priv(sdev->host);
 	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
@@ -1832,7 +1906,7 @@ static void ibmvfc_relogin(struct scsi_device *sdev)
 	unsigned long flags;
 
 	spin_lock_irqsave(vhost->host->host_lock, flags);
-	list_for_each_entry(tgt, &vhost->targets, queue) {
+	list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
 		if (rport == tgt->rport) {
 			ibmvfc_del_tgt(tgt);
 			break;
@@ -1876,7 +1950,7 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
 				memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
 			if ((be16_to_cpu(vfc_cmd->status) & IBMVFC_VIOS_FAILURE) &&
 			    (be16_to_cpu(vfc_cmd->error) == IBMVFC_PLOGI_REQUIRED))
-				ibmvfc_relogin(cmnd->device);
+				ibmvfc_scsi_relogin(cmnd->device);
 
 			if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER)))
 				cmnd->result = (DID_ERROR << 16);
@@ -1935,11 +2009,13 @@ static struct ibmvfc_cmd *ibmvfc_init_vfc_cmd(struct ibmvfc_event *evt, struct s
 	size_t offset;
 
 	memset(vfc_cmd, 0, sizeof(*vfc_cmd));
-	if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) {
+	if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF))
+		offset = offsetof(struct ibmvfc_cmd, v3scsi.rsp);
+	else if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
 		offset = offsetof(struct ibmvfc_cmd, v2.rsp);
-		vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name);
-	} else
+	else
 		offset = offsetof(struct ibmvfc_cmd, v1.rsp);
+	vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name);
 	vfc_cmd->resp.va = cpu_to_be64(be64_to_cpu(evt->crq.ioba) + offset);
 	vfc_cmd->resp.len = cpu_to_be32(sizeof(*rsp));
 	vfc_cmd->frame_type = cpu_to_be32(IBMVFC_SCSI_FCP_TYPE);
@@ -2130,7 +2206,7 @@ static int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id)
 
 	ENTER;
 	spin_lock_irqsave(vhost->host->host_lock, flags);
-	list_for_each_entry(tgt, &vhost->targets, queue) {
+	list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
 		if (tgt->scsi_id == port_id) {
 			issue_login = 0;
 			break;
@@ -3102,7 +3178,7 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
 
 	spin_lock_irqsave(shost->host_lock, flags);
 	found = 0;
-	list_for_each_entry(tgt, &vhost->targets, queue) {
+	list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
 		if (tgt->scsi_id == rport->port_id) {
 			found++;
 			break;
@@ -3242,7 +3318,23 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
 	case IBMVFC_AE_ELS_LOGO:
 	case IBMVFC_AE_ELS_PRLO:
 	case IBMVFC_AE_ELS_PLOGI:
-		list_for_each_entry(tgt, &vhost->targets, queue) {
+		list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+			if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
+				break;
+			if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id)
+				continue;
+			if (crq->wwpn && cpu_to_be64(tgt->ids.port_name) != crq->wwpn)
+				continue;
+			if (crq->node_name && cpu_to_be64(tgt->ids.node_name) != crq->node_name)
+				continue;
+			if (tgt->need_login && be64_to_cpu(crq->event) == IBMVFC_AE_ELS_LOGO)
+				tgt->logo_rcvd = 1;
+			if (!tgt->need_login || be64_to_cpu(crq->event) == IBMVFC_AE_ELS_PLOGI) {
+				ibmvfc_del_tgt(tgt);
+				ibmvfc_reinit_host(vhost);
+			}
+		}
+		list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
 			if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
 				break;
 			if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id)
@@ -4042,11 +4134,12 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
 	switch (status) {
 	case IBMVFC_MAD_SUCCESS:
-		tgt_dbg(tgt, "Process Login succeeded: %X %02X %04X\n",
-			parms->type, parms->flags, parms->service_parms);
+		tgt_dbg(tgt, "%s Process Login succeeded: %X %02X %04X\n",
+			proto_type[tgt->protocol], parms->type, parms->flags,
+			parms->service_parms);
 
+		index = ibmvfc_get_prli_rsp(be16_to_cpu(parms->flags));
 		if (parms->type == IBMVFC_SCSI_FCP_TYPE) {
-			index = ibmvfc_get_prli_rsp(be16_to_cpu(parms->flags));
 			if (prli_rsp[index].logged_in) {
 				if (be16_to_cpu(parms->flags) & IBMVFC_PRLI_EST_IMG_PAIR) {
 					tgt->need_login = 0;
@@ -4062,6 +4155,22 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
 				ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
 			else
 				ibmvfc_del_tgt(tgt);
+		} else if (parms->type == IBMVFC_NVME_FCP_TYPE) {
+			if (prli_rsp[index].logged_in) {
+				/* For FC-NVMe PRLI the Image Pair field is always set to zero (see 6.3.3) */
+				tgt->need_login = 0;
+				tgt->ids.roles = 0;
+				if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_TARGET)
+					tgt->ids.roles |= FC_PORT_ROLE_NVME_TARGET;
+				if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_INITIATOR)
+					tgt->ids.roles |= FC_PORT_ROLE_NVME_INITIATOR;
+				if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_DISCOVERY)
+					tgt->ids.roles |= FC_PORT_ROLE_NVME_DISCOVERY;
+				tgt->add_rport = 1;
+			} else if (prli_rsp[index].retry)
+				ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
+			else
+				ibmvfc_del_tgt(tgt);
 		} else
 			ibmvfc_del_tgt(tgt);
 		break;
@@ -4082,9 +4191,10 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
 		else
 			ibmvfc_del_tgt(tgt);
 
-		tgt_log(tgt, level, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
-			ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
-			be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), status);
+		tgt_log(tgt, level, "%s Process Login failed: %s (%x:%x) rc=0x%02X\n",
+			proto_type[tgt->protocol], ibmvfc_get_cmd_error(be16_to_cpu(rsp->status),
+			be16_to_cpu(rsp->error)), be16_to_cpu(rsp->status),
+			be16_to_cpu(rsp->error), status);
 		break;
 	}
 
@@ -4126,25 +4236,30 @@ static void ibmvfc_tgt_send_prli(struct ibmvfc_target *tgt)
 	} else {
 		prli->common.version = cpu_to_be32(1);
 	}
-	prli->common.opcode = cpu_to_be32(IBMVFC_PROCESS_LOGIN);
+	if (tgt->protocol == IBMVFC_PROTO_SCSI) {
+		prli->common.opcode = cpu_to_be32(IBMVFC_PROCESS_LOGIN);
+		prli->parms.type = IBMVFC_SCSI_FCP_TYPE;
+		prli->parms.flags = cpu_to_be16(IBMVFC_PRLI_EST_IMG_PAIR);
+		prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC);
+		prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED);
+
+		if (cls3_error)
+			prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_RETRY);
+	} else {
+		prli->common.opcode = cpu_to_be32(IBMVFC_NVMF_PROCESS_LOGIN);
+		prli->parms.type = IBMVFC_NVME_FCP_TYPE;
+		prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_NVME_INITIATOR);
+	}
 	prli->common.length = cpu_to_be16(sizeof(*prli));
 	prli->scsi_id = cpu_to_be64(tgt->scsi_id);
 
-	prli->parms.type = IBMVFC_SCSI_FCP_TYPE;
-	prli->parms.flags = cpu_to_be16(IBMVFC_PRLI_EST_IMG_PAIR);
-	prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC);
-	prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED);
-
-	if (cls3_error)
-		prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_RETRY);
-
 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
 	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
 		vhost->discovery_threads--;
 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
 		kref_put(&tgt->kref, ibmvfc_release_tgt);
 	} else
-		tgt_dbg(tgt, "Sent process login\n");
+		tgt_dbg(tgt, "%s Sent process login\n", proto_type[tgt->protocol]);
 }
 
 /**
@@ -4164,7 +4279,7 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
 	switch (status) {
 	case IBMVFC_MAD_SUCCESS:
-		tgt_dbg(tgt, "Port Login succeeded\n");
+		tgt_dbg(tgt, "%s Port Login succeeded\n", proto_type[tgt->protocol]);
 		if (tgt->ids.port_name &&
 		    tgt->ids.port_name != wwn_to_u64(rsp->service_parms.port_name)) {
 			vhost->reinit = 1;
@@ -4192,9 +4307,9 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
 		else
 			ibmvfc_del_tgt(tgt);
 
-		tgt_log(tgt, level, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
-			ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
-					     be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
+		tgt_log(tgt, level, "%s Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+			proto_type[tgt->protocol], ibmvfc_get_cmd_error(be16_to_cpu(rsp->status),
+			be16_to_cpu(rsp->error)), be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
 			ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type),
 			ibmvfc_get_ls_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain), status);
 		break;
@@ -4240,7 +4355,10 @@ static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt)
 	} else {
 		plogi->common.version = cpu_to_be32(1);
 	}
-	plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN);
+	if (tgt->protocol == IBMVFC_PROTO_SCSI)
+		plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN);
+	else
+		plogi->common.opcode = cpu_to_be32(IBMVFC_NVMF_PORT_LOGIN);
 	plogi->common.length = cpu_to_be16(sizeof(*plogi));
 	plogi->scsi_id = cpu_to_be64(tgt->scsi_id);
 
@@ -4249,7 +4367,7 @@ static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt)
 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
 		kref_put(&tgt->kref, ibmvfc_release_tgt);
 	} else
-		tgt_dbg(tgt, "Sent port login\n");
+		tgt_dbg(tgt, "Sent %s port login\n", proto_type[tgt->protocol]);
 }
 
 /**
@@ -4270,7 +4388,7 @@ static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt)
 
 	switch (status) {
 	case IBMVFC_MAD_SUCCESS:
-		tgt_dbg(tgt, "Implicit Logout succeeded\n");
+		tgt_dbg(tgt, "%s Implicit Logout succeeded\n", proto_type[tgt->protocol]);
 		break;
 	case IBMVFC_MAD_DRIVER_FAILED:
 		kref_put(&tgt->kref, ibmvfc_release_tgt);
@@ -4278,7 +4396,8 @@ static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt)
 		return;
 	case IBMVFC_MAD_FAILED:
 	default:
-		tgt_err(tgt, "Implicit Logout failed: rc=0x%02X\n", status);
+		tgt_err(tgt, "%s Implicit Logout failed: rc=0x%02X\n",
+			proto_type[tgt->protocol], status);
 		break;
 	}
 
@@ -4311,7 +4430,10 @@ static struct ibmvfc_event *__ibmvfc_tgt_get_implicit_logout_evt(struct ibmvfc_t
 	mad = &evt->iu.implicit_logout;
 	memset(mad, 0, sizeof(*mad));
 	mad->common.version = cpu_to_be32(1);
-	mad->common.opcode = cpu_to_be32(IBMVFC_IMPLICIT_LOGOUT);
+	if (tgt->protocol == IBMVFC_PROTO_SCSI)
+		mad->common.opcode = cpu_to_be32(IBMVFC_IMPLICIT_LOGOUT);
+	else
+		mad->common.opcode = cpu_to_be32(IBMVFC_NVMF_IMPLICIT_LOGOUT);
 	mad->common.length = cpu_to_be16(sizeof(*mad));
 	mad->old_scsi_id = cpu_to_be64(tgt->scsi_id);
 	return evt;
@@ -4347,7 +4469,7 @@ static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt)
 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
 		kref_put(&tgt->kref, ibmvfc_release_tgt);
 	} else
-		tgt_dbg(tgt, "Sent Implicit Logout\n");
+		tgt_dbg(tgt, "%s Sent Implicit Logout\n", proto_type[tgt->protocol]);
 }
 
 /**
@@ -4377,7 +4499,8 @@ static void ibmvfc_tgt_implicit_logout_and_del_done(struct ibmvfc_event *evt)
 	else
 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT);
 
-	tgt_dbg(tgt, "Implicit Logout %s\n", (status == IBMVFC_MAD_SUCCESS) ? "succeeded" : "failed");
+	tgt_dbg(tgt, "%s Implicit Logout %s\n", proto_type[tgt->protocol],
+		(status == IBMVFC_MAD_SUCCESS) ? "succeeded" : "failed");
 	kref_put(&tgt->kref, ibmvfc_release_tgt);
 	wake_up(&vhost->work_wait_q);
 }
@@ -4404,13 +4527,21 @@ static void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *tgt)
 	evt = __ibmvfc_tgt_get_implicit_logout_evt(tgt,
 						   ibmvfc_tgt_implicit_logout_and_del_done);
 
+	if (!evt) {
+		vhost->discovery_threads--;
+		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+		kref_put(&tgt->kref, ibmvfc_release_tgt);
+		__ibmvfc_reset_host(vhost);
+		return;
+	}
+
 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT);
 	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
 		vhost->discovery_threads--;
 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
 		kref_put(&tgt->kref, ibmvfc_release_tgt);
 	} else
-		tgt_dbg(tgt, "Sent Implicit Logout\n");
+		tgt_dbg(tgt, "%s Sent Implicit Logout\n", proto_type[tgt->protocol]);
 }
 
 /**
@@ -4430,7 +4561,8 @@ static void ibmvfc_tgt_move_login_done(struct ibmvfc_event *evt)
 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
 	switch (status) {
 	case IBMVFC_MAD_SUCCESS:
-		tgt_dbg(tgt, "Move Login succeeded for new scsi_id: %llX\n", tgt->new_scsi_id);
+		tgt_dbg(tgt, "%s Move Login succeeded for new scsi_id: %llX\n",
+			proto_type[tgt->protocol], tgt->new_scsi_id);
 		tgt->ids.node_name = wwn_to_u64(rsp->service_parms.node_name);
 		tgt->ids.port_name = wwn_to_u64(rsp->service_parms.port_name);
 		tgt->scsi_id = tgt->new_scsi_id;
@@ -4451,8 +4583,9 @@ static void ibmvfc_tgt_move_login_done(struct ibmvfc_event *evt)
 		level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_move_login);
 
 		tgt_log(tgt, level,
-			"Move Login failed: new scsi_id: %llX, flags:%x, vios_flags:%x, rc=0x%02X\n",
-			tgt->new_scsi_id, be32_to_cpu(rsp->flags), be16_to_cpu(rsp->vios_flags),
+			"%s Move Login failed: new scsi_id: %llX, flags:%x, vios_flags:%x, rc=0x%02X\n",
+			proto_type[tgt->protocol], tgt->new_scsi_id,
+			be32_to_cpu(rsp->flags), be16_to_cpu(rsp->vios_flags),
 			status);
 		break;
 	}
@@ -4492,7 +4625,10 @@ static void ibmvfc_tgt_move_login(struct ibmvfc_target *tgt)
 	move = &evt->iu.move_login;
 	memset(move, 0, sizeof(*move));
 	move->common.version = cpu_to_be32(1);
-	move->common.opcode = cpu_to_be32(IBMVFC_MOVE_LOGIN);
+	if (tgt->protocol == IBMVFC_PROTO_SCSI)
+		move->common.opcode = cpu_to_be32(IBMVFC_MOVE_LOGIN);
+	else
+		move->common.opcode = cpu_to_be32(IBMVFC_NVMF_MOVE_LOGIN);
 	move->common.length = cpu_to_be16(sizeof(*move));
 
 	move->old_scsi_id = cpu_to_be64(tgt->scsi_id);
@@ -4505,7 +4641,8 @@ static void ibmvfc_tgt_move_login(struct ibmvfc_target *tgt)
 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
 		kref_put(&tgt->kref, ibmvfc_release_tgt);
 	} else
-		tgt_dbg(tgt, "Sent Move Login for new scsi_id: %llX\n", tgt->new_scsi_id);
+		tgt_dbg(tgt, "Sent %s Move Login for new scsi_id: %llX\n",
+			proto_type[tgt->protocol], tgt->new_scsi_id);
 }
 
 /**
@@ -4766,7 +4903,7 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
 	switch (status) {
 	case IBMVFC_MAD_SUCCESS:
-		tgt_dbg(tgt, "Query Target succeeded\n");
+		tgt_dbg(tgt, "%s Query Target succeeded\n", proto_type[tgt->protocol]);
 		if (be64_to_cpu(rsp->scsi_id) != tgt->scsi_id)
 			ibmvfc_del_tgt(tgt);
 		else
@@ -4788,9 +4925,9 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
 		else
 			ibmvfc_del_tgt(tgt);
 
-		tgt_log(tgt, level, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
-			ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
-			be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
+		tgt_log(tgt, level, "%s Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+			proto_type[tgt->protocol], ibmvfc_get_cmd_error(be16_to_cpu(rsp->status),
+			be16_to_cpu(rsp->error)), be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
 			ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type),
 			ibmvfc_get_gs_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain),
 			status);
@@ -4830,7 +4967,10 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
 	query_tgt = &evt->iu.query_tgt;
 	memset(query_tgt, 0, sizeof(*query_tgt));
 	query_tgt->common.version = cpu_to_be32(1);
-	query_tgt->common.opcode = cpu_to_be32(IBMVFC_QUERY_TARGET);
+	if (tgt->protocol == IBMVFC_PROTO_SCSI)
+		query_tgt->common.opcode = cpu_to_be32(IBMVFC_QUERY_TARGET);
+	else
+		query_tgt->common.opcode = cpu_to_be32(IBMVFC_NVMF_QUERY_TARGET);
 	query_tgt->common.length = cpu_to_be16(sizeof(*query_tgt));
 	query_tgt->wwpn = cpu_to_be64(tgt->ids.port_name);
 
@@ -4840,7 +4980,7 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
 		kref_put(&tgt->kref, ibmvfc_release_tgt);
 	} else
-		tgt_dbg(tgt, "Sent Query Target\n");
+		tgt_dbg(tgt, "Sent %s Query Target\n", proto_type[tgt->protocol]);
 }
 
 /**
@@ -4852,25 +4992,32 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
  *	0 on success / other on failure
  **/
 static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
-			       struct ibmvfc_discover_targets_entry *target)
+			       struct ibmvfc_discover_targets_entry *target,
+			       enum ibmvfc_protocol protocol)
 {
 	struct ibmvfc_target *stgt = NULL;
 	struct ibmvfc_target *wtgt = NULL;
 	struct ibmvfc_target *tgt;
+	struct ibmvfc_channels *channels;
 	unsigned long flags;
 	u64 scsi_id = be32_to_cpu(target->scsi_id) & IBMVFC_DISC_TGT_SCSI_ID_MASK;
 	u64 wwpn = be64_to_cpu(target->wwpn);
 
+	if (protocol == IBMVFC_PROTO_SCSI)
+		channels = &vhost->scsi_scrqs;
+	else
+		channels = &vhost->nvme_scrqs;
+
 	/* Look to see if we already have a target allocated for this SCSI ID or WWPN */
 	spin_lock_irqsave(vhost->host->host_lock, flags);
-	list_for_each_entry(tgt, &vhost->targets, queue) {
+	list_for_each_entry(tgt, &channels->targets, queue) {
 		if (tgt->wwpn == wwpn) {
 			wtgt = tgt;
 			break;
 		}
 	}
 
-	list_for_each_entry(tgt, &vhost->targets, queue) {
+	list_for_each_entry(tgt, &channels->targets, queue) {
 		if (tgt->scsi_id == scsi_id) {
 			stgt = tgt;
 			break;
@@ -4918,6 +5065,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
 
 	tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO);
 	memset(tgt, 0, sizeof(*tgt));
+	tgt->protocol = protocol;
 	tgt->scsi_id = scsi_id;
 	tgt->wwpn = wwpn;
 	tgt->vhost = vhost;
@@ -4927,7 +5075,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
 	ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
 	spin_lock_irqsave(vhost->host->host_lock, flags);
 	tgt->cancel_key = vhost->task_set++;
-	list_add_tail(&tgt->queue, &vhost->targets);
+	list_add_tail(&tgt->queue, &channels->targets);
 
 unlock_out:
 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -4945,8 +5093,12 @@ static int ibmvfc_alloc_targets(struct ibmvfc_host *vhost)
 {
 	int i, rc;
 
-	for (i = 0, rc = 0; !rc && i < vhost->num_targets; i++)
-		rc = ibmvfc_alloc_target(vhost, &vhost->scsi_scrqs.disc_buf[i]);
+	for (i = 0, rc = 0; !rc && i < vhost->scsi_scrqs.num_targets; i++)
+		rc = ibmvfc_alloc_target(vhost, &vhost->scsi_scrqs.disc_buf[i],
+					 vhost->scsi_scrqs.protocol);
+	for (i = 0; !rc && i < vhost->nvme_scrqs.num_targets; i++)
+		rc = ibmvfc_alloc_target(vhost, &vhost->nvme_scrqs.disc_buf[i],
+					 vhost->nvme_scrqs.protocol);
 
 	return rc;
 }
@@ -4960,19 +5112,30 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
 {
 	struct ibmvfc_host *vhost = evt->vhost;
 	struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets;
+	struct ibmvfc_channels *channels;
 	u32 mad_status = be16_to_cpu(rsp->common.status);
+	u32 opcode = be32_to_cpu(rsp->common.opcode);
 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
 
+	if (opcode == IBMVFC_DISC_TARGETS)
+		channels = &vhost->scsi_scrqs;
+	else
+		channels = &vhost->nvme_scrqs;
+
 	switch (mad_status) {
 	case IBMVFC_MAD_SUCCESS:
-		ibmvfc_dbg(vhost, "Discover Targets succeeded\n");
-		vhost->num_targets = min_t(u32, be32_to_cpu(rsp->num_written),
-					   max_targets);
+		ibmvfc_dbg(vhost, "Discover %s Targets succeeded\n",
+			   proto_type[channels->protocol]);
+		channels->num_targets = min_t(u32, be32_to_cpu(rsp->num_written),
+					      max_targets);
+		ibmvfc_dbg(vhost, "%d %s targets found\n", channels->num_targets,
+			   proto_type[channels->protocol]);
 		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS);
 		break;
 	case IBMVFC_MAD_FAILED:
 		level += ibmvfc_retry_host_init(vhost);
-		ibmvfc_log(vhost, level, "Discover Targets failed: %s (%x:%x)\n",
+		ibmvfc_log(vhost, level, "Discover %s Targets failed: %s (%x:%x)\n",
+			   proto_type[channels->protocol],
 			   ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
 			   be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
 		break;
@@ -4988,6 +5151,32 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
 	wake_up(&vhost->work_wait_q);
 }
 
+static struct ibmvfc_event *ibmvfc_get_disc_event(struct ibmvfc_channels *channels)
+{
+	struct ibmvfc_discover_targets *mad;
+	struct ibmvfc_host *vhost = ibmvfc_channels_to_vhost(channels);
+	struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+
+	if (!evt)
+		return NULL;
+
+	ibmvfc_init_event(evt, ibmvfc_discover_targets_done, IBMVFC_MAD_FORMAT);
+	mad = &evt->iu.discover_targets;
+	memset(mad, 0, sizeof(*mad));
+	mad->common.version = cpu_to_be32(1);
+	if (channels->protocol == IBMVFC_PROTO_SCSI)
+		mad->common.opcode = cpu_to_be32(IBMVFC_DISC_TARGETS);
+	else
+		mad->common.opcode = cpu_to_be32(IBMVFC_DISC_NVMF_TARGETS);
+	mad->common.length = cpu_to_be16(sizeof(*mad));
+	mad->bufflen = cpu_to_be32(channels->disc_buf_sz);
+	mad->buffer.va = cpu_to_be64(channels->disc_buf_dma);
+	mad->buffer.len = cpu_to_be32(channels->disc_buf_sz);
+	mad->flags = cpu_to_be32(IBMVFC_DISC_TGT_PORT_ID_WWPN_LIST);
+
+	return evt;
+}
+
 /**
  * ibmvfc_discover_targets - Send Discover Targets MAD
  * @vhost:	ibmvfc host struct
@@ -4995,30 +5184,168 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
  **/
 static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
 {
-	struct ibmvfc_discover_targets *mad;
-	struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+	struct ibmvfc_event *evt = ibmvfc_get_disc_event(&vhost->scsi_scrqs);
 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
 
 	if (!evt) {
-		ibmvfc_log(vhost, level, "Discover Targets failed: no available events\n");
+		ibmvfc_log(vhost, level, "Discover SCSI Targets failed: no available events\n");
 		ibmvfc_hard_reset_host(vhost);
 		return;
 	}
 
-	ibmvfc_init_event(evt, ibmvfc_discover_targets_done, IBMVFC_MAD_FORMAT);
-	mad = &evt->iu.discover_targets;
+	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
+
+	if (!ibmvfc_send_event(evt, vhost, default_timeout))
+		ibmvfc_dbg(vhost, "Sent discover SCSI targets\n");
+	else
+		goto link_down;
+
+	if (!ibmvfc_nvme_active(vhost))
+		return;
+
+	evt = ibmvfc_get_disc_event(&vhost->nvme_scrqs);
+	if (!evt) {
+		ibmvfc_log(vhost, level, "Discover NVMe Targets failed: no available events\n");
+		ibmvfc_hard_reset_host(vhost);
+		return;
+	}
+
+	if (!ibmvfc_send_event(evt, vhost, default_timeout))
+		ibmvfc_dbg(vhost, "Sent discover NVMe targets\n");
+	else
+		goto link_down;
+
+	return;
+
+link_down:
+	ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+}
+
+static void ibmvfc_fabric_login_nvme_done(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_host *vhost = evt->vhost;
+	struct ibmvfc_fabric_login_mad *rsp = &evt->xfer_iu->fabric_login;
+	u32 mad_status = be16_to_cpu(rsp->common.status);
+	int level = IBMVFC_DEFAULT_LOG_LEVEL;
+
+	switch (mad_status) {
+	case IBMVFC_MAD_SUCCESS:
+		fc_host_port_id(vhost->host) = be64_to_cpu(rsp->nport_id);
+		ibmvfc_nvme_register(vhost);
+		ibmvfc_dbg(vhost, "NVMe fabric login succeeded\n");
+		break;
+	case IBMVFC_MAD_FAILED:
+		if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
+			level += ibmvfc_retry_host_init(vhost);
+		else
+			ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+		ibmvfc_log(vhost, level, "NVMe fabric login failed: %s (%x:%x)\n",
+			   ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
+			   be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
+		ibmvfc_free_event(evt);
+		return;
+	case IBMVFC_MAD_DRIVER_FAILED:
+		ibmvfc_free_event(evt);
+		return;
+	default:
+		dev_err(vhost->dev, "Invalid NVMe fabric login response: 0x%x\n", mad_status);
+		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+		break;
+	}
+
+	ibmvfc_free_event(evt);
+
+	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+	wake_up(&vhost->work_wait_q);
+}
+
+static void ibmvfc_fabric_login_nvme(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_fabric_login_mad *mad;
+	struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+
+	if (!evt) {
+		ibmvfc_retry_host_init(vhost);
+		return;
+	}
+
+	ibmvfc_init_event(evt, ibmvfc_fabric_login_nvme_done, IBMVFC_MAD_FORMAT);
+	mad = &evt->iu.fabric_login;
 	memset(mad, 0, sizeof(*mad));
 	mad->common.version = cpu_to_be32(1);
-	mad->common.opcode = cpu_to_be32(IBMVFC_DISC_TARGETS);
+	mad->common.opcode = cpu_to_be32(IBMVFC_NVMF_FABRIC_LOGIN);
 	mad->common.length = cpu_to_be16(sizeof(*mad));
-	mad->bufflen = cpu_to_be32(vhost->scsi_scrqs.disc_buf_sz);
-	mad->buffer.va = cpu_to_be64(vhost->scsi_scrqs.disc_buf_dma);
-	mad->buffer.len = cpu_to_be32(vhost->scsi_scrqs.disc_buf_sz);
-	mad->flags = cpu_to_be32(IBMVFC_DISC_TGT_PORT_ID_WWPN_LIST);
+
+	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
+
+	if (!ibmvfc_send_event(evt, vhost, default_timeout))
+		ibmvfc_dbg(vhost, "Send NVMe fabric login\n");
+	else
+		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+}
+
+static void ibmvfc_fabric_login_scsi_done(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_host *vhost = evt->vhost;
+	struct ibmvfc_fabric_login_mad *rsp = &evt->xfer_iu->fabric_login;
+	u32 mad_status = be16_to_cpu(rsp->common.status);
+	int level = IBMVFC_DEFAULT_LOG_LEVEL;
+
+	switch (mad_status) {
+	case IBMVFC_MAD_SUCCESS:
+		fc_host_port_id(vhost->host) = be64_to_cpu(rsp->nport_id);
+		ibmvfc_dbg(vhost, "SCSI fabric login succeeded\n");
+		break;
+	case IBMVFC_MAD_FAILED:
+		if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
+			level += ibmvfc_retry_host_init(vhost);
+		else
+			ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+		ibmvfc_log(vhost, level, "SCSI fabric login failed: %s (%x:%x)\n",
+			   ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
+			   be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
+		ibmvfc_free_event(evt);
+		return;
+	case IBMVFC_MAD_DRIVER_FAILED:
+		ibmvfc_free_event(evt);
+		return;
+	default:
+		dev_err(vhost->dev, "Invalid SCSI fabric login response: 0x%x\n", mad_status);
+		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+		break;
+	}
+
+	ibmvfc_free_event(evt);
+
+	if (vhost->do_nvme_login) {
+		ibmvfc_fabric_login_nvme(vhost);
+	} else {
+		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+		wake_up(&vhost->work_wait_q);
+	}
+}
+
+static void ibmvfc_fabric_login_scsi(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_fabric_login_mad *mad;
+	struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+
+	if (!evt) {
+		ibmvfc_retry_host_init(vhost);
+		return;
+	}
+
+	ibmvfc_init_event(evt, ibmvfc_fabric_login_scsi_done, IBMVFC_MAD_FORMAT);
+	mad = &evt->iu.fabric_login;
+	memset(mad, 0, sizeof(*mad));
+	mad->common.version = cpu_to_be32(1);
+	mad->common.opcode = cpu_to_be32(IBMVFC_FABRIC_LOGIN);
+	mad->common.length = cpu_to_be16(sizeof(*mad));
+
 	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
 
 	if (!ibmvfc_send_event(evt, vhost, default_timeout))
-		ibmvfc_dbg(vhost, "Sent discover targets\n");
+		ibmvfc_dbg(vhost, "Send SCSI fabric login\n");
 	else
 		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
 }
@@ -5027,10 +5354,11 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
 {
 	struct ibmvfc_host *vhost = evt->vhost;
 	struct ibmvfc_channel_setup *setup = vhost->channel_setup_buf;
-	struct ibmvfc_channels *scrqs = &vhost->scsi_scrqs;
+	struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;
+	struct ibmvfc_channels *nvme = &vhost->nvme_scrqs;
 	u32 mad_status = be16_to_cpu(evt->xfer_iu->channel_setup.common.status);
 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
-	int flags, active_queues, i;
+	int flags, i;
 
 	ibmvfc_free_event(evt);
 
@@ -5039,22 +5367,28 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
 		ibmvfc_dbg(vhost, "Channel Setup succeeded\n");
 		flags = be32_to_cpu(setup->flags);
 		vhost->do_enquiry = 0;
-		active_queues = be32_to_cpu(setup->num_scsi_subq_channels);
-		scrqs->active_queues = active_queues;
+		scsi->active_queues = be32_to_cpu(setup->num_scsi_subq_channels);
+		nvme->active_queues = be32_to_cpu(setup->num_nvme_subq_channels);
 
 		if (flags & IBMVFC_CHANNELS_CANCELED) {
 			ibmvfc_dbg(vhost, "Channels Canceled\n");
 			vhost->using_channels = 0;
-		} else {
-			if (active_queues)
-				vhost->using_channels = 1;
-			for (i = 0; i < active_queues; i++)
-				scrqs->scrqs[i].vios_cookie =
-					be64_to_cpu(setup->channel_handles[i]);
-
-			ibmvfc_dbg(vhost, "Using %u channels\n",
-				   vhost->scsi_scrqs.active_queues);
+			break;
 		}
+
+		if (scsi->active_queues || nvme->active_queues)
+			vhost->using_channels = 1;
+		for (i = 0; i < scsi->active_queues; i++)
+			scsi->scrqs[i].vios_cookie =
+				be64_to_cpu(setup->channel_handles[i]);
+		for (i = 0; i < nvme->active_queues; i++)
+			nvme->scrqs[i].vios_cookie =
+				be64_to_cpu(setup->channel_handles[scsi->active_queues + i]);
+
+		ibmvfc_dbg(vhost, "Using %u SCSI channels\n",
+			   scsi->active_queues);
+		ibmvfc_dbg(vhost, "Using %u NVMe channels\n",
+			   nvme->active_queues);
 		break;
 	case IBMVFC_MAD_FAILED:
 		level += ibmvfc_retry_host_init(vhost);
@@ -5069,8 +5403,12 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
 		return;
 	}
 
-	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
-	wake_up(&vhost->work_wait_q);
+	if (vhost->do_scsi_login) {
+		ibmvfc_fabric_login_scsi(vhost);
+	} else {
+		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+		wake_up(&vhost->work_wait_q);
+	}
 }
 
 static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
@@ -5078,9 +5416,12 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
 	struct ibmvfc_channel_setup_mad *mad;
 	struct ibmvfc_channel_setup *setup_buf = vhost->channel_setup_buf;
 	struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
-	struct ibmvfc_channels *scrqs = &vhost->scsi_scrqs;
-	unsigned int num_channels =
-		min(scrqs->desired_queues, vhost->max_vios_scsi_channels);
+	struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;
+	struct ibmvfc_channels *nvme = &vhost->nvme_scrqs;
+	unsigned int scsi_channels =
+		min(scsi->desired_queues, vhost->max_vios_scsi_channels);
+	unsigned int nvme_channels =
+		min(nvme->desired_queues, vhost->max_vios_nvme_channels);
 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
 	int i;
 
@@ -5091,12 +5432,17 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
 	}
 
 	memset(setup_buf, 0, sizeof(*setup_buf));
-	if (num_channels == 0)
+	if (!scsi_channels && !nvme_channels)
 		setup_buf->flags = cpu_to_be32(IBMVFC_CANCEL_CHANNELS);
 	else {
-		setup_buf->num_scsi_subq_channels = cpu_to_be32(num_channels);
-		for (i = 0; i < num_channels; i++)
-			setup_buf->channel_handles[i] = cpu_to_be64(scrqs->scrqs[i].cookie);
+		setup_buf->num_scsi_subq_channels = cpu_to_be32(scsi_channels);
+		setup_buf->num_nvme_subq_channels = cpu_to_be32(nvme_channels);
+		for (i = 0; i < scsi_channels; i++)
+			setup_buf->channel_handles[i] =
+				cpu_to_be64(scsi->scrqs[i].cookie);
+		for (i = 0; i < nvme_channels; i++)
+			setup_buf->channel_handles[scsi_channels + i] =
+				cpu_to_be64(nvme->scrqs[i].cookie);
 	}
 
 	ibmvfc_init_event(evt, ibmvfc_channel_setup_done, IBMVFC_MAD_FORMAT);
@@ -5127,6 +5473,7 @@ static void ibmvfc_channel_enquiry_done(struct ibmvfc_event *evt)
 	case IBMVFC_MAD_SUCCESS:
 		ibmvfc_dbg(vhost, "Channel Enquiry succeeded\n");
 		vhost->max_vios_scsi_channels = be32_to_cpu(rsp->num_scsi_subq_channels);
+		vhost->max_vios_nvme_channels = be32_to_cpu(rsp->num_nvme_subq_channels);
 		ibmvfc_free_event(evt);
 		break;
 	case IBMVFC_MAD_FAILED:
@@ -5261,8 +5608,14 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
 	vhost->host->can_queue = be32_to_cpu(rsp->max_cmds) - IBMVFC_NUM_INTERNAL_REQ;
 	vhost->host->max_sectors = npiv_max_sectors;
 
-	if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS) && vhost->do_enquiry) {
-		ibmvfc_channel_enquiry(vhost);
+
+	if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS)) {
+		if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_SCSI))
+			vhost->do_scsi_login = 1;
+		if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF))
+			vhost->do_nvme_login = 1;
+		if (vhost->do_enquiry)
+			ibmvfc_channel_enquiry(vhost);
 	} else {
 		vhost->do_enquiry = 0;
 		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
@@ -5323,6 +5676,7 @@ static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt)
 	case IBMVFC_MAD_SUCCESS:
 		if (list_empty(&vhost->crq.sent) &&
 		    vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) {
+			ibmvfc_nvme_unregister(vhost);
 			ibmvfc_init_host(vhost);
 			return;
 		}
@@ -5383,7 +5737,12 @@ static int ibmvfc_dev_init_to_do(struct ibmvfc_host *vhost)
 {
 	struct ibmvfc_target *tgt;
 
-	list_for_each_entry(tgt, &vhost->targets, queue) {
+	list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+		if (tgt->action == IBMVFC_TGT_ACTION_INIT ||
+		    tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
+			return 1;
+	}
+	list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
 		if (tgt->action == IBMVFC_TGT_ACTION_INIT ||
 		    tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
 			return 1;
@@ -5403,7 +5762,12 @@ static int ibmvfc_dev_logo_to_do(struct ibmvfc_host *vhost)
 {
 	struct ibmvfc_target *tgt;
 
-	list_for_each_entry(tgt, &vhost->targets, queue) {
+	list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+		if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT ||
+		    tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
+			return 1;
+	}
+	list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
 		if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT ||
 		    tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
 			return 1;
@@ -5433,10 +5797,16 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
 	case IBMVFC_HOST_ACTION_QUERY_TGTS:
 		if (vhost->discovery_threads == disc_threads)
 			return 0;
-		list_for_each_entry(tgt, &vhost->targets, queue)
+		list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
 			if (tgt->action == IBMVFC_TGT_ACTION_INIT)
 				return 1;
-		list_for_each_entry(tgt, &vhost->targets, queue)
+		list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
+			if (tgt->action == IBMVFC_TGT_ACTION_INIT)
+				return 1;
+		list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
+			if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
+				return 0;
+		list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
 			if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
 				return 0;
 		return 1;
@@ -5444,10 +5814,16 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
 	case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
 		if (vhost->discovery_threads == disc_threads)
 			return 0;
-		list_for_each_entry(tgt, &vhost->targets, queue)
+		list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
+			if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT)
+				return 1;
+		list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
 			if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT)
 				return 1;
-		list_for_each_entry(tgt, &vhost->targets, queue)
+		list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
+			if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
+				return 0;
+		list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
 			if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
 				return 0;
 		return 1;
@@ -5500,6 +5876,51 @@ static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events)
 		fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_LINKUP, 0);
 }
 
+/**
+ * ibmvfc_tgt_add_nvme_rport - Tell the FC transport about a new remote port
+ * @tgt:		ibmvfc target struct
+ *
+ **/
+static void ibmvfc_tgt_add_nvme_rport(struct ibmvfc_target *tgt)
+{
+	struct ibmvfc_host *vhost = tgt->vhost;
+	struct nvme_fc_remote_port *rport;
+	unsigned long flags;
+
+	tgt_dbg(tgt, "Adding NVMe rport\n");
+	ibmvfc_nvme_register_remoteport(tgt);
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	rport = tgt->nvme_remote_port;
+
+	if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
+		tgt_dbg(tgt, "Deleting NVMe rport\n");
+		list_del(&tgt->queue);
+		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
+		spin_unlock_irqrestore(vhost->host->host_lock, flags);
+		ibmvfc_nvme_unregister_remoteport(tgt);
+		timer_delete_sync(&tgt->timer);
+		kref_put(&tgt->kref, ibmvfc_release_tgt);
+		return;
+	} else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
+		tgt_dbg(tgt, "Deleting NVMe rport with outstanding I/O\n");
+		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT);
+		tgt->init_retries = 0;
+		spin_unlock_irqrestore(vhost->host->host_lock, flags);
+		ibmvfc_nvme_unregister_remoteport(tgt);
+		return;
+	} else if (rport && tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
+		spin_unlock_irqrestore(vhost->host->host_lock, flags);
+		return;
+	}
+
+	if (rport) {
+		tgt_dbg(tgt, "NVMe rport add succeeded\n");
+		tgt->target_id = tgt->nvme_remote_port->port_id;
+	} else
+		tgt_dbg(tgt, "NVMe rport add failed\n");
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+}
+
 /**
  * ibmvfc_tgt_add_rport - Tell the FC transport about a new remote port
  * @tgt:		ibmvfc target struct
@@ -5564,6 +5985,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
 	struct ibmvfc_target *tgt;
 	unsigned long flags;
 	struct fc_rport *rport;
+	struct nvme_fc_remote_port *nvme_rport;
 	LIST_HEAD(purge);
 	int rc;
 
@@ -5579,6 +6001,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
 		list_splice_init(&vhost->purge, &purge);
 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
 		ibmvfc_complete_purge(&purge);
+		ibmvfc_nvme_unregister(vhost);
 		rc = ibmvfc_reset_crq(vhost);
 
 		spin_lock_irqsave(vhost->host->host_lock, flags);
@@ -5635,12 +6058,20 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
 			vhost->job_step(vhost);
 		break;
 	case IBMVFC_HOST_ACTION_QUERY:
-		list_for_each_entry(tgt, &vhost->targets, queue)
+		list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
+			ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target);
+		list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
 			ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target);
 		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY_TGTS);
 		break;
 	case IBMVFC_HOST_ACTION_QUERY_TGTS:
-		list_for_each_entry(tgt, &vhost->targets, queue) {
+		list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+			if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
+				tgt->job_step(tgt);
+				break;
+			}
+		}
+		list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
 			if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
 				tgt->job_step(tgt);
 				break;
@@ -5652,7 +6083,13 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
 		break;
 	case IBMVFC_HOST_ACTION_TGT_DEL:
 	case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
-		list_for_each_entry(tgt, &vhost->targets, queue) {
+		list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+			if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
+				tgt->job_step(tgt);
+				break;
+			}
+		}
+		list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
 			if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
 				tgt->job_step(tgt);
 				break;
@@ -5664,7 +6101,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
 			return;
 		}
 
-		list_for_each_entry(tgt, &vhost->targets, queue) {
+		list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
 			if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
 				tgt_dbg(tgt, "Deleting rport\n");
 				rport = tgt->rport;
@@ -5702,6 +6139,30 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
 			}
 		}
 
+		list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
+			if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
+				tgt_dbg(tgt, "Deleting NVMe rport\n");
+				nvme_rport = tgt->nvme_remote_port;
+				list_del(&tgt->queue);
+				ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
+				spin_unlock_irqrestore(vhost->host->host_lock, flags);
+				if (nvme_rport)
+					ibmvfc_nvme_unregister_remoteport(tgt);
+				timer_delete_sync(&tgt->timer);
+				kref_put(&tgt->kref, ibmvfc_release_tgt);
+				return;
+			} else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
+				tgt_dbg(tgt, "Deleting NVMe rport with outstanding I/O\n");
+				nvme_rport = tgt->nvme_remote_port;
+				ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT);
+				tgt->init_retries = 0;
+				spin_unlock_irqrestore(vhost->host->host_lock, flags);
+				if (nvme_rport)
+					ibmvfc_nvme_unregister_remoteport(tgt);
+				return;
+			}
+		}
+
 		if (vhost->state == IBMVFC_INITIALIZING) {
 			if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) {
 				if (vhost->reinit) {
@@ -5739,7 +6200,13 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
 		spin_lock_irqsave(vhost->host->host_lock, flags);
 		break;
 	case IBMVFC_HOST_ACTION_TGT_INIT:
-		list_for_each_entry(tgt, &vhost->targets, queue) {
+		list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+			if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
+				tgt->job_step(tgt);
+				break;
+			}
+		}
+		list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
 			if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
 				tgt->job_step(tgt);
 				break;
@@ -6094,6 +6561,13 @@ static void ibmvfc_init_sub_crqs(struct ibmvfc_host *vhost)
 
 	ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs);
 
+	if (vhost->nvme_enabled) {
+		if (ibmvfc_alloc_channels(vhost, &vhost->nvme_scrqs))
+			vhost->nvme_enabled = 0;
+		else
+			ibmvfc_reg_sub_crqs(vhost, &vhost->nvme_scrqs);
+	}
+
 	LEAVE;
 }
 
@@ -6122,8 +6596,10 @@ static void ibmvfc_release_sub_crqs(struct ibmvfc_host *vhost)
 		return;
 
 	ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs);
-
 	ibmvfc_release_channels(vhost, &vhost->scsi_scrqs);
+
+	ibmvfc_dereg_sub_crqs(vhost, &vhost->nvme_scrqs);
+	ibmvfc_release_channels(vhost, &vhost->nvme_scrqs);
 	LEAVE;
 }
 
@@ -6148,6 +6624,7 @@ static void ibmvfc_free_mem(struct ibmvfc_host *vhost)
 	mempool_destroy(vhost->tgt_pool);
 	kfree(vhost->trace);
 	ibmvfc_free_disc_buf(vhost->dev, &vhost->scsi_scrqs);
+	ibmvfc_free_disc_buf(vhost->dev, &vhost->nvme_scrqs);
 	dma_free_coherent(vhost->dev, sizeof(*vhost->login_buf),
 			  vhost->login_buf, vhost->login_buf_dma);
 	dma_free_coherent(vhost->dev, sizeof(*vhost->channel_setup_buf),
@@ -6210,12 +6687,15 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost)
 	if (ibmvfc_alloc_disc_buf(dev, &vhost->scsi_scrqs))
 		goto free_login_buffer;
 
+	if (ibmvfc_alloc_disc_buf(dev, &vhost->nvme_scrqs))
+		goto free_scsi_disc_buffer;
+
 	vhost->trace = kzalloc_objs(struct ibmvfc_trace_entry,
 				    IBMVFC_NUM_TRACE_ENTRIES);
 	atomic_set(&vhost->trace_index, -1);
 
 	if (!vhost->trace)
-		goto free_scsi_disc_buffer;
+		goto free_nvme_disc_buffer;
 
 	vhost->tgt_pool = mempool_create_kmalloc_pool(IBMVFC_TGT_MEMPOOL_SZ,
 						      sizeof(struct ibmvfc_target));
@@ -6241,6 +6721,8 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost)
 	mempool_destroy(vhost->tgt_pool);
 free_trace:
 	kfree(vhost->trace);
+free_nvme_disc_buffer:
+	ibmvfc_free_disc_buf(dev, &vhost->nvme_scrqs);
 free_scsi_disc_buffer:
 	ibmvfc_free_disc_buf(dev, &vhost->scsi_scrqs);
 free_login_buffer:
@@ -6266,6 +6748,7 @@ static void ibmvfc_rport_add_thread(struct work_struct *work)
 						 rport_add_work_q);
 	struct ibmvfc_target *tgt;
 	struct fc_rport *rport;
+	struct nvme_fc_remote_port *nvme_rport;
 	unsigned long flags;
 	int did_work;
 
@@ -6276,7 +6759,7 @@ static void ibmvfc_rport_add_thread(struct work_struct *work)
 		if (vhost->state != IBMVFC_ACTIVE)
 			break;
 
-		list_for_each_entry(tgt, &vhost->targets, queue) {
+		list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
 			if (tgt->add_rport) {
 				did_work = 1;
 				tgt->add_rport = 0;
@@ -6299,7 +6782,28 @@ static void ibmvfc_rport_add_thread(struct work_struct *work)
 				break;
 			}
 		}
-	} while(did_work);
+
+		list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
+			if (tgt->add_rport) {
+				did_work = 1;
+				tgt->add_rport = 0;
+				kref_get(&tgt->kref);
+				nvme_rport = tgt->nvme_remote_port;
+				if (!nvme_rport) {
+					spin_unlock_irqrestore(vhost->host->host_lock, flags);
+					ibmvfc_tgt_add_nvme_rport(tgt);
+				} else {
+					spin_unlock_irqrestore(vhost->host->host_lock, flags);
+					if (IS_ENABLED(CONFIG_NVME_FC))
+						nvme_fc_rescan_remoteport(nvme_rport);
+				}
+
+				kref_put(&tgt->kref, ibmvfc_release_tgt);
+				spin_lock_irqsave(vhost->host->host_lock, flags);
+				break;
+			}
+		}
+	} while (did_work);
 
 	if (vhost->state == IBMVFC_ACTIVE)
 		vhost->scan_complete = 1;
@@ -6322,7 +6826,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
 	struct device *dev = &vdev->dev;
 	int rc = -ENOMEM;
 	unsigned int online_cpus = num_online_cpus();
-	unsigned int max_scsi_queues = min((unsigned int)IBMVFC_MAX_SCSI_QUEUES, online_cpus);
+	unsigned int max_scsi_queues = min_t(unsigned int, IBMVFC_MAX_SCSI_QUEUES, online_cpus);
+	unsigned int max_nvme_queues = min_t(unsigned int, IBMVFC_MAX_NVME_QUEUES, online_cpus);
 
 	ENTER;
 	shost = scsi_host_alloc(&driver_template, sizeof(*vhost));
@@ -6341,7 +6846,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
 	shost->nr_hw_queues = mq_enabled ? min(max_scsi_queues, nr_scsi_hw_queues) : 1;
 
 	vhost = shost_priv(shost);
-	INIT_LIST_HEAD(&vhost->targets);
+	INIT_LIST_HEAD(&vhost->scsi_scrqs.targets);
+	INIT_LIST_HEAD(&vhost->nvme_scrqs.targets);
 	INIT_LIST_HEAD(&vhost->purge);
 	sprintf(vhost->name, IBMVFC_NAME);
 	vhost->host = shost;
@@ -6356,6 +6862,10 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
 	vhost->scsi_scrqs.protocol = IBMVFC_PROTO_SCSI;
 	vhost->using_channels = 0;
 	vhost->do_enquiry = 1;
+	vhost->nvme_enabled = mq_enabled ? nvme_enabled : 0;
+	vhost->nvme_scrqs.desired_queues = min(max_nvme_queues, nr_nvme_channels);
+	vhost->nvme_scrqs.max_queues = max_nvme_queues;
+	vhost->nvme_scrqs.protocol = IBMVFC_PROTO_NVME;
 	vhost->scan_timeout = 0;
 
 	strcpy(vhost->partition_name, "UNKNOWN");
@@ -6441,6 +6951,7 @@ static void ibmvfc_remove(struct vio_dev *vdev)
 
 	ibmvfc_wait_while_resetting(vhost);
 	kthread_stop(vhost->work_thread);
+	flush_work(&vhost->rport_add_work_q);
 	fc_remove_host(vhost->host);
 	scsi_remove_host(vhost->host);
 
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
new file mode 100644
index 000000000000..54f80b6265d4
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -0,0 +1,573 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * ibmvfc-nvme.c -- IBM Power Virtual Fibre Channel NVMeoF HBA driver
+ *
+ * Written By: Tyrel Datwyler <tyreld@linux.ibm.com>, IBM Corporation
+ *
+ * Copyright (C) IBM Corporation, 2026
+ */
+
+#include <linux/dmapool.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "ibmvfc-nvme.h"
+
+static unsigned int default_timeout = IBMVFC_DEFAULT_TIMEOUT;
+
+static void ibmvfc_nvme_localport_delete(struct nvme_fc_local_port *lport)
+{
+	struct ibmvfc_host *vhost = lport->private;
+
+	vhost->nvme_local_port = NULL;
+	complete(&vhost->nvme_delete_done);
+}
+
+static void ibmvfc_nvme_remoteport_delete(struct nvme_fc_remote_port *rport)
+{
+	struct ibmvfc_target *tgt = rport->private;
+
+	tgt->nvme_remote_port = NULL;
+	complete(&tgt->nvme_delete_done);
+}
+
+static int ibmvfc_nvme_create_queue(struct nvme_fc_local_port *lport, unsigned int qidx,
+				    u16 qsize, void **handle)
+{
+	struct ibmvfc_host *vhost = lport->private;
+	struct ibmvfc_nvme_qhandle *qhandle;
+
+	if (!vhost->nvme_scrqs.active_queues)
+		return -ENODEV;
+
+	qhandle = kzalloc_obj(struct ibmvfc_nvme_qhandle);
+	if (!qhandle)
+		return -ENOMEM;
+
+	qhandle->cpu_id = raw_smp_processor_id();
+	qhandle->qidx = qidx;
+
+	/* Admin and first IO queue are both mapped to index 0 */
+	if (qidx)
+		qhandle->index = (qidx - 1) % vhost->nvme_scrqs.active_queues;
+	else
+		qhandle->index = qidx;
+
+	qhandle->queue = &vhost->nvme_scrqs.scrqs[qhandle->index];
+
+	*handle = qhandle;
+	return 0;
+}
+
+static void ibmvfc_nvme_delete_queue(struct nvme_fc_local_port *lport, unsigned int qidx,
+				     void *handle)
+{
+	kfree(handle);
+}
+
+static void ibmvfc_ls_req_done(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_target *tgt = evt->tgt;
+	struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru;
+	struct fcnvme_ls_rqst_w0 *ls_rqst;
+	struct fcnvme_ls_cr_assoc_acc *ls_resp;
+	u32 status = be16_to_cpu(mad->common.status);
+	int rc = 0;
+
+	ls_rqst = (struct fcnvme_ls_rqst_w0 *)evt->ls_req->rqstaddr;
+	ls_resp = (struct fcnvme_ls_cr_assoc_acc *)evt->ls_req->rspaddr;
+
+	switch (status) {
+	case IBMVFC_MAD_SUCCESS:
+		tgt_dbg(tgt, "ls_req succeeded\n");
+		if ((ls_rqst->ls_cmd == FCNVME_LS_CREATE_ASSOCIATION) &&
+		    (ls_resp->hdr.w0.ls_cmd == FCNVME_LS_ACC)) {
+			tgt->assoc_id = be64_to_cpu(ls_resp->associd.association_id);
+			tgt_dbg(tgt, "assoc_id 0x%llx\n", tgt->assoc_id);
+		}
+		break;
+	case IBMVFC_MAD_DRIVER_FAILED:
+		break;
+	case IBMVFC_MAD_FAILED:
+	default:
+		tgt_info(tgt, "ls_req failed: %s (%x:%x) rc=0x%02X\n",
+			 ibmvfc_get_cmd_error(be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error)),
+			 be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error), status);
+		break;
+	}
+
+	if (status)
+		rc = -EIO;
+
+	evt->ls_req->done(evt->ls_req, rc);
+
+	kref_put(&tgt->kref, ibmvfc_release_tgt);
+	ibmvfc_free_event(evt);
+}
+
+static void ibmvfc_init_ls_req(struct ibmvfc_event *evt, struct nvmefc_ls_req *ls_req)
+{
+	struct ibmvfc_passthru_mad *mad = &evt->iu.passthru;
+
+	memset(mad, 0, sizeof(*mad));
+	mad->common.version = cpu_to_be32(2);
+	mad->common.opcode = cpu_to_be32(IBMVFC_NVMF_PASSTHRU);
+	mad->common.length = cpu_to_be16(sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu));
+	mad->cmd_ioba.va = cpu_to_be64((u64)be64_to_cpu(evt->crq.ioba) +
+				       offsetof(struct ibmvfc_passthru_mad, iu));
+	mad->cmd_ioba.len = cpu_to_be32(sizeof(mad->iu));
+	mad->iu.cmd_len = cpu_to_be32(ls_req->rqstlen);
+	mad->iu.rsp_len = cpu_to_be32(ls_req->rsplen);
+	mad->iu.cmd.va = cpu_to_be64(ls_req->rqstdma);
+	mad->iu.cmd.len = cpu_to_be32(ls_req->rqstlen);
+	mad->iu.rsp.va = cpu_to_be64(ls_req->rspdma);
+	mad->iu.rsp.len = cpu_to_be32(ls_req->rsplen);
+}
+
+static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port *lport,
+			      struct nvme_fc_remote_port *rport,
+			      struct nvmefc_ls_req *ls_req)
+{
+	struct ibmvfc_host *vhost = lport->private;
+	struct ibmvfc_target *tgt = rport->private;
+	struct ibmvfc_passthru_mad *mad;
+	struct ibmvfc_event *evt;
+
+	kref_get(&tgt->kref);
+	evt = ibmvfc_get_event(&vhost->crq);
+	if (!evt) {
+		kref_put(&tgt->kref, ibmvfc_release_tgt);
+		return -EBUSY;
+	}
+
+	ibmvfc_init_event(evt, ibmvfc_ls_req_done, IBMVFC_MAD_FORMAT);
+	evt->tgt = tgt;
+	evt->ls_req = ls_req;
+	ls_req->private = evt;
+
+	ibmvfc_init_ls_req(evt, ls_req);
+	mad = &evt->iu.passthru;
+	mad->iu.flags = cpu_to_be32(IBMVFC_FC4_LS_DSC_CTRL);
+	mad->iu.scsi_id = cpu_to_be64(tgt->scsi_id);
+	mad->iu.cancel_key = cpu_to_be32((u64)evt);
+	mad->iu.target_wwpn = cpu_to_be64(tgt->wwpn);
+
+	ibmvfc_dbg(vhost, "nvme_ls_req\n");
+	if (ibmvfc_send_event(evt, vhost, IBMVFC_FC4_LS_PLUS_CANCEL_TIMEOUT)) {
+		kref_put(&tgt->kref, ibmvfc_release_tgt);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void ibmvfc_sync_nvme_completion(struct ibmvfc_event *evt)
+{
+	/* copy the response back */
+	if (evt->sync_iu)
+		*evt->sync_iu = *evt->xfer_iu;
+
+	complete(&evt->comp);
+}
+
+static void ibmvfc_init_ls_abort(struct ibmvfc_event *evt, struct nvmefc_ls_req *ls_abort)
+{
+	struct ibmvfc_tmf *tmf;
+	struct ibmvfc_event *abt_evt = ls_abort->private;
+	struct ibmvfc_target *tgt = abt_evt->tgt;
+	struct ibmvfc_host *vhost = evt->vhost;
+
+	tmf = &evt->iu.tmf;
+	memset(tmf, 0, sizeof(*tmf));
+	tmf->common.version = cpu_to_be32(2);
+	tmf->target_wwpn = cpu_to_be64(tgt->wwpn);
+	tmf->common.opcode = cpu_to_be32(IBMVFC_NVMF_TMF_MAD);
+	tmf->common.length = cpu_to_be16(sizeof(*tmf));
+	if (vhost->state != IBMVFC_ACTIVE)
+		if (!ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPRESS_ABTS))
+			tmf->flags = cpu_to_be32(IBMVFC_TMF_SUPPRESS_ABTS);
+	tmf->cancel_key = cpu_to_be32((u64)abt_evt);
+	tmf->my_cancel_key = cpu_to_be32((u64)evt);
+	tmf->assoc_id = cpu_to_be64(tgt->assoc_id);
+
+	init_completion(&evt->comp);
+}
+
+static void ibmvfc_nvme_ls_abort(struct nvme_fc_local_port *lport,
+				struct nvme_fc_remote_port *rport,
+				struct nvmefc_ls_req *ls_abort)
+{
+	struct ibmvfc_host *vhost = lport->private;
+	struct ibmvfc_target *tgt = rport->private;
+	struct ibmvfc_event *evt;
+	union ibmvfc_iu rsp;
+	unsigned long flags;
+	u16 status;
+
+	evt = ibmvfc_get_event(&vhost->crq);
+	if (!vhost->logged_in || !evt)
+		return;
+
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	kref_get(&tgt->kref);
+	ibmvfc_init_event(evt, ibmvfc_sync_nvme_completion, IBMVFC_MAD_FORMAT);
+	ibmvfc_init_ls_abort(evt, ls_abort);
+	evt->sync_iu = &rsp;
+
+	if (ibmvfc_send_event(evt, vhost, default_timeout))
+		goto out;
+
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+	wait_for_completion(&evt->comp);
+	status = be16_to_cpu(rsp.mad_common.status);
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	ibmvfc_free_event(evt);
+out:
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+	ibmvfc_dbg(vhost, "ls_abort: cancel failed with rc=%x\n", status);
+	kref_put(&tgt->kref, ibmvfc_release_tgt);
+}
+
+static void ibmvfc_nvme_done(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
+	struct nvmefc_fcp_req *fcp_req = evt->fcp_req;
+	struct nvme_fc_ersp_iu *ersp = (struct nvme_fc_ersp_iu *)fcp_req->rspaddr;
+	struct nvme_completion *cqe = &ersp->cqe;
+	struct nvme_command *sqe = &((struct nvme_fc_cmd_iu *)fcp_req->cmdaddr)->sqe;
+
+	ibmvfc_dbg(evt->vhost, "fc_done: (%x:%x)\n", be16_to_cpu(vfc_cmd->status),
+		   be16_to_cpu(vfc_cmd->error));
+	ibmvfc_dbg(evt->vhost, "fc_done: cmdlen: %d, rsplen %d, payload_len %d\n",
+		   fcp_req->cmdlen, fcp_req->rsplen, fcp_req->payload_length);
+
+	fcp_req->status = 0;
+	if (!vfc_cmd->status) {
+		fcp_req->rcv_rsplen = NVME_FC_SIZEOF_ZEROS_RSP;
+		fcp_req->transferred_length = fcp_req->payload_length;
+	} else if (be16_to_cpu(vfc_cmd->status) & IBMVFC_FC_NVME_STATUS) {
+		fcp_req->rcv_rsplen = sizeof(struct nvme_fc_ersp_iu);
+		fcp_req->transferred_length = be32_to_cpu(ersp->xfrd_len);
+		if (be16_to_cpu(vfc_cmd->error) & IBMVFC_NVMS_VALID_NODMA_CQE)
+			cqe->command_id = sqe->common.command_id;
+	} else {
+		fcp_req->rcv_rsplen = 0;
+		fcp_req->transferred_length = 0;
+		fcp_req->status = NVME_SC_INTERNAL;
+	}
+
+	fcp_req->done(fcp_req);
+	ibmvfc_free_event(evt);
+}
+
+static struct ibmvfc_cmd *ibmvfc_nvme_init_vfc_cmd(struct ibmvfc_event *evt,
+						   struct nvme_fc_remote_port *rport,
+						   struct nvmefc_fcp_req *fcp_req)
+{
+	struct ibmvfc_target *tgt = rport->private;
+	struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd;
+
+	memset(vfc_cmd, 0, sizeof(*vfc_cmd));
+
+	vfc_cmd->resp.va = cpu_to_be64(fcp_req->rspdma);
+	vfc_cmd->resp.len = cpu_to_be32(fcp_req->rsplen);
+	vfc_cmd->frame_type = cpu_to_be32(IBMVFC_NVME_FCP_TYPE);
+	vfc_cmd->flags |= cpu_to_be16(IBMVFC_NVMEOF_PROTOCOL);
+	vfc_cmd->payload_len = cpu_to_be32(fcp_req->cmdlen);
+	vfc_cmd->resp_len = cpu_to_be32(fcp_req->rsplen);
+	vfc_cmd->cancel_key = cpu_to_be32((u64)evt);
+	vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name);
+	vfc_cmd->tgt_scsi_id = cpu_to_be64(rport->port_id);
+	vfc_cmd->assoc_id = cpu_to_be64(tgt->assoc_id);
+
+	memcpy(&vfc_cmd->v3nvme.iu, fcp_req->cmdaddr, fcp_req->cmdlen);
+
+	return vfc_cmd;
+}
+
+static void ibmvfc_nvme_map_sg_list(struct nvmefc_fcp_req *fcp_req,
+				    struct srp_direct_buf *md)
+{
+	int i;
+	struct scatterlist *sg;
+
+	for_each_sg(fcp_req->first_sgl, sg, fcp_req->sg_cnt, i) {
+		md[i].va = cpu_to_be64(sg_dma_address(sg));
+		md[i].len = cpu_to_be32(sg_dma_len(sg));
+		md[i].key = 0;
+	}
+}
+
+static int ibmvfc_nvme_map_sg_data(struct nvmefc_fcp_req *fcp_req,
+				    struct ibmvfc_event *evt,
+				    struct ibmvfc_cmd *vfc_cmd)
+{
+	struct srp_direct_buf *data = &vfc_cmd->ioba;
+	struct ibmvfc_host *vhost = evt->vhost;
+
+	if (!fcp_req->sg_cnt) {
+		vfc_cmd->flags |= cpu_to_be16(IBMVFC_NO_MEM_DESC);
+		return 0;
+	}
+
+	if (fcp_req->io_dir == NVMEFC_FCP_WRITE)
+		vfc_cmd->flags |= cpu_to_be16(IBMVFC_WRITE);
+	else
+		vfc_cmd->flags |= cpu_to_be16(IBMVFC_READ);
+
+	if (fcp_req->sg_cnt == 1) {
+		ibmvfc_nvme_map_sg_list(fcp_req, data);
+		return 0;
+	}
+
+	vfc_cmd->flags |= cpu_to_be16(IBMVFC_SCATTERLIST);
+
+	if (!evt->ext_list) {
+		evt->ext_list = dma_pool_alloc(vhost->sg_pool, GFP_ATOMIC,
+					       &evt->ext_list_token);
+
+		if (!evt->ext_list)
+			return -ENOMEM;
+	}
+
+	ibmvfc_nvme_map_sg_list(fcp_req, evt->ext_list);
+
+	data->va = cpu_to_be64(evt->ext_list_token);
+	data->len = cpu_to_be32(fcp_req->sg_cnt * sizeof(struct srp_direct_buf));
+	data->key = 0;
+	return 0;
+}
+
+static int ibmvfc_nvme_fcp_io(struct nvme_fc_local_port *lport,
+			      struct nvme_fc_remote_port *rport,
+			      void *hw_queue_handle,
+			      struct nvmefc_fcp_req *fcp_req)
+{
+	struct ibmvfc_host *vhost = lport->private;
+	struct ibmvfc_nvme_qhandle *qhandle = hw_queue_handle;
+	struct ibmvfc_cmd *vfc_cmd;
+	struct ibmvfc_event *evt;
+	int rc;
+
+	ibmvfc_dbg(vhost, "nvme_fcp_io\n");
+	evt = ibmvfc_get_event(qhandle->queue);
+	if (!evt)
+		return -EBUSY;
+
+	evt->hwq = qhandle->index;
+	ibmvfc_dbg(vhost, "vfc-nvme-mq-%d\n", evt->hwq);
+
+	ibmvfc_init_event(evt, ibmvfc_nvme_done, IBMVFC_CMD_FORMAT);
+	evt->fcp_req = fcp_req;
+	fcp_req->private = evt;
+
+	vfc_cmd = ibmvfc_nvme_init_vfc_cmd(evt, rport, fcp_req);
+
+	vfc_cmd->correlation = cpu_to_be64((u64)evt);
+
+	if (likely(!(rc = ibmvfc_nvme_map_sg_data(fcp_req, evt, vfc_cmd))))
+		return ibmvfc_send_event(evt, vhost, 0);
+
+	ibmvfc_free_event(evt);
+
+	return rc;
+}
+
+static void ibmvfc_init_fcp_abort(struct ibmvfc_event *evt,
+				  struct nvmefc_fcp_req *abort_req)
+{
+	struct ibmvfc_tmf *tmf;
+	struct ibmvfc_event *abt_evt = abort_req->private;
+	struct ibmvfc_target *tgt = abt_evt->tgt;
+
+	tmf = &evt->iu.tmf;
+	memset(tmf, 0, sizeof(*tmf));
+	tmf->common.version = cpu_to_be32(2);
+	tmf->common.opcode = cpu_to_be32(IBMVFC_NVMF_TMF_MAD);
+	tmf->common.length = cpu_to_be16(sizeof(*tmf));
+	tmf->flags = cpu_to_be32(IBMVFC_TMF_ABORT_TASK | IBMVFC_TMF_NVMF_ASSOC);
+	tmf->cancel_key = cpu_to_be32((u64)abt_evt);
+	tmf->my_cancel_key = cpu_to_be32((u64)evt);
+	tmf->target_wwpn = cpu_to_be64(tgt->wwpn);
+	tmf->assoc_id = cpu_to_be64(tgt->assoc_id);
+	tmf->task_tag = cpu_to_be64((u64)abt_evt);
+
+	init_completion(&evt->comp);
+}
+
+static void ibmvfc_nvme_fcp_abort(struct nvme_fc_local_port *lport,
+				  struct nvme_fc_remote_port *rport,
+				  void *hw_queue_handle,
+				  struct nvmefc_fcp_req *abort_req)
+{
+	struct ibmvfc_host *vhost = lport->private;
+	struct ibmvfc_target *tgt = rport->private;
+	struct ibmvfc_event *evt, *abt_evt = abort_req->private;
+	struct ibmvfc_queue *queue;
+	union ibmvfc_iu rsp;
+	unsigned long flags;
+	u16 status = 0;
+
+	if (!abt_evt)
+		return;
+
+	queue = abt_evt->queue;
+	if (!vhost->logged_in || !queue)
+		return;
+
+	evt = ibmvfc_get_event(queue);
+	if (!evt)
+		return;
+
+	spin_lock_irqsave(queue->q_lock, flags);
+	kref_get(&tgt->kref);
+	ibmvfc_init_event(evt, ibmvfc_sync_nvme_completion, IBMVFC_MAD_FORMAT);
+	ibmvfc_init_fcp_abort(evt, abort_req);
+	evt->sync_iu = &rsp;
+
+	if (ibmvfc_send_event(evt, vhost, default_timeout))
+		goto out;
+
+	spin_unlock_irqrestore(queue->q_lock, flags);
+
+	wait_for_completion(&evt->comp);
+	status = be16_to_cpu(rsp.mad_common.status);
+
+	spin_lock_irqsave(queue->q_lock, flags);
+	ibmvfc_free_event(evt);
+out:
+	spin_unlock_irqrestore(queue->q_lock, flags);
+
+	if (status)
+		ibmvfc_dbg(vhost, "fcp_abort: cancel failed with rc=%x\n", status);
+
+	kref_put(&tgt->kref, ibmvfc_release_tgt);
+}
+
+static struct nvme_fc_port_template ibmvfc_nvme_fc_transport = {
+	.localport_delete	= ibmvfc_nvme_localport_delete,
+	.remoteport_delete	= ibmvfc_nvme_remoteport_delete,
+	.create_queue		= ibmvfc_nvme_create_queue,
+	.delete_queue		= ibmvfc_nvme_delete_queue,
+	.ls_req			= ibmvfc_nvme_ls_req,
+	.ls_abort		= ibmvfc_nvme_ls_abort,
+	.fcp_io			= ibmvfc_nvme_fcp_io,
+	.fcp_abort		= ibmvfc_nvme_fcp_abort,
+	.map_queues		= NULL,
+	.max_hw_queues		= IBMVFC_NVME_HW_QUEUES,
+	.max_sgl_segments	= 1024,
+	.max_dif_sgl_segments	= 64,
+	.dma_boundary		= 0xFFFFFFFF,
+	.local_priv_sz		= sizeof(struct ibmvfc_host *),
+	.remote_priv_sz		= sizeof(struct ibmvfc_target *),
+	.lsrqst_priv_sz		= sizeof(struct ibmvfc_event *),
+	.fcprqst_priv_sz	= sizeof(struct ibmvfc_event *),
+};
+
+int ibmvfc_nvme_register_remoteport(struct ibmvfc_target *tgt)
+{
+	struct ibmvfc_host *vhost = tgt->vhost;
+	struct nvme_fc_port_info pinfo;
+	int rc;
+
+	if (!IS_ENABLED(CONFIG_NVME_FC))
+		return 0;
+
+	if (!vhost->nvme_local_port) {
+		dev_err(vhost->dev, "Attempt to register NVMe fc remoteport without valid localport\n");
+		return -EINVAL;
+	}
+
+	memset(&pinfo, 0, sizeof(struct nvme_fc_port_info));
+	pinfo.node_name = tgt->ids.node_name;
+	pinfo.port_name = tgt->ids.port_name;
+	pinfo.port_id = tgt->ids.port_id;
+	pinfo.port_role = FC_PORT_ROLE_NVME_TARGET;
+
+	rc = nvme_fc_register_remoteport(vhost->nvme_local_port, &pinfo,
+					 &tgt->nvme_remote_port);
+
+	if (!rc) {
+		ibmvfc_log(vhost, 2, "register_remoteport: traddr=nn-0x%llx:pn-0x%llx PortID:%x\n",
+			   pinfo.node_name, pinfo.port_name, pinfo.port_id);
+		tgt->nvme_remote_port->private = tgt;
+	}
+
+	return rc;
+}
+
+void ibmvfc_nvme_unregister_remoteport(struct ibmvfc_target *tgt)
+{
+	struct ibmvfc_host *vhost = tgt->vhost;
+	struct nvme_fc_remote_port *rport = tgt->nvme_remote_port;
+	int rc;
+
+	if (!IS_ENABLED(CONFIG_NVME_FC))
+		return;
+
+	if (!tgt->nvme_remote_port)
+		return;
+
+	ibmvfc_log(vhost, 2, "unregister_remoteport: traddr=nn-0x%llx:pn-0x%llx PortID:%x\n",
+		   rport->node_name, rport->port_name, rport->port_id);
+	init_completion(&tgt->nvme_delete_done);
+	rc = nvme_fc_unregister_remoteport(tgt->nvme_remote_port);
+
+	if (!rc) {
+		wait_for_completion(&tgt->nvme_delete_done);
+	}
+}
+
+int ibmvfc_nvme_register(struct ibmvfc_host *vhost)
+{
+	struct nvme_fc_port_info pinfo;
+	int rc;
+
+	if (!IS_ENABLED(CONFIG_NVME_FC))
+		return 0;
+
+	pinfo.node_name = fc_host_node_name(vhost->host);
+	pinfo.port_name = fc_host_port_name(vhost->host);
+	pinfo.port_id = fc_host_port_id(vhost->host);
+	pinfo.port_role = FC_PORT_ROLE_NVME_INITIATOR;
+	pinfo.dev_loss_tmo = 0;
+
+	rc = nvme_fc_register_localport(&pinfo, &ibmvfc_nvme_fc_transport,
+					get_device(vhost->dev),
+					&vhost->nvme_local_port);
+
+	if (!rc) {
+		ibmvfc_log(vhost, 2, "register_localport: host-traddr=nn-0x%llx:pn-0x%llx on portID:%x\n",
+			   pinfo.node_name, pinfo.port_name, pinfo.port_id);
+		vhost->nvme_local_port->private = vhost;
+	} else {
+		dev_err(vhost->dev, "Failed to register NVMe fc localport (%d)\n", rc);
+		put_device(vhost->dev);
+	}
+
+	return rc;
+}
+
+void ibmvfc_nvme_unregister(struct ibmvfc_host *vhost)
+{
+	int rc;
+
+	if (!IS_ENABLED(CONFIG_NVME_FC))
+		return;
+
+	if (vhost->nvme_local_port) {
+		ibmvfc_log(vhost, 2, "unregister_localport: host-traddr=nn-0x%llx:pn-0x%llx on portID:%x\n",
+			   vhost->nvme_local_port->node_name,
+			   vhost->nvme_local_port->port_name,
+			   vhost->nvme_local_port->port_id);
+		init_completion(&vhost->nvme_delete_done);
+		rc = nvme_fc_unregister_localport(vhost->nvme_local_port);
+		if (!rc) {
+			wait_for_completion(&vhost->nvme_delete_done);
+		} else
+			dev_err(vhost->dev, "Failed to unregister NVMe fc localport (%d)\n", rc);
+
+		put_device(vhost->dev);
+	}
+}
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
new file mode 100644
index 000000000000..e245b6ed0875
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * ibmvfc-nvme.h -- IBM Power Virtual Fibre Channel NVMeoF HBA driver
+ *
+ * Written By: Tyrel Datwyler <tyreld@linux.ibm.com>, IBM Corporation
+ *
+ * Copyright (C) IBM Corporation, 2026
+ */
+
+#ifndef _IBMVFC_NVME_H
+#define _IBMVFC_NVME_H
+
+#include <scsi/fc/fc_fs.h>
+#include <scsi/fc/fc_els.h>
+#include <linux/nvme-fc-driver.h>
+#include <linux/nvme.h>
+#include <linux/nvme-fc.h>
+
+#include "ibmvfc.h"
+
+#define IBMVFC_NVME		0
+#define IBMVFC_NVME_HW_QUEUES	8
+#define IBMVFC_MAX_NVME_QUEUES	16
+#define IBMVFC_NVME_CHANNELS	8
+
+#define IBMVFC_FC4_LS_TIMEOUT	15
+#define IBMVFC_FC4_LS_CANCEL_TIMEOUT	45
+#define IBMVFC_FC4_LS_PLUS_CANCEL_TIMEOUT	\
+	(IBMVFC_FC4_LS_TIMEOUT + IBMVFC_FC4_LS_CANCEL_TIMEOUT)
+
+extern unsigned int ibmvfc_debug;
+
+struct ibmvfc_host;
+struct ibmvfc_target;
+struct ibmvfc_queue;
+
+struct ibmvfc_nvme_qhandle {
+	unsigned int qidx;
+	u16 cpu_id;
+	unsigned long index;
+	struct ibmvfc_queue *queue;
+};
+
+int ibmvfc_nvme_register_remoteport(struct ibmvfc_target *tgt);
+void ibmvfc_nvme_unregister_remoteport(struct ibmvfc_target *tgt);
+int ibmvfc_nvme_register(struct ibmvfc_host *vhost);
+void ibmvfc_nvme_unregister(struct ibmvfc_host *vhost);
+#endif
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index c73ed2314ad0..ca80ceffe53a 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -1,20 +1,27 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
+
 /*
  * ibmvfc.h -- driver for IBM Power Virtual Fibre Channel Adapter
  *
  * Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
  *
- * Copyright (C) IBM Corporation, 2008
+ * Copyright (C) IBM Corporation, 2008-2026
  */
 
 #ifndef _IBMVFC_H
 #define _IBMVFC_H
 
+#include <linux/interrupt.h>
 #include <linux/list.h>
 #include <linux/types.h>
+#include <scsi/scsi_device.h>
 #include <scsi/viosrp.h>
+#include <linux/nvme.h>
+#include <linux/nvme-fc.h>
+
+#include "ibmvfc-nvme.h"
 
-#define IBMVFC_NAME	"ibmvfc"
+#define IBMVFC_NAME			"ibmvfc"
 #define IBMVFC_DRIVER_VERSION		"1.0.11"
 #define IBMVFC_DRIVER_DATE		"(April 12, 2013)"
 
@@ -92,6 +99,7 @@ enum ibmvfc_cmd_status_flags {
 	IBMVFC_FC_SCSI_ERROR		= 0x0008,
 	IBMVFC_HW_EVENT_LOGGED		= 0x0010,
 	IBMVFC_VIOS_LOGGED		= 0x0020,
+	IBMVFC_FC_NVME_STATUS		= 0x0040,
 };
 
 enum ibmvfc_fabric_mapped_errors {
@@ -124,20 +132,37 @@ enum ibmvfc_vios_errors {
 	IBMVFC_COMMAND_FAILED			= 0x8000,
 };
 
+enum ibmvfc_fc_nvme_errors {
+	IBMVFC_NVMS_VALID_ERSP		= 0x0001,
+	IBMVFC_NVMS_VALID_NODMA_CQE	= 0x0002,
+};
+
 enum ibmvfc_mad_types {
 	IBMVFC_NPIV_LOGIN		= 0x0001,
-	IBMVFC_DISC_TARGETS	= 0x0002,
+	IBMVFC_DISC_TARGETS		= 0x0002,
+	IBMVFC_DISC_NVMF_TARGETS	= 0x0003,
 	IBMVFC_PORT_LOGIN		= 0x0004,
-	IBMVFC_PROCESS_LOGIN	= 0x0008,
-	IBMVFC_QUERY_TARGET	= 0x0010,
+	IBMVFC_NVMF_PORT_LOGIN		= 0x0005,
+	IBMVFC_PROCESS_LOGIN		= 0x0008,
+	IBMVFC_NVMF_PROCESS_LOGIN	= 0x0009,
+	IBMVFC_QUERY_TARGET		= 0x0010,
+	IBMVFC_NVMF_QUERY_TARGET	= 0x0011,
 	IBMVFC_MOVE_LOGIN		= 0x0020,
-	IBMVFC_IMPLICIT_LOGOUT	= 0x0040,
-	IBMVFC_PASSTHRU		= 0x0200,
-	IBMVFC_TMF_MAD		= 0x0100,
-	IBMVFC_NPIV_LOGOUT	= 0x0800,
-	IBMVFC_CHANNEL_ENQUIRY	= 0x1000,
-	IBMVFC_CHANNEL_SETUP	= 0x2000,
-	IBMVFC_CONNECTION_INFO	= 0x4000,
+	IBMVFC_NVMF_MOVE_LOGIN		= 0x0021,
+	IBMVFC_IMPLICIT_LOGOUT		= 0x0040,
+	IBMVFC_NVMF_IMPLICIT_LOGOUT	= 0x0041,
+	IBMVFC_RNID			= 0x0080,
+	IBMVFC_NVMF_RNID		= 0x0081,
+	IBMVFC_TMF_MAD			= 0x0100,
+	IBMVFC_NVMF_TMF_MAD		= 0x0101,
+	IBMVFC_PASSTHRU			= 0x0200,
+	IBMVFC_NVMF_PASSTHRU		= 0x0201,
+	IBMVFC_NPIV_LOGOUT		= 0x0800,
+	IBMVFC_CHANNEL_ENQUIRY		= 0x1000,
+	IBMVFC_CHANNEL_SETUP		= 0x2000,
+	IBMVFC_CONNECTION_INFO		= 0x4000,
+	IBMVFC_FABRIC_LOGIN		= 0x8000,
+	IBMVFC_NVMF_FABRIC_LOGIN	= 0x8001,
 };
 
 struct ibmvfc_mad_common {
@@ -175,11 +200,16 @@ struct ibmvfc_npiv_login {
 #define IBMVFC_FLUSH_ON_HALT		0x02
 	__be32 max_cmds;
 	__be64 capabilities;
-#define IBMVFC_CAN_MIGRATE		0x01
-#define IBMVFC_CAN_USE_CHANNELS		0x02
-#define IBMVFC_CAN_HANDLE_FPIN		0x04
-#define IBMVFC_CAN_USE_MAD_VERSION	0x08
-#define IBMVFC_CAN_SEND_VF_WWPN		0x10
+#define IBMVFC_CAN_MIGRATE		0x001
+#define IBMVFC_CAN_USE_CHANNELS		0x002
+#define IBMVFC_CAN_HANDLE_FPIN		0x004
+#define IBMVFC_CAN_USE_MAD_VERSION	0x008
+#define IBMVFC_CAN_SEND_VF_WWPN		0x010
+#define IBMVFC_YES_NVMEOF		0x020
+#define IBMVFC_YES_SCSI			0x040
+#define IBMVFC_CAN_USE_WWPN_ALL		0x080
+#define IBMVFC_USE_ASYNC_SUBQ		0x100
+#define IBMVFC_CAN_USE_NOOP_CMD		0x200
 	__be64 node_name;
 	struct srp_direct_buf async;
 	u8 partition_name[IBMVFC_MAX_NAME];
@@ -219,13 +249,18 @@ struct ibmvfc_npiv_login_resp {
 	__be16 error;
 	__be32 flags;
 #define IBMVFC_NATIVE_FC		0x01
-	__be32 reserved;
+	__be32 possible_nports;
 	__be64 capabilities;
-#define IBMVFC_CAN_FLUSH_ON_HALT	0x08
-#define IBMVFC_CAN_SUPPRESS_ABTS	0x10
-#define IBMVFC_MAD_VERSION_CAP		0x20
-#define IBMVFC_HANDLE_VF_WWPN		0x40
-#define IBMVFC_CAN_SUPPORT_CHANNELS	0x80
+#define IBMVFC_CAN_FLUSH_ON_HALT	0x0008
+#define IBMVFC_CAN_SUPPRESS_ABTS	0x0010
+#define IBMVFC_MAD_VERSION_CAP		0x0020
+#define IBMVFC_HANDLE_VF_WWPN		0x0040
+#define IBMVFC_CAN_SUPPORT_CHANNELS	0x0080
+#define IBMVFC_SUPPORT_NVMEOF		0x0100
+#define IBMVFC_SUPPORT_SCSI		0x0200
+#define IBMVFC_SUPPORT_WWPN_ALL		0x0400
+#define IBMVFC_ASYNC_SUBQ		0x0800
+#define IBMVFC_SUPPORT_NOOP_CMD		0x1000
 	__be32 max_cmds;
 	__be32 scsi_id_sz;
 	__be64 max_dma_len;
@@ -238,7 +273,7 @@ struct ibmvfc_npiv_login_resp {
 	u8 port_loc_code[IBMVFC_MAX_NAME];
 	u8 drc_name[IBMVFC_MAX_NAME];
 	struct ibmvfc_service_parms service_parms;
-	__be64 reserved2;
+	__be64 reserved;
 } __packed __aligned(8);
 
 union ibmvfc_npiv_login_data {
@@ -246,6 +281,17 @@ union ibmvfc_npiv_login_data {
 	struct ibmvfc_npiv_login_resp resp;
 } __packed __aligned(8);
 
+struct ibmvfc_fabric_login_mad {
+	struct ibmvfc_mad_common common;
+	__be64 flags;
+	__be64 capabilities;
+	__be64 nport_id;
+	__be16 status;
+	__be16 error;
+	__be32 reserved;
+	__be64 reserved2[16];
+} __packed __aligned(8);
+
 struct ibmvfc_discover_targets_entry {
 	__be32 scsi_id;
 	__be32 pad;
@@ -287,6 +333,7 @@ enum ibmvfc_fc_type {
 	IBMVFC_FABRIC_BUSY	= 0x04,
 	IBMVFC_PORT_BUSY		= 0x05,
 	IBMVFC_BASIC_REJECT	= 0x06,
+	IBMVFC_FC4_LS_REJECT	= 0x07,
 };
 
 enum ibmvfc_gs_explain {
@@ -334,6 +381,7 @@ struct ibmvfc_move_login {
 struct ibmvfc_prli_svc_parms {
 	u8 type;
 #define IBMVFC_SCSI_FCP_TYPE		0x08
+#define IBMVFC_NVME_FCP_TYPE		0x28
 	u8 type_ext;
 	__be16 flags;
 #define IBMVFC_PRLI_ORIG_PA_VALID			0x8000
@@ -349,6 +397,11 @@ struct ibmvfc_prli_svc_parms {
 #define IBMVFC_PRLI_TARGET_FUNC			0x00000010
 #define IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED	0x00000002
 #define IBMVFC_PRLI_WR_FCP_XFER_RDY_DISABLED	0x00000001
+#define IBMVFC_PRLI_NVME_PI_CTRL		0x00000200
+#define IBMVFC_PRLI_NVME_SLER			0x00000100
+#define IBMVFC_PRLI_NVME_INITIATOR		0x00000020
+#define IBMVFC_PRLI_NVME_TARGET			0x00000010
+#define IBMVFC_PRLI_NVME_DISCOVERY		0x00000008
 } __packed __aligned(4);
 
 struct ibmvfc_process_login {
@@ -377,20 +430,27 @@ struct ibmvfc_query_tgt {
 struct ibmvfc_implicit_logout {
 	struct ibmvfc_mad_common common;
 	__be64 old_scsi_id;
-	__be64 reserved[2];
+	__be64 reserved[8];
+	__be64 target_wwpn;
 } __packed __aligned(8);
 
 struct ibmvfc_tmf {
 	struct ibmvfc_mad_common common;
 	__be64 scsi_id;
-	struct scsi_lun lun;
+	union {
+		struct scsi_lun lun;
+		__be64 assoc_id;
+	};
 	__be32 flags;
-#define IBMVFC_TMF_ABORT_TASK		0x02
-#define IBMVFC_TMF_ABORT_TASK_SET	0x04
-#define IBMVFC_TMF_LUN_RESET		0x10
-#define IBMVFC_TMF_TGT_RESET		0x20
-#define IBMVFC_TMF_LUA_VALID		0x40
-#define IBMVFC_TMF_SUPPRESS_ABTS	0x80
+#define IBMVFC_TMF_ABORT_TASK		0x002
+#define IBMVFC_TMF_ABORT_TASK_SET	0x004
+#define IBMVFC_TMF_LUN_RESET		0x010
+#define IBMVFC_TMF_TGT_RESET		0x020
+#define IBMVFC_TMF_LUA_VALID		0x040
+#define IBMVFC_TMF_SUPPRESS_ABTS	0x080
+#define IBMVFC_TMF_NVMF_ASSOC		0x100
+#define IBMVFC_TMF_NVMF_TARGET		0x200
+#define IBMVFC_TMF_BITMASK_VALID	0x400
 	__be32 cancel_key;
 	__be32 my_cancel_key;
 	__be32 pad;
@@ -446,6 +506,8 @@ enum ibmvfc_cmd_flags {
 	IBMVFC_WRITE		= 0x0008,
 	IBMVFC_TMF			= 0x0080,
 	IBMVFC_CLASS_3_ERR	= 0x0100,
+	IBMVFC_NVMEOF_PROTOCOL	= 0x0200,
+	IBMVFC_NVMF_SLER	= 0x0400,
 };
 
 enum ibmvfc_fc_task_attr {
@@ -493,7 +555,7 @@ struct ibmvfc_cmd {
 	__be64 tgt_scsi_id;
 	__be64 tag;
 	__be64 target_wwpn;
-	__be64 reserved3;
+	__be64 assoc_id;
 	union {
 		struct {
 			struct ibmvfc_fcp_cmd_iu iu;
@@ -504,6 +566,15 @@ struct ibmvfc_cmd {
 			struct ibmvfc_fcp_cmd_iu iu;
 			struct ibmvfc_fcp_rsp rsp;
 		} v2;
+		struct {
+			__be64 reserved5[4];
+			struct ibmvfc_fcp_cmd_iu iu;
+			struct ibmvfc_fcp_rsp rsp;
+		} v3scsi;
+		struct {
+			__be64 reserved[4];
+			struct nvme_fc_cmd_iu iu;
+		} v3nvme;
 	};
 } __packed __aligned(8);
 
@@ -522,6 +593,9 @@ struct ibmvfc_passthru_iu {
 	__be32 flags;
 #define IBMVFC_FC_ELS		0x01
 #define IBMVFC_FC_CT_IU		0x02
+#define IBMVFC_PT_PRLI		0x04
+#define IBMVFC_FC4_LS_OTH	0x08
+#define IBMVFC_FC4_LS_DSC_CTRL	0x10
 	__be32 cancel_key;
 #define IBMVFC_PASSTHRU_CANCEL_KEY	0x80000000
 #define IBMVFC_INTERNAL_CANCEL_KEY	0x80000001
@@ -549,9 +623,9 @@ struct ibmvfc_channel_enquiry {
 #define IBMVFC_SUPPORT_VARIABLE_SUBQ_MSG	0x02
 #define IBMVFC_NO_N_TO_M_CHANNELS_SUPPORT	0x04
 	__be32 num_scsi_subq_channels;
-	__be32 num_nvmeof_subq_channels;
+	__be32 num_nvme_subq_channels;
 	__be32 num_scsi_vas_channels;
-	__be32 num_nvmeof_vas_channels;
+	__be32 num_nvme_vas_channels;
 } __packed __aligned(8);
 
 struct ibmvfc_channel_setup_mad {
@@ -559,7 +633,7 @@ struct ibmvfc_channel_setup_mad {
 	struct srp_direct_buf buffer;
 } __packed __aligned(8);
 
-#define IBMVFC_MAX_CHANNELS	502
+#define IBMVFC_MAX_CHANNELS	501
 
 struct ibmvfc_channel_setup {
 	__be32 flags;
@@ -568,12 +642,13 @@ struct ibmvfc_channel_setup {
 #define IBMVFC_CHANNELS_CANCELED	0x04
 	__be32 reserved;
 	__be32 num_scsi_subq_channels;
-	__be32 num_nvmeof_subq_channels;
+	__be32 num_nvme_subq_channels;
 	__be32 num_scsi_vas_channels;
-	__be32 num_nvmeof_vas_channels;
+	__be32 num_nvme_vas_channels;
 	struct srp_direct_buf buffer;
 	__be64 reserved2[5];
 	__be64 channel_handles[IBMVFC_MAX_CHANNELS];
+	__be64 async_sub_crq_handle;
 } __packed __aligned(8);
 
 struct ibmvfc_connection_info {
@@ -620,7 +695,8 @@ struct ibmvfc_trace_entry {
 
 enum ibmvfc_crq_formats {
 	IBMVFC_CMD_FORMAT		= 0x01,
-	IBMVFC_ASYNC_EVENT	= 0x02,
+	IBMVFC_ASYNC_EVENT		= 0x02,
+	IBMVFC_NOOP			= 0x03,
 	IBMVFC_MAD_FORMAT		= 0x04,
 };
 
@@ -639,6 +715,9 @@ enum ibmvfc_async_event {
 	IBMVFC_AE_RESUME			= 0x0800,
 	IBMVFC_AE_ADAPTER_FAILED	= 0x1000,
 	IBMVFC_AE_FPIN			= 0x2000,
+	IBMVFC_NVME_DISCONNECT		= 0x4000,
+	IBMVFC_NVMEOF_PROTO_AVAIL	= 0x40000000,
+	IBMVFC_SCSI_PROTO_AVAIL		= 0x80000000,
 };
 
 struct ibmvfc_async_desc {
@@ -683,13 +762,30 @@ struct ibmvfc_async_crq {
 	volatile __be64 scsi_id;
 	volatile __be64 wwpn;
 	volatile __be64 node_name;
-	__be64 reserved;
+	__be64 assoc_id;
+} __packed __aligned(8);
+
+struct ibmvfc_async_sub_crq {
+	volatile u8 valid;
+	u8 flags;
+#define IBMVFC_ASYNC_ID_IS_ASSOC_ID	0x01
+	u8 link_state;
+	u8 fpin_status;
+	__be16 event;
+	__be16 pad;
+	__be64 wwpn;
+	__be64 nport_id;
+	union {
+		__be64 node_name;
+		__be64 assoc_id;
+	} id;
 } __packed __aligned(8);
 
 union ibmvfc_iu {
 	struct ibmvfc_mad_common mad_common;
 	struct ibmvfc_npiv_login_mad npiv_login;
 	struct ibmvfc_npiv_logout_mad npiv_logout;
+	struct ibmvfc_fabric_login_mad fabric_login;
 	struct ibmvfc_discover_targets discover_targets;
 	struct ibmvfc_port_login plogi;
 	struct ibmvfc_process_login prli;
@@ -728,6 +824,7 @@ struct ibmvfc_target {
 	u64 scsi_id;
 	u64 wwpn;
 	u64 new_scsi_id;
+	u64 assoc_id;
 	struct fc_rport *rport;
 	int target_id;
 	enum ibmvfc_target_action action;
@@ -743,6 +840,8 @@ struct ibmvfc_target {
 	void (*job_step) (struct ibmvfc_target *);
 	struct timer_list timer;
 	struct kref kref;
+	struct completion nvme_delete_done;
+	struct nvme_fc_remote_port *nvme_remote_port;
 };
 
 /* a unit of work for the hosting partition */
@@ -753,6 +852,8 @@ struct ibmvfc_event {
 	struct ibmvfc_queue *queue;
 	struct ibmvfc_target *tgt;
 	struct scsi_cmnd *cmnd;
+	struct nvmefc_ls_req *ls_req;
+	struct nvmefc_fcp_req *fcp_req;
 	atomic_t free;
 	atomic_t active;
 	union ibmvfc_iu *xfer_iu;
@@ -828,6 +929,8 @@ struct ibmvfc_channels {
 	unsigned int active_queues;
 	unsigned int desired_queues;
 	unsigned int max_queues;
+	int num_targets;
+	struct list_head targets;
 	int disc_buf_sz;
 	struct ibmvfc_discover_targets_entry *disc_buf;
 	dma_addr_t disc_buf_dma;
@@ -871,8 +974,6 @@ struct ibmvfc_host {
 #define IBMVFC_TRACE_SIZE	(sizeof(struct ibmvfc_trace_entry) * IBMVFC_NUM_TRACE_ENTRIES)
 	struct ibmvfc_trace_entry *trace;
 	atomic_t trace_index;
-	int num_targets;
-	struct list_head targets;
 	struct list_head purge;
 	struct device *dev;
 	struct dma_pool *sg_pool;
@@ -880,6 +981,7 @@ struct ibmvfc_host {
 	struct ibmvfc_queue crq;
 	struct ibmvfc_queue async_crq;
 	struct ibmvfc_channels scsi_scrqs;
+	struct ibmvfc_channels nvme_scrqs;
 	struct ibmvfc_npiv_login login_info;
 	union ibmvfc_npiv_login_data *login_buf;
 	dma_addr_t login_buf_dma;
@@ -888,6 +990,7 @@ struct ibmvfc_host {
 	int log_level;
 	struct mutex passthru_mutex;
 	unsigned int max_vios_scsi_channels;
+	unsigned int max_vios_nvme_channels;
 	int task_set;
 	int init_retries;
 	int discovery_threads;
@@ -899,6 +1002,9 @@ struct ibmvfc_host {
 	unsigned int mq_enabled:1;
 	unsigned int using_channels:1;
 	unsigned int do_enquiry:1;
+	unsigned int nvme_enabled:1;
+	unsigned int do_scsi_login:1;
+	unsigned int do_nvme_login:1;
 	unsigned int aborting_passthru:1;
 	unsigned int scan_complete:1;
 	int scan_timeout;
@@ -914,8 +1020,37 @@ struct ibmvfc_host {
 	struct work_struct rport_add_work_q;
 	wait_queue_head_t init_wait_q;
 	wait_queue_head_t work_wait_q;
+	struct nvme_fc_local_port *nvme_local_port;
+	struct completion nvme_delete_done;
 };
 
+struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved);
+#define ibmvfc_get_event(queue) __ibmvfc_get_event(queue, 0)
+#define ibmvfc_get_reserved_event(queue) __ibmvfc_get_event(queue, 1)
+
+void ibmvfc_init_event(struct ibmvfc_event *evt, void (*done) (struct ibmvfc_event *), u8 format);
+void ibmvfc_free_event(struct ibmvfc_event *evt);
+void ibmvfc_release_tgt(struct kref *kref);
+int ibmvfc_send_event(struct ibmvfc_event *evt, struct ibmvfc_host *vhost, unsigned long timeout);
+const char *ibmvfc_get_cmd_error(u16 status, u16 error);
+
+static inline int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags)
+{
+	u64 host_caps = be64_to_cpu(vhost->login_buf->resp.capabilities);
+
+	return (host_caps & cap_flags) ? 1 : 0;
+}
+
+static inline struct ibmvfc_host *ibmvfc_channels_to_vhost(struct ibmvfc_channels *channels)
+{
+	if (channels->protocol == IBMVFC_PROTO_SCSI)
+		return container_of(channels, struct ibmvfc_host, scsi_scrqs);
+	else if (channels->protocol == IBMVFC_PROTO_NVME)
+		return container_of(channels, struct ibmvfc_host, nvme_scrqs);
+
+	return NULL;
+}
+
 #define DBG_CMD(CMD) do { if (ibmvfc_debug) CMD; } while (0)
 
 #define tgt_dbg(t, fmt, ...)			\
diff --git a/drivers/scsi/isci/scu_task_context.h b/drivers/scsi/isci/scu_task_context.h
index e56cd0fdc35f..f258f4014e7f 100644
--- a/drivers/scsi/isci/scu_task_context.h
+++ b/drivers/scsi/isci/scu_task_context.h
@@ -193,21 +193,7 @@ typedef enum {
 
 #define SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_SHIFT        18
 #define SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_MASK         0x001C0000
-#define scu_get_command_request_subtype(x) \
-	((x) & SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_MASK)
-
-#define SCU_CONTEXT_COMMAND_REQUEST_FULLTYPE_MASK	 \
-	(\
-		SCU_CONTEXT_COMMAND_REQUEST_TYPE_MASK		  \
-		| SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_MASK	    \
-	)
-#define scu_get_command_request_full_type(x) \
-	((x) & SCU_CONTEXT_COMMAND_REQUEST_FULLTYPE_MASK)
-
 #define SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT  16
-#define SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_MASK   0x00010000
-#define scu_get_command_protocl_engine_group(x)	\
-	((x) & SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_MASK)
 
 #define SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT           12
 #define SCU_CONTEXT_COMMAND_LOGICAL_PORT_MASK            0x00007000
diff --git a/drivers/scsi/leapraid/Kconfig b/drivers/scsi/leapraid/Kconfig
new file mode 100644
index 000000000000..f4ece78f80d3
--- /dev/null
+++ b/drivers/scsi/leapraid/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+config SCSI_LEAPRAID
+	tristate "LeapIO RAID Adapter"
+	depends on PCI && SCSI
+	select SCSI_SAS_ATTRS
+	help
+	  This driver supports LeapIO PCIe-based storage
+	  and RAID controllers.
+
+	  <http://www.leap-io.com>
+
+	  To compile this driver as a module, choose M here: the
+	  resulting kernel module will be named leapraid.
diff --git a/drivers/scsi/leapraid/Makefile b/drivers/scsi/leapraid/Makefile
new file mode 100644
index 000000000000..bdafc036cd00
--- /dev/null
+++ b/drivers/scsi/leapraid/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the LEAPRAID drivers.
+#
+
+obj-$(CONFIG_SCSI_LEAPRAID) += leapraid.o
+leapraid-objs += leapraid_func.o \
+		leapraid_os.o \
+		leapraid_transport.o \
+		leapraid_app.o
diff --git a/drivers/scsi/leapraid/leapraid.h b/drivers/scsi/leapraid/leapraid.h
new file mode 100644
index 000000000000..c2d28359be5b
--- /dev/null
+++ b/drivers/scsi/leapraid/leapraid.h
@@ -0,0 +1,2048 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 LeapIO Tech Inc.
+ *
+ * LeapRAID storage and RAID controller driver.
+ */
+#ifndef LEAPRAID_H
+#define LEAPRAID_H
+
+/* Doorbell register definitions. */
+#define LEAPRAID_DB_RESET       0x00000000
+#define LEAPRAID_DB_READY       0x10000000
+#define LEAPRAID_DB_OPERATIONAL 0x20000000
+#define LEAPRAID_DB_FAULT       0x40000000
+
+#define LEAPRAID_DB_MASK        0xF0000000
+
+#define LEAPRAID_DB_OVER_TEMPERATURE    0x2810
+
+#define LEAPRAID_DB_USED                0x08000000
+#define LEAPRAID_DB_DATA_MASK           0x0000FFFF
+#define LEAPRAID_DB_FUNC_SHIFT          24
+#define LEAPRAID_DB_ADD_DWORDS_SHIFT    16
+
+/* Maximum number of retries waiting for doorbell to become ready. */
+#define LEAPRAID_DB_RETRY_COUNT_MAX    10
+/* Maximum number of retries waiting for doorbell to become operational. */
+#define LEAPRAID_DB_WAIT_OP_SHORT   10
+#define LEAPRAID_DB_WAIT_OP_LONG   200
+
+/* Maximum number of retries waiting for host to end recovery. */
+#define LEAPRAID_WAIT_SHOST_RECOVERY   400
+
+/* Diagnostic register definitions. */
+#define LEAPRAID_DIAG_WRITE_ENABLE      0x00000080
+#define LEAPRAID_DIAG_RESET             0x00000004
+
+/* Interrupt status register definitions. */
+#define LEAPRAID_HOST2ADAPTER_DB_STATUS 0x80000000
+#define LEAPRAID_ADAPTER2HOST_DB_STATUS 0x00000001
+
+/* The number of debug register. */
+#define LEAPRAID_DEBUGLOG_SZ_MAX    16
+
+/* Reply post host register definitions. */
+#define REP_POST_HOST_IDX_REG_CNT 16
+#define LEAPRAID_RPHI_MSIX_IDX_SHIFT    24
+
+/* Virtual PHY flags. */
+#define LEAPRAID_SAS_PHYINFO_VPHY       0x00001000
+
+/* Linux driver init firmware. */
+#define LEAPRAID_WHOINIT_LINUX_DRIVER    0x04
+
+/* Reply descriptor post queue array mode. */
+#define LEAPRAID_ADAPTER_INIT_MSGFLG_RDPQ_ARRAY_MODE    0x01
+
+/* Request description flags. */
+#define LEAPRAID_REQ_DESC_FLG_SCSI_IO   0x00
+#define LEAPRAID_REQ_DESC_FLG_HPR       0x06
+#define LEAPRAID_REQ_DESC_FLG_DFLT_TYPE 0x08
+
+/* Reply description flags. */
+#define LEAPRAID_RPY_DESC_FLG_TYPE_MASK                 0x0F
+#define LEAPRAID_RPY_DESC_FLG_SCSI_IO_SUCCESS           0x00
+#define LEAPRAID_RPY_DESC_FLG_ADDRESS_REPLY             0x01
+#define LEAPRAID_RPY_DESC_FLG_FP_SCSI_IO_SUCCESS        0x06
+#define LEAPRAID_RPY_DESC_FLG_UNUSED                    0x0F
+
+/* Request and reply messages share the same set of function codes.
+ *
+ * Note: SCSIIO (SCSI I/O) represents SCSI I/O operations and is used
+ * consistently throughout the driver for all SCSI command handling.
+ */
+#define LEAPRAID_FUNC_SCSIIO                    0x00
+#define LEAPRAID_FUNC_SCSI_TMF                  0x01
+#define LEAPRAID_FUNC_ADAPTER_INIT              0x02
+#define LEAPRAID_FUNC_GET_ADAPTER_FEATURES      0x03
+#define LEAPRAID_FUNC_CONFIG_OP                 0x04
+#define LEAPRAID_FUNC_SCAN_DEV                  0x06
+#define LEAPRAID_FUNC_EVENT_NOTIFY              0x07
+#define LEAPRAID_FUNC_FW_DOWNLOAD               0x09
+#define LEAPRAID_FUNC_FW_UPLOAD                 0x12
+#define LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH   0x16
+#define LEAPRAID_FUNC_SCSI_ENC_PROCESSOR        0x18
+#define LEAPRAID_FUNC_SMP_PASSTHROUGH           0x1A
+#define LEAPRAID_FUNC_SAS_IO_UNIT_CTRL          0x1B
+#define LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH   0x1C
+#define LEAPRAID_FUNC_ADAPTER_UNIT_RESET        0x40
+#define LEAPRAID_FUNC_HANDSHAKE                 0x42
+#define LEAPRAID_FUNC_LOGBUF_INIT               0x57
+
+/* Adapter status values. */
+#define LEAPRAID_ADAPTER_STATUS_MASK                    0x7FFF
+#define LEAPRAID_ADAPTER_STATUS_SUCCESS                 0x0000
+#define LEAPRAID_ADAPTER_STATUS_BUSY                    0x0002
+#define LEAPRAID_ADAPTER_STATUS_INTERNAL_ERROR          0x0004
+#define LEAPRAID_ADAPTER_STATUS_INSUFFICIENT_RESOURCES  0x0006
+#define LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_ACTION   0x0020
+#define LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_TYPE     0x0021
+#define LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_PAGE     0x0022
+#define LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_DATA     0x0023
+#define LEAPRAID_ADAPTER_STATUS_CONFIG_NO_DEFAULTS      0x0024
+#define LEAPRAID_ADAPTER_STATUS_CONFIG_CANT_COMMIT      0x0025
+#define LEAPRAID_ADAPTER_STATUS_SCSI_RECOVERED_ERROR    0x0040
+#define LEAPRAID_ADAPTER_STATUS_SCSI_DEVICE_NOT_THERE   0x0043
+#define LEAPRAID_ADAPTER_STATUS_SCSI_DATA_OVERRUN       0x0044
+#define LEAPRAID_ADAPTER_STATUS_SCSI_DATA_UNDERRUN      0x0045
+#define LEAPRAID_ADAPTER_STATUS_SCSI_IO_DATA_ERROR      0x0046
+#define LEAPRAID_ADAPTER_STATUS_SCSI_PROTOCOL_ERROR     0x0047
+#define LEAPRAID_ADAPTER_STATUS_SCSI_TASK_TERMINATED    0x0048
+#define LEAPRAID_ADAPTER_STATUS_SCSI_RESIDUAL_MISMATCH  0x0049
+#define LEAPRAID_ADAPTER_STATUS_SCSI_TASK_MGMT_FAILED   0x004A
+#define LEAPRAID_ADAPTER_STATUS_SCSI_ADAPTER_TERMINATED 0x004B
+#define LEAPRAID_ADAPTER_STATUS_SCSI_EXT_TERMINATED     0x004C
+
+/* SGE flags. */
+#define LEAPRAID_SGE_FLG_LAST_ONE       0x80
+#define LEAPRAID_SGE_FLG_EOB            0x40
+#define LEAPRAID_SGE_FLG_EOL            0x01
+#define LEAPRAID_SGE_FLG_SHIFT          24
+#define LEAPRAID_SGE_FLG_SIMPLE_ONE     0x10
+#define LEAPRAID_SGE_FLG_SYSTEM_ADDR    0x00
+#define LEAPRAID_SGE_FLG_H2C            0x04
+#define LEAPRAID_SGE_FLG_32             0x00
+#define LEAPRAID_SGE_FLG_64             0x02
+
+#define LEAPRAID_IEEE_SGE_FLG_EOL               0x40
+#define LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE        0x00
+#define LEAPRAID_IEEE_SGE_FLG_CHAIN_ONE         0x80
+#define LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR       0x00
+
+#define LEAPRAID_SGE_NO_DATA_ADDR -1
+#define LEAPRAID_SGE_OFFSET_SIZE  4
+
+/* The type of page. */
+#define LEAPRAID_CFG_PT_IO_UNIT         0x00
+#define LEAPRAID_CFG_PT_ADAPTER         0x01
+#define LEAPRAID_CFG_PT_BIOS            0x02
+#define LEAPRAID_CFG_PT_RAID_VOLUME     0x08
+#define LEAPRAID_CFG_PT_MANUFACTURING   0x09
+#define LEAPRAID_CFG_PT_RAID_PHYSDISK   0x0A
+#define LEAPRAID_CFG_PT_EXTENDED        0x0F
+
+/* The type of extended page. */
+#define LEAPRAID_CFG_EXTPT_SAS_IO_UNIT  0x10
+#define LEAPRAID_CFG_EXTPT_SAS_EXP      0x11
+#define LEAPRAID_CFG_EXTPT_SAS_DEV      0x12
+#define LEAPRAID_CFG_EXTPT_SAS_PHY      0x13
+#define LEAPRAID_CFG_EXTPT_ENC          0x15
+#define LEAPRAID_CFG_EXTPT_RAID_CONFIG  0x16
+
+/* Config page address. */
+#define LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP     0x00000000
+#define LEAPRAID_SAS_ENC_CFG_PGAD_HDL           0x10000000
+#define LEAPRAID_SAS_DEV_CFG_PGAD_HDL           0x20000000
+#define LEAPRAID_SAS_EXP_CFG_PGAD_HDL_PHY_NUM   0x10000000
+#define LEAPRAID_SAS_EXP_CFD_PGAD_HDL           0x20000000
+#define LEAPRAID_SAS_EXP_CFG_PGAD_PHYNUM_SHIFT  16
+#define LEAPRAID_RAID_VOL_CFG_PGAD_HDL          0x10000000
+#define LEAPRAID_SAS_PHY_CFG_PGAD_PHY_NUMBER    0x00000000
+#define LEAPRAID_PHYSDISK_CFG_PGAD_PHYSDISKNUM  0x10000000
+
+/* Config page operations. */
+#define LEAPRAID_CFG_ACT_PAGE_HEADER    0x00
+#define LEAPRAID_CFG_ACT_PAGE_READ_CUR  0x01
+#define LEAPRAID_CFG_ACT_PAGE_WRITE_CUR 0x02
+
+/* BIOS page number. */
+#define LEAPRAID_CFG_PAGE_NUM_BIOS2     0x2
+#define LEAPRAID_CFG_PAGE_NUM_BIOS3     0x3
+
+/* Manufacturing page number. */
+#define LEAPRAID_CFG_PAGE_NUM_MANU0     0x0
+
+/* SAS device page number. */
+#define LEAPRAID_CFG_PAGE_NUM_DEV0      0x0
+
+/* SAS device page 0 flags. */
+#define LEAPRAID_SAS_DEV_P0_FLG_ENC_LEVEL_VALID 0x0002
+#define LEAPRAID_SAS_DEV_P0_FLG_DEV_PRESENT     0x0001
+#define LEAPRAID_SAS_DEV_P0_CON_NAME_LEN        4
+
+/* SAS I/O unit page number. */
+#define LEAPRAID_CFG_PAGE_NUM_IOUNIT0   0x0
+#define LEAPRAID_CFG_PAGE_NUM_IOUNIT1   0x1
+
+/* SAS expander page number. */
+#define LEAPRAID_CFG_PAGE_NUM_EXP0      0x0
+#define LEAPRAID_CFG_PAGE_NUM_EXP1      0x1
+
+/* SAS enclosure page number. */
+#define LEAPRAID_CFG_PAGE_NUM_ENC0      0x0
+
+/* SAS PHY page number. */
+#define LEAPRAID_CFG_PAGE_NUM_PHY0      0x0
+
+/* RAID volume page number. */
+#define LEAPRAID_CFG_PAGE_NUM_VOL0      0x0
+#define LEAPRAID_CFG_PAGE_NUM_VOL1      0x1
+
+/* Physical disk page number. */
+#define LEAPRAID_CFG_PAGE_NUM_PD0       0x0
+
+#define LEAPRAID_CFG_UNIT_SIZE  4
+
+/* Raid volume type and state. */
+#define LEAPRAID_VOL_STATE_ONLINE       0x03
+#define LEAPRAID_VOL_STATE_DEGRADED     0x04
+#define LEAPRAID_VOL_STATE_OPTIMAL      0x05
+#define LEAPRAID_VOL_TYPE_RAID0         0x00
+#define LEAPRAID_VOL_TYPE_RAID1E        0x01
+#define LEAPRAID_VOL_TYPE_RAID1         0x02
+#define LEAPRAID_VOL_TYPE_RAID10        0x05
+#define LEAPRAID_VOL_TYPE_UNKNOWN       0xFF
+
+/* Raid volume element flags. */
+#define LEAPRAID_RAIDCFG_P0_EFLG_MASK_ELEMENT_TYPE      0x000F
+#define LEAPRAID_RAIDCFG_P0_EFLG_VOL_PHYS_DISK_ELEMENT  0x0001
+#define LEAPRAID_RAIDCFG_P0_EFLG_HOT_SPARE_ELEMENT      0x0002
+#define LEAPRAID_RAIDCFG_P0_EFLG_OCE_ELEMENT            0x0003
+
+/* SAS negotiated link rates. */
+#define LEAPRAID_SAS_NEG_LINK_RATE_MASK_PHYSICAL        0x0F
+#define LEAPRAID_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE    0x00
+#define LEAPRAID_SAS_NEG_LINK_RATE_PHY_DISABLED         0x01
+#define LEAPRAID_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED   0x02
+#define LEAPRAID_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE    0x03
+#define LEAPRAID_SAS_NEG_LINK_RATE_PORT_SELECTOR        0x04
+#define LEAPRAID_SAS_NEG_LINK_RATE_SMP_RESETTING        0x05
+#define LEAPRAID_SAS_NEG_LINK_RATE_SHIFT                4
+
+#define LEAPRAID_SAS_NEG_LINK_RATE_1_5  0x08
+#define LEAPRAID_SAS_NEG_LINK_RATE_3_0  0x09
+#define LEAPRAID_SAS_NEG_LINK_RATE_6_0  0x0A
+#define LEAPRAID_SAS_NEG_LINK_RATE_12_0 0x0B
+
+#define LEAPRAID_SAS_PRATE_MIN_RATE_MASK        0x0F
+#define LEAPRAID_SAS_HWRATE_MIN_RATE_MASK       0x0F
+
+/*
+ * Control flags for a SCSI I/O Request.
+ */
+#define LEAPRAID_SCSIIO_CTRL_CDB_32BYTE         4
+#define LEAPRAID_SCSIIO_CTRL_CDB_LEN_SHIFT      26
+#define LEAPRAID_SCSIIO_CTRL_NODATATRANSFER     0x00000000
+#define LEAPRAID_SCSIIO_CTRL_WRITE              0x01000000
+#define LEAPRAID_SCSIIO_CTRL_READ               0x02000000
+#define LEAPRAID_SCSIIO_CTRL_BIDIRECTIONAL      0x03000000
+#define LEAPRAID_SCSIIO_CTRL_SIMPLEQ            0x00000000
+#define LEAPRAID_SCSIIO_CTRL_ORDEREDQ           0x00000200
+#define LEAPRAID_SCSIIO_CTRL_CMDPRI             0x00000800
+
+/* SCSI state and status. */
+#define LEAPRAID_SCSI_STATUS_BUSY                       0x08
+#define LEAPRAID_SCSI_STATUS_RESERVATION_CONFLICT       0x18
+#define LEAPRAID_SCSI_STATUS_TASK_SET_FULL              0x28
+
+#define LEAPRAID_SCSI_STATE_RESPONSE_INFO_VALID 0x10
+#define LEAPRAID_SCSI_STATE_TERMINATED          0x08
+#define LEAPRAID_SCSI_STATE_NO_SCSI_STATUS      0x04
+#define LEAPRAID_SCSI_STATE_AUTOSENSE_FAILED    0x02
+#define LEAPRAID_SCSI_STATE_AUTOSENSE_VALID     0x01
+
+/* SCSI task management defines. */
+#define LEAPRAID_TM_TASKTYPE_ABORT_TASK         0x01
+#define LEAPRAID_TM_TASKTYPE_ABRT_TASK_SET      0x02
+#define LEAPRAID_TM_TASKTYPE_TARGET_RESET       0x03
+#define LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET 0x05
+#define LEAPRAID_TM_TASKTYPE_CLEAR_TASK_SET     0x06
+#define LEAPRAID_TM_TASKTYPE_QUERY_TASK         0x07
+#define LEAPRAID_TM_TASKTYPE_CLEAR_ACA          0x08
+#define LEAPRAID_TM_TASKTYPE_QUERY_TASK_SET     0x09
+#define LEAPRAID_TM_TASKTYPE_QUERY_ASYNC_EVENT  0x0A
+
+#define LEAPRAID_TM_MSGFLAGS_LINK_RESET         0x00
+#define LEAPRAID_TM_RSP_INVALID_FRAME           0x02
+
+/* SCSI enclosure processor request defines. */
+#define LEAPRAID_SEP_REQ_ACT_WRITE_STATUS               0x00
+#define LEAPRAID_SEP_REQ_FLG_DEVHDL_ADDRESS             0x00
+#define LEAPRAID_SEP_REQ_FLG_ENCLOSURE_SLOT_ADDRESS     0x01
+#define LEAPRAID_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT     0x00000040
+
+/* The capabilities of the adapter. */
+#define LEAPRAID_ADAPTER_FEATURES_CAP_ATOMIC_REQ                0x00080000
+#define LEAPRAID_ADAPTER_FEATURES_CAP_INTEGRATED_RAID           0x00001000
+
+/* Event code definitions for the firmware. */
+#define LEAPRAID_EVT_SAS_DEV_STATUS_CHANGE      0x000F
+#define LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST       0x001C
+#define LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE 0x001D
+#define LEAPRAID_EVT_IR_CHANGE          0x0020
+#define LEAPRAID_EVT_TURN_ON_PFA_LED    0xFFFC
+#define LEAPRAID_EVT_SCAN_DEV_DONE      0xFFFD
+#define LEAPRAID_EVT_REMOVE_DEAD_DEV    0xFFFF
+#define LEAPRAID_MAX_EVENT_NUM          128
+
+#define LEAPRAID_EVT_SAS_DEV_STAT_RC_INTERNAL_DEV_RESET         0x08
+#define LEAPRAID_EVT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET     0x0E
+
+/* RAID configuration change event. */
+#define LEAPRAID_EVT_IR_RC_VOLUME_ADD                   0x01
+#define LEAPRAID_EVT_IR_RC_VOLUME_DELETE                0x02
+#define LEAPRAID_EVT_IR_RC_PD_HIDDEN_TO_ADD             0x03
+#define LEAPRAID_EVT_IR_RC_PD_UNHIDDEN_TO_DELETE        0x04
+#define LEAPRAID_EVT_IR_RC_PD_CREATED_TO_HIDE           0x05
+#define LEAPRAID_EVT_IR_RC_PD_DELETED_TO_EXPOSE         0x06
+#define LEAPRAID_EVT_IR_RC_VOLUME_HIDE                  0x0A
+#define LEAPRAID_EVT_IR_RC_VOLUME_UNHIDE                0x0B
+
+/* SAS topology change event. */
+#define LEAPRAID_EVT_SAS_TOPO_ES_NO_EXPANDER    0x00
+#define LEAPRAID_EVT_SAS_TOPO_ES_ADDED          0x01
+#define LEAPRAID_EVT_SAS_TOPO_ES_NOT_RESPONDING 0x02
+#define LEAPRAID_EVT_SAS_TOPO_ES_RESPONDING     0x03
+
+#define LEAPRAID_EVT_SAS_TOPO_RC_MASK                   0x0F
+#define LEAPRAID_EVT_SAS_TOPO_RC_TARG_ADDED             0x01
+#define LEAPRAID_EVT_SAS_TOPO_RC_TARG_NOT_RESPONDING    0x02
+
+/* Enclosure device status change event. */
+#define LEAPRAID_EVT_SAS_ENCL_RC_ADDED          0x01
+#define LEAPRAID_EVT_SAS_ENCL_RC_NOT_RESPONDING 0x02
+
+/* Device type and identifiers. */
+#define LEAPRAID_DEVTYP_SEP             0x00004000
+#define LEAPRAID_DEVTYP_SSP_TGT         0x00000400
+#define LEAPRAID_DEVTYP_STP_TGT         0x00000200
+#define LEAPRAID_DEVTYP_SMP_TGT         0x00000100
+#define LEAPRAID_DEVTYP_SATA_DEV        0x00000080
+#define LEAPRAID_DEVTYP_SSP_INIT        0x00000040
+#define LEAPRAID_DEVTYP_STP_INIT        0x00000020
+#define LEAPRAID_DEVTYP_SMP_INIT        0x00000010
+#define LEAPRAID_DEVTYP_SATA_HOST       0x00000008
+
+#define LEAPRAID_DEVTYP_MASK_DEV_TYPE   0x00000007
+#define LEAPRAID_DEVTYP_NO_DEV          0x00000000
+#define LEAPRAID_DEVTYP_END_DEV         0x00000001
+#define LEAPRAID_DEVTYP_EDGE_EXPANDER   0x00000002
+#define LEAPRAID_DEVTYP_FANOUT_EXPANDER 0x00000003
+
+/* SAS control operation. */
+#define LEAPRAID_SAS_OP_PHY_LINK_RESET  0x06
+#define LEAPRAID_SAS_OP_PHY_HARD_RESET  0x07
+#define LEAPRAID_SAS_OP_SET_PARAMETER   0x0F
+
+/* Boot device defines */
+#define LEAPRAID_BOOTDEV_FORM_MASK      0x0F
+#define LEAPRAID_BOOTDEV_FORM_NONE      0x00
+#define LEAPRAID_BOOTDEV_FORM_SAS_WWID  0x05
+#define LEAPRAID_BOOTDEV_FORM_ENC_SLOT  0x06
+#define LEAPRAID_BOOTDEV_FORM_DEV_NAME  0x07
+
+/**
+ * struct leapraid_reg_base - Register layout of the LeapRAID controller
+ *
+ * @db: Doorbell register used to signal commands or status to firmware.
+ * @ws: Write sequence register for synchronizing doorbell operations.
+ * @host_diag: Diagnostic register used for status or debug reporting.
+ * @r1: Reserved.
+ * @host_int_status: Interrupt status register reporting active interrupts.
+ * @host_int_mask: Interrupt mask register enabling or disabling sources.
+ * @r2: Reserved.
+ * @rep_msg_host_idx: Reply message index for the next available reply slot.
+ * @r3: Reserved.
+ * @debug_log: DebugLog registers for firmware debug and diagnostic output.
+ * @r4: Reserved.
+ * @atomic_req_desc_post: Atomic register for single descriptor posting.
+ * @adapter_log_buf_pos: Adapter log buffer write position.
+ * @host_log_buf_pos: Host log buffer write position.
+ * @r5: Reserved.
+ * @rep_post_reg_idx: Array of reply post index registers, one per queue.
+ *                    The number of entries is defined by
+ *                    REP_POST_HOST_IDX_REG_CNT.
+ */
+struct leapraid_reg_base {
+	__le32 db;
+	__le32 ws;
+	__le32 host_diag;
+	__le32 r1[9];
+	__le32 host_int_status;
+	__le32 host_int_mask;
+	__le32 r2[4];
+	__le32 rep_msg_host_idx;
+	__le32 r3[13];
+	__le32 debug_log[LEAPRAID_DEBUGLOG_SZ_MAX];
+	__le32 r4[2];
+	__le32 atomic_req_desc_post;
+	__le32 adapter_log_buf_pos;
+	__le32 host_log_buf_pos;
+	__le32 r5[142];
+	struct leapraid_rep_post_reg_idx {
+		__le32 idx;
+		__le32 r1;
+		__le32 r2;
+		__le32 r3;
+	} rep_post_reg_idx[REP_POST_HOST_IDX_REG_CNT];
+} __packed;
+
+/**
+ * struct leapraid_atomic_req_desc - Atomic request descriptor
+ *
+ * @flg: Descriptor flag indicating the type of request (e.g. SCSI I/O).
+ * @msix_idx: MSI-X vector index used for interrupt routing.
+ * @taskid: Unique task identifier associated with this request.
+ */
+struct leapraid_atomic_req_desc {
+	u8 flg;
+	u8 msix_idx;
+	__le16 taskid;
+};
+
+/**
+ * union leapraid_rep_desc_union - Unified reply descriptor format
+ *
+ * @dflt_rep: Default reply descriptor containing basic completion info.
+ * @dflt_rep.rep_flg: Reply flag indicating reply type or status.
+ * @dflt_rep.msix_idx: MSI-X index for interrupt routing.
+ * @dflt_rep.taskid: Task identifier matching the submitted request.
+ * @r1: Reserved.
+ *
+ * @addr_rep: Address reply descriptor used when firmware returns a
+ *            memory address associated with the reply.
+ * @addr_rep.rep_flg: Reply flag indicating reply type or status.
+ * @addr_rep.msix_idx: MSI-X index for interrupt routing.
+ * @addr_rep.taskid: Task identifier matching the submitted request.
+ * @addr_rep.rep_frame_addr: Physical address of the reply frame.
+ *
+ * @words: Raw 64-bit representation of the reply descriptor.
+ * @u: Alternative access using 32-bit low/high words.
+ * @u.low: Lower 32 bits of the descriptor.
+ * @u.high: Upper 32 bits of the descriptor.
+ */
+union leapraid_rep_desc_union {
+	struct leapraid_rep_desc {
+		u8 rep_flg;
+		u8 msix_idx;
+		__le16 taskid;
+		u8 r1[4];
+	} dflt_rep;
+	struct leapraid_add_rep_desc {
+		u8 rep_flg;
+		u8 msix_idx;
+		__le16 taskid;
+		__le32 rep_frame_addr;
+	} addr_rep;
+	__le64 words;
+	struct {
+		u32 low;
+		u32 high;
+	} u;
+}  __packed __aligned(4);
+
+/**
+ * struct leapraid_req - Generic request header
+ *
+ * @func_dep1: Function-dependent parameter (low 16 bits).
+ * @r1: Reserved.
+ * @func: Function code identifying the command type.
+ * @r2: Reserved.
+ */
+struct leapraid_req {
+	__le16 func_dep1;
+	u8 r1;
+	u8 func;
+	u8 r2[8];
+};
+
+/**
+ * struct leapraid_rep - Generic reply header
+ *
+ * @r1: Reserved.
+ * @msg_len: Length of the reply message in bytes.
+ * @function: Function code corresponding to the request.
+ * @r2: Reserved.
+ * @adapter_status: Status code reported by the adapter.
+ * @r3: Reserved.
+ */
+struct leapraid_rep {
+	u8 r1[2];
+	u8 msg_len;
+	u8 function;
+	u8 r2[10];
+	__le16 adapter_status;
+	u8 r3[4];
+};
+
+/**
+ * struct leapraid_sge_simple32 - 32-bit simple scatter-gather entry
+ *
+ * @flg_and_len: Combined field for flags and segment length.
+ * @addr: 32-bit physical address of the data buffer.
+ */
+struct leapraid_sge_simple32 {
+	__le32 flg_and_len;
+	__le32 addr;
+};
+
+/**
+ * struct leapraid_sge_simple64 - 64-bit simple scatter-gather entry
+ *
+ * @flg_and_len: Combined field for flags and segment length.
+ * @addr: 64-bit physical address of the data buffer.
+ */
+struct leapraid_sge_simple64 {
+	__le32 flg_and_len;
+	__le64 addr;
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_sge_simple_union - Unified 32/64-bit SGE representation
+ *
+ * @flg_and_len: Combined field for flags and segment length.
+ * @u.addr32: 32-bit address field.
+ * @u.addr64: 64-bit address field.
+ */
+struct leapraid_sge_simple_union {
+	__le32 flg_and_len;
+	union {
+		__le32 addr32;
+		__le64 addr64;
+	} __packed __aligned(4) u;
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_sge_chain_union - Chained scatter-gather entry
+ *
+ * @len: Length of the chain descriptor.
+ * @next_chain_offset: Offset to the next SGE chain.
+ * @flg: Flags indicating chain or termination properties.
+ * @u.addr32: 32-bit physical address.
+ * @u.addr64: 64-bit physical address.
+ */
+struct leapraid_sge_chain_union {
+	__le16 len;
+	u8 next_chain_offset;
+	u8 flg;
+	union {
+		__le32 addr32;
+		__le64 addr64;
+	} __packed __aligned(4) u;
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_ieee_sge_simple32 - IEEE 32-bit simple SGE format
+ *
+ * @addr: 32-bit physical address of the data buffer.
+ * @flg_and_len: Combined field for flags and data length.
+ */
+struct leapraid_ieee_sge_simple32 {
+	__le32 addr;
+	__le32 flg_and_len;
+};
+
+/**
+ * struct leapraid_ieee_sge_simple64 - IEEE 64-bit simple SGE format
+ *
+ * @addr: 64-bit physical address of the data buffer.
+ * @len: Length of the data segment.
+ * @r1: Reserved.
+ * @flg: Flags indicating transfer properties.
+ */
+struct leapraid_ieee_sge_simple64 {
+	__le64 addr;
+	__le32 len;
+	u8 r1[3];
+	u8 flg;
+} __packed __aligned(4);
+
+/**
+ * union leapraid_ieee_sge_simple_union - Unified IEEE SGE format
+ *
+ * @simple32: IEEE 32-bit simple SGE entry.
+ * @simple64: IEEE 64-bit simple SGE entry.
+ */
+union leapraid_ieee_sge_simple_union {
+	struct leapraid_ieee_sge_simple32 simple32;
+	struct leapraid_ieee_sge_simple64 simple64;
+};
+
+/**
+ * union leapraid_ieee_sge_chain_union - Unified IEEE SGE chain format
+ *
+ * @chain32: IEEE 32-bit chain SGE entry.
+ * @chain64: IEEE 64-bit chain SGE entry.
+ */
+union leapraid_ieee_sge_chain_union {
+	struct leapraid_ieee_sge_simple32 chain32;
+	struct leapraid_ieee_sge_simple64 chain64;
+};
+
+/**
+ * struct leapraid_chain64_ieee_sg - 64-bit IEEE chain SGE descriptor
+ *
+ * @addr: Physical address of the next chain segment.
+ * @len: Length of the current SGE.
+ * @r1: Reserved.
+ * @next_chain_offset: Offset to the next chain element.
+ * @flg: Flags that describe SGE attributes.
+ */
+struct leapraid_chain64_ieee_sg {
+	__le64 addr;
+	__le32 len;
+	u8 r1[2];
+	u8 next_chain_offset;
+	u8 flg;
+} __packed __aligned(4);
+
+/**
+ * union leapraid_ieee_sge_io_union - IEEE-style SGE union for I/O
+ *
+ * @ieee_simple: Simple IEEE SGE descriptor.
+ * @ieee_chain: IEEE chain SGE descriptor.
+ */
+union leapraid_ieee_sge_io_union {
+	struct leapraid_ieee_sge_simple64 ieee_simple;
+	struct leapraid_chain64_ieee_sg ieee_chain;
+};
+
+/**
+ * union leapraid_simple_sge_union - Union of simple SGE descriptors
+ *
+ * @leapraid_simple: LeapRAID simple SGE.
+ * @ieee_simple: IEEE-style simple SGE.
+ */
+union leapraid_simple_sge_union {
+	struct leapraid_sge_simple_union leapraid_simple;
+	union leapraid_ieee_sge_simple_union ieee_simple;
+};
+
+/**
+ * union leapraid_sge_io_union - Combined SGE union for all I/O types
+ *
+ * @leapraid_simple: LeapRAID simple SGE format.
+ * @leapraid_chain: LeapRAID chain SGE format.
+ * @ieee_simple: IEEE simple SGE format.
+ * @ieee_chain: IEEE chain SGE format.
+ */
+union leapraid_sge_io_union {
+	struct leapraid_sge_simple_union leapraid_simple;
+	struct leapraid_sge_chain_union leapraid_chain;
+	union leapraid_ieee_sge_simple_union ieee_simple;
+	union leapraid_ieee_sge_chain_union ieee_chain;
+};
+
+/**
+ * struct leapraid_cfg_pg_header - Standard configuration page header
+ *
+ * @r1: Reserved.
+ * @page_len: Length of the page in 4-byte units.
+ * @page_num: Page number.
+ * @page_type: Page type.
+ */
+struct leapraid_cfg_pg_header {
+	u8 r1;
+	u8 page_len;
+	u8 page_num;
+	u8 page_type;
+};
+
+/**
+ * struct leapraid_cfg_ext_pg_header - Extended configuration page header
+ *
+ * @r1: Reserved.
+ * @r2: Reserved.
+ * @page_num: Page number.
+ * @page_type: Page type.
+ * @ext_page_len: Extended page length.
+ * @ext_page_type: Extended page type.
+ * @r3: Reserved.
+ */
+struct leapraid_cfg_ext_pg_header {
+	u8 r1;
+	u8 r2;
+	u8 page_num;
+	u8 page_type;
+	__le16 ext_page_len;
+	u8 ext_page_type;
+	u8 r3;
+};
+
+/**
+ * struct leapraid_cfg_req - Configuration request message
+ *
+ * @action: Requested action type.
+ * @sgl_flag: SGL flag field.
+ * @chain_offset: Offset to next chain SGE.
+ * @func: Function code.
+ * @ext_page_len: Extended page length.
+ * @ext_page_type: Extended page type.
+ * @msg_flag: Message flags.
+ * @r1: Reserved.
+ * @header: Configuration page header.
+ * @page_addr: Address of the page buffer.
+ * @page_buf_sge: SGE describing the page buffer.
+ */
+struct leapraid_cfg_req {
+	u8 action;
+	u8 sgl_flag;
+	u8 chain_offset;
+	u8 func;
+	__le16 ext_page_len;
+	u8 ext_page_type;
+	u8 msg_flag;
+	u8 r1[12];
+	struct leapraid_cfg_pg_header header;
+	__le32 page_addr;
+	union leapraid_sge_io_union page_buf_sge;
+};
+
+/**
+ * struct leapraid_cfg_rep - Configuration reply message
+ *
+ * @action: Action type from the request.
+ * @r1: Reserved.
+ * @msg_len: Message length in bytes.
+ * @func: Function code.
+ * @ext_page_len: Extended page length.
+ * @ext_page_type: Extended page type.
+ * @msg_flag: Message flags.
+ * @r2: Reserved.
+ * @adapter_status: Adapter status code.
+ * @r3: Reserved.
+ * @header: Configuration page header.
+ */
+struct leapraid_cfg_rep {
+	u8 action;
+	u8 r1;
+	u8 msg_len;
+	u8 func;
+	__le16 ext_page_len;
+	u8 ext_page_type;
+	u8 msg_flag;
+	u8 r2[6];
+	__le16 adapter_status;
+	u8 r3[4];
+	struct leapraid_cfg_pg_header header;
+};
+
+/**
+ * struct leapraid_boot_dev_format_sas_wwid - Boot device identified by wwid
+ *
+ * @sas_addr: SAS address of the device.
+ * @lun: Logical unit number.
+ * @r1: Reserved.
+ */
+struct leapraid_boot_dev_format_sas_wwid {
+	__le64 sas_addr;
+	u8 lun[8];
+	u8 r1[8];
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_boot_dev_format_enc_slot - identified by enclosure
+ *
+ * @enc_lid: Enclosure logical ID.
+ * @r1: Reserved.
+ * @slot_num: Slot number in the enclosure.
+ * @r2: Reserved.
+ */
+struct leapraid_boot_dev_format_enc_slot {
+	__le64 enc_lid;
+	u8 r1[8];
+	__le16 slot_num;
+	u8 r2[6];
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_boot_dev_format_dev_name - Boot device by device name
+ *
+ * @dev_name: Device name identifier.
+ * @lun: Logical unit number.
+ * @r1: Reserved.
+ */
+struct leapraid_boot_dev_format_dev_name {
+	__le64 dev_name;
+	u8 lun[8];
+	u8 r1[8];
+} __packed __aligned(4);
+
+/**
+ * union leapraid_boot_dev_format - Boot device format union
+ *
+ * @sas_wwid: Format using SAS WWID and LUN.
+ * @enc_slot: Format using enclosure slot and ID.
+ * @dev_name: Format using device name and LUN.
+ */
+union leapraid_boot_dev_format {
+	struct leapraid_boot_dev_format_sas_wwid sas_wwid;
+	struct leapraid_boot_dev_format_enc_slot enc_slot;
+	struct leapraid_boot_dev_format_dev_name dev_name;
+};
+
+/**
+ * struct leapraid_manufacturing_p0 - Manufacturing configuration page 0
+ *
+ * @header: Configuration page header.
+ * @chip_name: Chip name as a 16-byte ASCII string.
+ * @chip_revision: Chip revision as an 8-byte ASCII string.
+ * @board_name: Board name as a 16-byte ASCII string.
+ * @board_assembly: Board assembly as a 16-byte ASCII string.
+ * @board_tracer_number: Board tracer number as a 16-byte ASCII string.
+ */
+struct leapraid_manufacturing_p0 {
+	struct leapraid_cfg_pg_header header;
+	u8 chip_name[16];
+	u8 chip_revision[8];
+	u8 board_name[16];
+	u8 board_assembly[16];
+	u8 board_tracer_number[16];
+};
+
+/**
+ * struct leapraid_bios_page2 - BIOS configuration page 2
+ *
+ * @header: Configuration page header.
+ * @r1: Reserved.
+ * @requested_boot_dev_form: Format type of the requested boot device.
+ * @r2: Reserved.
+ * @requested_boot_dev: Boot device requested by BIOS or user.
+ * @requested_alt_boot_dev_form: Format of the alternate boot device.
+ * @r3: Reserved.
+ * @requested_alt_boot_dev: Alternate boot device requested.
+ * @current_boot_dev_form: Format type of the active boot device.
+ * @r4: Reserved.
+ * @current_boot_dev: Currently active boot device in use.
+ */
+struct leapraid_bios_page2 {
+	struct leapraid_cfg_pg_header header;
+	u8 r1[24];
+	u8 requested_boot_dev_form;
+	u8 r2[3];
+	union leapraid_boot_dev_format requested_boot_dev;
+	u8 requested_alt_boot_dev_form;
+	u8 r3[3];
+	union leapraid_boot_dev_format requested_alt_boot_dev;
+	u8 current_boot_dev_form;
+	u8 r4[3];
+	union leapraid_boot_dev_format current_boot_dev;
+};
+
+/**
+ * struct leapraid_bios_page3 - BIOS configuration page 3
+ *
+ * @header: Configuration page header.
+ * @r1: Reserved.
+ * @bios_version: BIOS firmware version number.
+ * @r2: Reserved.
+ */
+struct leapraid_bios_page3 {
+	struct leapraid_cfg_pg_header header;
+	u8 r1[4];
+	__le32 bios_version;
+	u8 r2[84];
+};
+
+/**
+ * struct leapraid_raidvol0_phys_disk - Physical disk in RAID volume
+ *
+ * @r1: Reserved.
+ * @phys_disk_num: Physical disk number within the RAID volume.
+ * @r2: Reserved.
+ */
+struct leapraid_raidvol0_phys_disk {
+	u8 r1[2];
+	u8 phys_disk_num;
+	u8 r2;
+};
+
+/**
+ * struct leapraid_raidvol_p0 - RAID volume configuration page 0
+ *
+ * @header: Configuration page header.
+ * @dev_hdl: Device handle for the RAID volume.
+ * @volume_state: State of the RAID volume.
+ * @volume_type: RAID type.
+ * @r1: Reserved.
+ * @num_phys_disks: Number of physical disks in the volume.
+ * @r2: Reserved.
+ * @phys_disk: Array of physical disks in this volume.
+ */
+struct leapraid_raidvol_p0 {
+	struct leapraid_cfg_pg_header header;
+	__le16 dev_hdl;
+	u8 volume_state;
+	u8 volume_type;
+	u8 r1[28];
+	u8 num_phys_disks;
+	u8 r2[3];
+	struct leapraid_raidvol0_phys_disk phys_disk[];
+};
+
+/**
+ * struct leapraid_raidvol_p1 - RAID volume configuration page 1
+ *
+ * @header: Configuration page header.
+ * @dev_hdl: Device handle of the RAID volume.
+ * @r1: Reserved.
+ * @wwid: World-wide identifier for the volume.
+ * @r2: Reserved.
+ */
+struct leapraid_raidvol_p1 {
+	struct leapraid_cfg_pg_header header;
+	__le16 dev_hdl;
+	u8 r1[42];
+	__le64 wwid;
+	u8 r2[8];
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_raidpd_p0 - Physical disk configuration page 0
+ *
+ * @header: Configuration page header.
+ * @dev_hdl: Device handle of the physical disk.
+ * @r1: Reserved.
+ * @phys_disk_num: Physical disk number.
+ * @r2: Reserved.
+ */
+struct leapraid_raidpd_p0 {
+	struct leapraid_cfg_pg_header header;
+	__le16 dev_hdl;
+	u8 r1;
+	u8 phys_disk_num;
+	u8 r2[112];
+};
+
+/**
+ * struct leapraid_sas_io_unit0_phy_info - PHY info for SAS I/O unit
+ *
+ * @port: Port number the PHY belongs to.
+ * @port_flg: Flags describing port status.
+ * @phy_flg: Flags describing PHY status.
+ * @neg_link_rate: Negotiated link rate of the PHY.
+ * @controller_phy_dev_info: Controller PHY device info.
+ * @attached_dev_hdl: Handle of attached device.
+ * @controller_dev_hdl: Handle of the controller device.
+ * @r1: Reserved.
+ */
+struct leapraid_sas_io_unit0_phy_info {
+	u8 port;
+	u8 port_flg;
+	u8 phy_flg;
+	u8 neg_link_rate;
+	__le32 controller_phy_dev_info;
+	__le16 attached_dev_hdl;
+	__le16 controller_dev_hdl;
+	u8 r1[8];
+};
+
+/**
+ * struct leapraid_sas_io_unit_p0 - SAS I/O unit configuration page 0
+ *
+ * @header: Extended configuration page header.
+ * @r1: Reserved.
+ * @phy_num: Number of PHYs in this unit.
+ * @r2: Reserved.
+ * @phy_info: Array of PHY information.
+ */
+struct leapraid_sas_io_unit_p0 {
+	struct leapraid_cfg_ext_pg_header header;
+	u8 r1[4];
+	u8 phy_num;
+	u8 r2[3];
+	struct leapraid_sas_io_unit0_phy_info phy_info[];
+};
+
+/**
+ * struct leapraid_exp_p0 - SAS expander page 0
+ *
+ * @header: Extended page header.
+ * @physical_port: Physical port number.
+ * @r1: Reserved.
+ * @enc_hdl: Enclosure handle.
+ * @sas_address: SAS address of the expander.
+ * @r2: Reserved.
+ * @dev_hdl: Device handle of this expander.
+ * @parent_dev_hdl: Device handle of parent expander.
+ * @r3: Reserved.
+ * @phy_num: Number of PHYs.
+ * @r4: Reserved.
+ */
+struct leapraid_exp_p0 {
+	struct leapraid_cfg_ext_pg_header header;
+	u8 physical_port;
+	u8 r1;
+	__le16 enc_hdl;
+	__le64 sas_address;
+	u8 r2[4];
+	__le16 dev_hdl;
+	__le16 parent_dev_hdl;
+	u8 r3[4];
+	u8 phy_num;
+	u8 r4[27];
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_exp_p1 - SAS expander page 1
+ *
+ * @header: Extended page header.
+ * @r1: Reserved.
+ * @p_link_rate: PHY link rate.
+ * @hw_link_rate: Hardware supported link rate.
+ * @attached_dev_hdl: Attached device handle.
+ * @r2: Reserved.
+ * @neg_link_rate: Negotiated link rate.
+ * @r3: Reserved.
+ */
+struct leapraid_exp_p1 {
+	struct leapraid_cfg_ext_pg_header header;
+	u8 r1[8];
+	u8 p_link_rate;
+	u8 hw_link_rate;
+	__le16 attached_dev_hdl;
+	u8 r2[11];
+	u8 neg_link_rate;
+	u8 r3[12];
+};
+
+/**
+ * struct leapraid_sas_dev_p0 - SAS device page 0
+ *
+ * @header: Extended configuration page header.
+ * @slot: Slot number.
+ * @enc_hdl: Enclosure handle.
+ * @sas_address: SAS address.
+ * @parent_dev_hdl: Parent device handle.
+ * @phy_num: Number of PHYs.
+ * @r1: Reserved.
+ * @dev_hdl: Device handle.
+ * @r2: Reserved.
+ * @dev_info: Device information.
+ * @flg: Flags.
+ * @physical_port: Physical port number.
+ * @max_port_connections: Maximum port connections.
+ * @dev_name: Device name.
+ * @port_groups: Number of port groups.
+ * @r3: Reserved.
+ * @enc_level: Enclosure level.
+ * @connector_name: Connector identifier.
+ * @r4: Reserved.
+ */
+struct leapraid_sas_dev_p0 {
+	struct leapraid_cfg_ext_pg_header header;
+	__le16 slot;
+	__le16 enc_hdl;
+	__le64 sas_address;
+	__le16 parent_dev_hdl;
+	u8 phy_num;
+	u8 r1;
+	__le16 dev_hdl;
+	u8 r2[2];
+	__le32 dev_info;
+	__le16 flg;
+	u8 physical_port;
+	u8 max_port_connections;
+	__le64 dev_name;
+	u8 port_groups;
+	u8 r3[2];
+	u8 enc_level;
+	u8 connector_name[LEAPRAID_SAS_DEV_P0_CON_NAME_LEN];
+	u8 r4[4];
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_sas_phy_p0 - SAS PHY configuration page 0
+ *
+ * @header: Extended configuration page header.
+ * @r1: Reserved.
+ * @attached_dev_hdl: Handle of attached device.
+ * @r2: Reserved.
+ * @p_link_rate: PHY link rate.
+ * @hw_link_rate: Hardware supported link rate.
+ * @r3: Reserved.
+ * @phy_info: PHY information.
+ * @neg_link_rate: Negotiated link rate.
+ * @r4: Reserved.
+ */
+struct leapraid_sas_phy_p0 {
+	struct leapraid_cfg_ext_pg_header header;
+	u8 r1[4];
+	__le16 attached_dev_hdl;
+	u8 r2[6];
+	u8 p_link_rate;
+	u8 hw_link_rate;
+	u8 r3[2];
+	__le32 phy_info;
+	u8 neg_link_rate;
+	u8 r4[3];
+};
+
+/**
+ * struct leapraid_enc_p0 - SAS enclosure page 0
+ *
+ * @header: Extended configuration page header.
+ * @r1: Reserved.
+ * @enc_lid: Enclosure logical ID.
+ * @r2: Reserved.
+ * @enc_hdl: Enclosure handle.
+ * @r3: Reserved.
+ */
+struct leapraid_enc_p0 {
+	struct leapraid_cfg_ext_pg_header header;
+	u8 r1[4];
+	__le64 enc_lid;
+	u8 r2[2];
+	__le16 enc_hdl;
+	u8 r3[15];
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_raid_cfg_p0_element - RAID configuration element
+ *
+ * @element_flg: Element flags.
+ * @vol_dev_hdl: Volume device handle.
+ * @r1: Reserved.
+ * @phys_disk_dev_hdl: Physical disk device handle.
+ */
+struct leapraid_raid_cfg_p0_element {
+	__le16 element_flg;
+	__le16 vol_dev_hdl;
+	u8 r1[2];
+	__le16 phys_disk_dev_hdl;
+};
+
+/**
+ * struct leapraid_raid_cfg_p0 - RAID configuration page 0
+ *
+ * @header: Extended configuration page header.
+ * @r1: Reserved.
+ * @cfg_num: Configuration number.
+ * @r2: Reserved.
+ * @elements_num: Number of RAID elements.
+ * @r3: Reserved.
+ * @cfg_element: Array of RAID elements.
+ */
+struct leapraid_raid_cfg_p0 {
+	struct leapraid_cfg_ext_pg_header header;
+	u8 r1[3];
+	u8 cfg_num;
+	u8 r2[32];
+	u8 elements_num;
+	u8 r3[3];
+	struct leapraid_raid_cfg_p0_element cfg_element[];
+};
+
+/**
+ * union leapraid_mpi_scsi_io_cdb_union - SCSI I/O CDB or simple SGE
+ *
+ * @cdb32: 32-byte SCSI command descriptor block.
+ * @sge: Simple SGE format.
+ */
+union leapraid_mpi_scsi_io_cdb_union {
+	u8 cdb32[32];
+	struct leapraid_sge_simple_union sge;
+};
+
+/**
+ * struct leapraid_mpi_scsiio_req - MPI SCSI I/O request
+ *
+ * @dev_hdl: Device handle for the target.
+ * @chain_offset: Offset for chained SGE.
+ * @func: Function code.
+ * @r1: Reserved.
+ * @msg_flg: Message flags.
+ * @r2: Reserved.
+ * @sense_buffer_low_add: Lower 32-bit address of sense buffer.
+ * @dma_flag: DMA flags.
+ * @r3: Reserved.
+ * @sense_buffer_len: Sense buffer length.
+ * @r4: Reserved.
+ * @sgl_offset0..3: SGL offsets.
+ * @skip_count: Bytes to skip before transfer.
+ * @data_len: Length of data transfer.
+ * @bi_dir_data_len: Bi-directional transfer length.
+ * @io_flg: I/O flags.
+ * @eedp_flag: End-to-end data protection flags.
+ * @eedp_block_size: End-to-end data protection block size.
+ * @r5: Reserved.
+ * @secondary_ref_tag: Secondary reference tag.
+ * @secondary_app_tag: Secondary application tag.
+ * @app_tag_trans_mask: Application tag mask.
+ * @lun: Logical Unit Number.
+ * @ctrl: Control flags.
+ * @cdb: SCSI Command Descriptor Block or simple SGE.
+ * @sgl: Scatter-gather list.
+ */
+struct leapraid_mpi_scsiio_req {
+	__le16 dev_hdl;
+	u8 chain_offset;
+	u8 func;
+	u8 r1[3];
+	u8 msg_flg;
+	u8 r2[4];
+	__le32 sense_buffer_low_add;
+	u8 dma_flag;
+	u8 r3;
+	u8 sense_buffer_len;
+	u8 r4;
+	u8 sgl_offset0;
+	u8 sgl_offset1;
+	u8 sgl_offset2;
+	u8 sgl_offset3;
+	__le32 skip_count;
+	__le32 data_len;
+	__le32 bi_dir_data_len;
+	__le16 io_flg;
+	__le16 eedp_flag;
+	__le16 eedp_block_size;
+	u8 r5[2];
+	__le32 secondary_ref_tag;
+	__le16 secondary_app_tag;
+	__le16 app_tag_trans_mask;
+	u8 lun[8];
+	__le32 ctrl;
+	union leapraid_mpi_scsi_io_cdb_union cdb;
+	union leapraid_sge_io_union sgl;
+};
+
+/**
+ * union leapraid_scsi_io_cdb_union - SCSI I/O CDB or IEEE simple SGE
+ *
+ * @cdb32: 32-byte SCSI CDB.
+ * @sge: IEEE simple 64-bit SGE.
+ */
+union leapraid_scsi_io_cdb_union {
+	u8 cdb32[32];
+	struct leapraid_ieee_sge_simple64 sge;
+};
+
+/**
+ * struct leapraid_scsiio_req - SCSI I/O request
+ *
+ * @dev_hdl: Device handle.
+ * @chain_offset: Offset for chained SGE.
+ * @func: Function code.
+ * @r1: Reserved.
+ * @msg_flg: Message flags.
+ * @r2: Reserved.
+ * @sense_buffer_low_add: Lower 32-bit address of sense buffer.
+ * @dma_flag: DMA flag.
+ * @r3: Reserved.
+ * @sense_buffer_len: Sense buffer length.
+ * @r4: Reserved.
+ * @sgl_offset0-3: SGL offsets.
+ * @skip_count: Bytes to skip before transfer.
+ * @data_len: Length of data transfer.
+ * @bi_dir_data_len: Bi-directional transfer length.
+ * @io_flg: I/O flags.
+ * @eedp_flag: End-to-end data protection flags.
+ * @eedp_block_size: End-to-end data protection block size.
+ * @r5: Reserved.
+ * @secondary_ref_tag: Secondary reference tag.
+ * @secondary_app_tag: Secondary application tag.
+ * @app_tag_trans_mask: Application tag mask.
+ * @lun: Logical Unit Number.
+ * @ctrl: Control flags.
+ * @cdb: SCSI Command Descriptor Block or simple SGE.
+ * @sgl: Scatter-gather list.
+ */
+struct leapraid_scsiio_req {
+	__le16 dev_hdl;
+	u8 chain_offset;
+	u8 func;
+	u8 r1[3];
+	u8 msg_flg;
+	u8 r2[4];
+	__le32 sense_buffer_low_add;
+	u8 dma_flag;
+	u8 r3;
+	u8 sense_buffer_len;
+	u8 r4;
+	u8 sgl_offset0;
+	u8 sgl_offset1;
+	u8 sgl_offset2;
+	u8 sgl_offset3;
+	__le32 skip_count;
+	__le32 data_len;
+	__le32 bi_dir_data_len;
+	__le16 io_flg;
+	__le16 eedp_flag;
+	__le16 eedp_block_size;
+	u8 r5[2];
+	__le32 secondary_ref_tag;
+	__le16 secondary_app_tag;
+	__le16 app_tag_trans_mask;
+	u8 lun[8];
+	__le32 ctrl;
+	union leapraid_scsi_io_cdb_union cdb;
+	union leapraid_ieee_sge_io_union sgl;
+};
+
+/**
+ * struct leapraid_scsiio_rep - SCSI I/O response
+ *
+ * @dev_hdl: Device handle.
+ * @msg_len: Length of response message.
+ * @func: Function code.
+ * @r1: Reserved.
+ * @msg_flg: Message flags.
+ * @r2: Reserved.
+ * @scsi_status: SCSI status.
+ * @scsi_state: SCSI state.
+ * @adapter_status: Adapter status.
+ * @r3: Reserved.
+ * @transfer_count: Number of bytes transferred.
+ * @sense_count: Number of sense bytes.
+ * @resp_info: Additional response info.
+ * @task_tag: Task identifier.
+ * @scsi_status_qualifier: SCSI status qualifier.
+ * @bi_dir_trans_count: Bi-directional transfer count.
+ * @r4: Reserved.
+ */
+struct leapraid_scsiio_rep {
+	__le16 dev_hdl;
+	u8 msg_len;
+	u8 func;
+	u8 r1[3];
+	u8 msg_flg;
+	u8 r2[4];
+	u8 scsi_status;
+	u8 scsi_state;
+	__le16 adapter_status;
+	u8 r3[4];
+	__le32 transfer_count;
+	__le32 sense_count;
+	__le32 resp_info;
+	__le16 task_tag;
+	__le16 scsi_status_qualifier;
+	__le32 bi_dir_trans_count;
+	__le32 r4[3];
+};
+
+/**
+ * struct leapraid_scsi_tm_req - SCSI Task Management request
+ *
+ * @dev_hdl: Device handle.
+ * @chain_offset: Offset for chained SGE.
+ * @func: Function code.
+ * @r1: Reserved.
+ * @task_type: Task management function type.
+ * @r2: Reserved.
+ * @msg_flg: Message flags.
+ * @r3: Reserved.
+ * @lun: Logical Unit Number.
+ * @r4: Reserved.
+ * @task_mid: Task identifier.
+ * @r5: Reserved.
+ */
+struct leapraid_scsi_tm_req {
+	__le16 dev_hdl;
+	u8 chain_offset;
+	u8 func;
+	u8 r1;
+	u8 task_type;
+	u8 r2;
+	u8 msg_flg;
+	u8 r3[4];
+	u8 lun[8];
+	u8 r4[28];
+	__le16 task_mid;
+	u8 r5[2];
+};
+
+/**
+ * struct leapraid_scsi_tm_rep - SCSI Task Management response
+ *
+ * @dev_hdl: Device handle.
+ * @msg_len: Length of response message.
+ * @func: Function code.
+ * @resp_code: Response code.
+ * @task_type: Task management type.
+ * @r1: Reserved.
+ * @msg_flag: Message flags.
+ * @r2: Reserved.
+ * @adapter_status: Adapter status.
+ * @r3: Reserved.
+ * @termination_count: Count of terminated tasks.
+ * @response_info: Additional response info.
+ */
+struct leapraid_scsi_tm_rep {
+	__le16 dev_hdl;
+	u8 msg_len;
+	u8 func;
+	u8 resp_code;
+	u8 task_type;
+	u8 r1;
+	u8 msg_flag;
+	u8 r2[6];
+	__le16 adapter_status;
+	u8 r3[4];
+	__le32 termination_count;
+	__le32 response_info;
+};
+
+/**
+ * struct leapraid_sep_req - SEP (SCSI Enclosure Processor) request
+ *
+ * @dev_hdl: Device handle.
+ * @chain_offset: Offset for chained SGE.
+ * @func: Function code.
+ * @act: Action to perform.
+ * @flg: Flags.
+ * @r1: Reserved.
+ * @msg_flag: Message flags.
+ * @r2: Reserved.
+ * @slot_status: Slot status.
+ * @r3: Reserved.
+ * @slot: Slot number.
+ * @enc_hdl: Enclosure handle.
+ */
+struct leapraid_sep_req {
+	__le16 dev_hdl;
+	u8 chain_offset;
+	u8 func;
+	u8 act;
+	u8 flg;
+	u8 r1;
+	u8 msg_flag;
+	u8 r2[4];
+	__le32 slot_status;
+	u8 r3[12];
+	__le16 slot;
+	__le16 enc_hdl;
+};
+
+/**
+ * struct leapraid_sep_rep - SEP response
+ *
+ * @dev_hdl: Device handle.
+ * @msg_len: Message length.
+ * @func: Function code.
+ * @act: Action performed.
+ * @flg: Flags.
+ * @msg_flag: Message flags.
+ * @r1: Reserved.
+ * @adapter_status: Adapter status.
+ * @r2: Reserved.
+ * @slot_status: Slot status.
+ * @r3: Reserved.
+ * @slot: Slot number.
+ * @enc_hdl: Enclosure handle.
+ */
+struct leapraid_sep_rep {
+	__le16 dev_hdl;
+	u8 msg_len;
+	u8 func;
+	u8 act;
+	u8 flg;
+	u8 r1;
+	u8 msg_flag;
+	u8 r2[6];
+	__le16 adapter_status;
+	u8 r3[4];
+	__le32 slot_status;
+	u8 r4[4];
+	__le16 slot;
+	__le16 enc_hdl;
+};
+
+/**
+ * struct leapraid_adapter_init_req - Adapter initialization request
+ *
+ * @who_init: Initiator of the initialization.
+ * @r1: Reserved.
+ * @chain_offset: Chain offset.
+ * @func: Function code.
+ * @r2: Reserved.
+ * @msg_flg: Message flags.
+ * @r3: Reserved.
+ * @msg_ver: Message version.
+ * @header_ver: Header version.
+ * @host_buf_addr: Host buffer address (non adapter-ref).
+ * @r4: Reserved.
+ * @host_buf_size: Host buffer size (non adapter-ref).
+ * @host_msix_vectors: Number of host MSI-X vectors.
+ * @r6: Reserved.
+ * @req_frame_size: Request frame size.
+ * @rep_desc_qd: Reply descriptor queue depth.
+ * @rep_msg_qd: Reply message queue depth.
+ * @sense_buffer_add_high: High 32-bit of sense buffer address.
+ * @rep_msg_dma_high: High 32-bit of reply message DMA address.
+ * @task_desc_base_addr: Base address of task descriptors.
+ * @rep_desc_q_arr_addr: Address of reply descriptor queue array.
+ * @rep_msg_addr_dma: Reply message DMA address.
+ * @time_stamp: Timestamp.
+ */
+struct leapraid_adapter_init_req {
+	u8 who_init;
+	u8 r1;
+	u8 chain_offset;
+	u8 func;
+	u8 r2[3];
+	u8 msg_flg;
+	__le32 driver_ver;
+	__le16 msg_ver;
+	__le16 header_ver;
+	__le32 host_buf_addr;
+	u8 r4[2];
+	u8 host_buf_size;
+	u8 host_msix_vectors;
+	u8 r6[2];
+	__le16 req_frame_size;
+	__le16 rep_desc_qd;
+	__le16 rep_msg_qd;
+	__le32 sense_buffer_add_high;
+	__le32 rep_msg_dma_high;
+	__le64 task_desc_base_addr;
+	__le64 rep_desc_q_arr_addr;
+	__le64 rep_msg_addr_dma;
+	__le64 time_stamp;
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_rep_desc_q_arr - Reply descriptor queue array
+ *
+ * @rep_desc_base_addr: Base address of the reply descriptors.
+ * @r1: Reserved.
+ */
+struct leapraid_rep_desc_q_arr {
+	__le64 rep_desc_base_addr;
+	__le64 r1;
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_adapter_init_rep - Adapter initialization reply
+ *
+ * @who_init: Initiator of the initialization.
+ * @r1: Reserved.
+ * @msg_len: Length of reply message.
+ * @func: Function code.
+ * @r2: Reserved.
+ * @msg_flag: Message flags.
+ * @r3: Reserved.
+ * @adapter_status: Adapter status.
+ * @r4: Reserved.
+ */
+struct leapraid_adapter_init_rep {
+	u8 who_init;
+	u8 r1;
+	u8 msg_len;
+	u8 func;
+	u8 r2[3];
+	u8 msg_flag;
+	u8 r3[6];
+	__le16 adapter_status;
+	u8 r4[4];
+};
+
+/**
+ * struct leapraid_adapter_log_req - Adapter log request
+ *
+ * @action: Action code.
+ * @type: Log type.
+ * @chain_offset: Offset for chained SGE.
+ * @func: Function code.
+ * r1: Reserved.
+ * @msg_flag: Message flags.
+ * r2: Reserved.
+ * @mbox: Mailbox for command-specific parameters.
+ * @sge: Scatter-gather entry for data buffer.
+ */
+struct leapraid_adapter_log_req {
+	u8 action;
+	u8 type;
+	u8 chain_offset;
+	u8 func;
+	u8 r1[3];
+	u8 msg_flag;
+	u8 r2[4];
+	union {
+		u8	b[12];
+		__le16	s[6];
+		__le32	w[3];
+	} mbox;
+	struct leapraid_sge_simple64 sge;
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_adapter_log_rep - Adapter log reply
+ *
+ * @action: Action code echoed.
+ * @type: Log type echoed.
+ * @msg_len: Length of message.
+ * @func: Function code.
+ * @r1: Reserved.
+ * @msg_flag: Message flags.
+ * @r2: Reserved.
+ * @adapter_status: Status returned by adapter.
+ */
+struct leapraid_adapter_log_rep {
+	u8 action;
+	u8 type;
+	u8 msg_len;
+	u8 func;
+	u8 r1[3];
+	u8 msg_flag;
+	u8 r2[6];
+	__le16 adapter_status;
+};
+
+/**
+ * struct leapraid_adapter_features_req - Request adapter features
+ *
+ * @r1: Reserved.
+ * @chain_offset: Offset for chained SGE.
+ * @func: Function code.
+ * @r2: Reserved.
+ * @msg_flag: Message flags.
+ * @r3: Reserved.
+ */
+struct leapraid_adapter_features_req {
+	u8 r1[2];
+	u8 chain_offset;
+	u8 func;
+	u8 r2[3];
+	u8 msg_flag;
+	u8 r3[4];
+};
+
+/**
+ * struct leapraid_adapter_features_rep - Adapter features reply
+ *
+ * @msg_ver: Message version.
+ * @msg_len: Length of reply message.
+ * @func: Function code.
+ * @header_ver: Header version.
+ * @r1: Reserved.
+ * @msg_flag: Message flags.
+ * @r2: Reserved.
+ * @adapter_status: Adapter status.
+ * @r3: Reserved.
+ * @who_init: Who initialized the adapter.
+ * @r4: Reserved.
+ * @max_msix_vectors: Max MSI-X vectors supported.
+ * @req_slot: Number of request slots.
+ * @r5: Reserved.
+ * @adapter_caps: Adapter capabilities.
+ * @fw_version: Firmware version.
+ * @sas_wide_max_qdepth: Max wide SAS queue depth.
+ * @sas_narrow_max_qdepth: Max narrow SAS queue depth.
+ * @r6: Reserved.
+ * @hp_slot: Number of high-priority slots.
+ * @r7: Reserved.
+ * @max_volumes: Maximum supported volumes.
+ * @max_dev_hdl: Maximum device handle.
+ * @r8: Reserved.
+ * @min_dev_hdl: Minimum device handle.
+ * @r9: Reserved.
+ */
+struct leapraid_adapter_features_rep {
+	__le16 msg_ver;
+	u8 msg_len;
+	u8 func;
+	u16 header_ver;
+	u8 r1;
+	u8 msg_flag;
+	u8 r2[6];
+	u16 adapter_status;
+	u8 r3[4];
+	u8 sata_max_qdepth;
+	u8 who_init;
+	u8 r4;
+	u8 max_msix_vectors;
+	__le16 req_slot;
+	__le16 product_id;
+	__le32 adapter_caps;
+	__le32 fw_version;
+	__le16 sas_wide_max_qdepth;
+	__le16 sas_narrow_max_qdepth;
+	u8 r6[10];
+	__le16 hp_slot;
+	u8 r7[3];
+	u8 max_volumes;
+	__le16 max_dev_hdl;
+	u8 r8[2];
+	__le16 min_dev_hdl;
+	u8 r9[6];
+};
+
+/**
+ * struct leapraid_scan_dev_req - Request to scan devices
+ *
+ * @r1: Reserved.
+ * @chain_offset: Offset for chained SGE.
+ * @func: Function code.
+ * @r2: Reserved.
+ * @msg_flag: Message flags.
+ * @r3: Reserved.
+ */
+struct leapraid_scan_dev_req {
+	u8 r1[2];
+	u8 chain_offset;
+	u8 func;
+	u8 r2[3];
+	u8 msg_flag;
+	u8 r3[4];
+};
+
+/**
+ * struct leapraid_scan_dev_rep - Scan devices reply
+ *
+ * @r1: Reserved.
+ * @msg_len: Length of message.
+ * @func: Function code.
+ * @r2: Reserved.
+ * @msg_flag: Message flags.
+ * @r3: Reserved.
+ * @adapter_status: Adapter status.
+ * @r4: Reserved.
+ */
+struct leapraid_scan_dev_rep {
+	u8 r1[2];
+	u8 msg_len;
+	u8 func;
+	u8 r2[3];
+	u8 msg_flag;
+	u8 r3[6];
+	__le16 adapter_status;
+	u8 r4[4];
+};
+
+/**
+ * struct leapraid_evt_notify_req - Event notification request
+ *
+ * @r1: Reserved.
+ * @chain_offset: Offset for chained SGE.
+ * @func: Function code.
+ * @r2: Reserved.
+ * @msg_flag: Message flags.
+ * @r3: Reserved.
+ * @evt_masks: Event masks to enable notifications.
+ * @r4: Reserved.
+ */
+struct leapraid_evt_notify_req {
+	u8 r1[2];
+	u8 chain_offset;
+	u8 func;
+	u8 r2[3];
+	u8 msg_flag;
+	u8 r3[12];
+	__le32 evt_masks[4];
+	u8 r4[8];
+};
+
+/**
+ * struct leapraid_evt_notify_rep - Event notification reply
+ *
+ * @evt_data_len: Length of event data.
+ * @msg_len: Length of message.
+ * @func: Function code.
+ * @r1: Reserved.
+ * @r2: Reserved.
+ * @msg_flag: Message flags.
+ * @r3: Reserved.
+ * @adapter_status: Adapter status.
+ * @r4: Reserved.
+ * @evt: Event code.
+ * @r5: Reserved.
+ * @evt_data: Event data array.
+ */
+struct leapraid_evt_notify_rep {
+	__le16 evt_data_len;
+	u8 msg_len;
+	u8 func;
+	u8 r1[2];
+	u8 r2;
+	u8 msg_flag;
+	u8 r3[6];
+	__le16 adapter_status;
+	u8 r4[4];
+	__le16 evt;
+	u8 r5[6];
+	__le32 evt_data[];
+};
+
+/**
+ * struct leapraid_evt_data_sas_dev_status_change - SAS device status change
+ *
+ * @task_tag: Task identifier.
+ * @reason_code: Reason for status change.
+ * @physical_port: Physical port number.
+ * @r1: Reserved.
+ * @dev_hdl: Device handle.
+ * @r2: Reserved.
+ * @sas_address: SAS address of device.
+ * @lun: Logical unit Number.
+ */
+struct leapraid_evt_data_sas_dev_status_change {
+	__le16 task_tag;
+	u8 reason_code;
+	u8 physical_port;
+	u8 r1[2];
+	__le16 dev_hdl;
+	u8 r2[4];
+	__le64 sas_address;
+	u8 lun[8];
+} __packed __aligned(4);
+/**
+ * struct leapraid_evt_data_ir_change - IR (Integrated RAID) change event data
+ *
+ * @r1: Reserved.
+ * @reason_code: Reason for IR change.
+ * @r2: Reserved.
+ * @vol_dev_hdl: Volume device handle.
+ * @phys_disk_dev_hdl: Physical disk device handle.
+ */
+struct leapraid_evt_data_ir_change {
+	u8 r1;
+	u8 reason_code;
+	u8 r2[2];
+	__le16 vol_dev_hdl;
+	__le16 phys_disk_dev_hdl;
+};
+
+/**
+ * struct leapraid_evt_data_sas_disc - SAS discovery event data
+ *
+ * @r1: Reserved.
+ * @reason_code: Reason for discovery event.
+ * @physical_port: Physical port number where event occurred.
+ * @r2: Reserved.
+ */
+struct leapraid_evt_data_sas_disc {
+	u8 r1;
+	u8 reason_code;
+	u8 physical_port;
+	u8 r2[5];
+};
+
+/**
+ * struct leapraid_evt_sas_topo_phy_entry - SAS topology PHY entry
+ *
+ * @attached_dev_hdl: Device handle attached to PHY.
+ * @link_rate: Current link rate.
+ * @phy_status: PHY status flags.
+ */
+struct leapraid_evt_sas_topo_phy_entry {
+	__le16 attached_dev_hdl;
+	u8 link_rate;
+	u8 phy_status;
+};
+
+/**
+ * struct leapraid_evt_data_sas_topo_change_list - SAS topology change list
+ *
+ * @encl_hdl: Enclosure handle.
+ * @exp_dev_hdl: Expander device handle.
+ * @num_phys: Number of PHYs in this entry.
+ * @r1: Reserved.
+ * @entry_num: Number of PHY elements.
+ * @start_phy_num: Start PHY number.
+ * @exp_status: Expander status.
+ * @physical_port: Physical port number.
+ * @phy: Array of SAS PHY entries.
+ */
+struct leapraid_evt_data_sas_topo_change_list {
+	__le16 encl_hdl;
+	__le16 exp_dev_hdl;
+	u8 num_phys;
+	u8 r1[3];
+	u8 entry_num;
+	u8 start_phy_num;
+	u8 exp_status;
+	u8 physical_port;
+	struct leapraid_evt_sas_topo_phy_entry phy[];
+};
+
+/**
+ * struct leapraid_evt_data_sas_enc_dev_status_change -
+ * SAS enclosure device status
+ *
+ * @enc_hdl: Enclosure handle.
+ * @reason_code: Reason code for status change.
+ * @physical_port: Physical port number.
+ * @encl_logical_id: Enclosure logical ID.
+ * @num_slots: Number of slots in enclosure.
+ * @start_slot: First affected slot.
+ * @phy_bits: Bitmap of affected PHYs.
+ */
+struct leapraid_evt_data_sas_enc_dev_status_change {
+	__le16 enc_hdl;
+	u8 reason_code;
+	u8 physical_port;
+	__le64 encl_logical_id;
+	__le16 num_slots;
+	__le16 start_slot;
+	__le32 phy_bits;
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_io_unit_ctrl_req - I/O unit control request
+ *
+ * @op: Operation code.
+ * @r1: Reserved.
+ * @chain_offset: SGE chain offset.
+ * @func: Function code.
+ * @dev_hdl: Device handle.
+ * @adapter_para: Adapter parameter selector.
+ * @msg_flag: Message flags.
+ * @r2: Reserved.
+ * @phy_num: PHY number.
+ * @r3: Reserved.
+ * @adapter_para_value: Value for adapter parameter.
+ * @adapter_para_value2: Optional second parameter value.
+ * @r4: Reserved.
+ */
+struct leapraid_io_unit_ctrl_req {
+	u8 op;
+	u8 r1;
+	u8 chain_offset;
+	u8 func;
+	__le16 dev_hdl;
+	u8 adapter_para;
+	u8 msg_flag;
+	u8 r2[6];
+	u8 phy_num;
+	u8 r3[17];
+	__le32 adapter_para_value;
+	__le32 adapter_para_value2;
+	u8 r4[4];
+};
+
+/**
+ * struct leapraid_io_unit_ctrl_rep - I/O unit control reply
+ *
+ * @op: Operation code echoed.
+ * @r1: Reserved.
+ * @func: Function code.
+ * @dev_hdl: Device handle.
+ * @r2: Reserved.
+ */
+struct leapraid_io_unit_ctrl_rep {
+	u8 op;
+	u8 r1[2];
+	u8 func;
+	__le16 dev_hdl;
+	u8 r2[14];
+};
+
+/**
+ * struct leapraid_raid_act_req - RAID action request
+ *
+ * @act: RAID action code.
+ * @r1: Reserved.
+ * @func: Function code.
+ * @r2: Reserved.
+ * @phys_disk_num: Number of physical disks involved.
+ * @r3: Reserved.
+ * @action_data_sge: SGE describing action-specific data.
+ */
+struct leapraid_raid_act_req {
+	u8 act;
+	u8 r1[2];
+	u8 func;
+	u8 r2[2];
+	u8 phys_disk_num;
+	u8 r3[13];
+	struct leapraid_sge_simple_union action_data_sge;
+};
+
+/**
+ * struct leapraid_raid_act_rep - RAID action reply
+ *
+ * @act: RAID action code echoed.
+ * @r1: Reserved.
+ * @func: Function code.
+ * @vol_dev_hdl: Volume device handle.
+ * @r2: Reserved
+ * @adapter_status: Status returned by adapter.
+ * @r3: Reserved.
+ */
+struct leapraid_raid_act_rep {
+	u8 act;
+	u8 r1[2];
+	u8 func;
+	__le16 vol_dev_hdl;
+	u8 r2[8];
+	__le16 adapter_status;
+	u8 r3[76];
+};
+
+/**
+ * struct leapraid_smp_passthrough_req - SMP passthrough request
+ *
+ * @passthrough_flg: Passthrough flags.
+ * @physical_port: Target PHY port.
+ * @r1: Reserved.
+ * @func: Function code.
+ * @req_data_len: Request data length.
+ * @r2: Reserved.
+ * @sas_address: SAS address of target device.
+ * @r3: Reserved.
+ * @sgl: Scatter-gather list describing request buffer.
+ */
+struct leapraid_smp_passthrough_req {
+	u8 passthrough_flg;
+	u8 physical_port;
+	u8 r1;
+	u8 func;
+	__le16 req_data_len;
+	u8 r2[10];
+	__le64 sas_address;
+	u8 r3[8];
+	union leapraid_simple_sge_union sgl;
+} __packed __aligned(4);
+
+/**
+ * struct leapraid_smp_passthrough_rep - SMP passthrough reply
+ *
+ * @passthrough_flg: Passthrough flags echoed.
+ * @physical_port: Target PHY port.
+ * @r1: Reserved.
+ * @func: Function code.
+ * @resp_data_len: Length of response data.
+ * @r2: Reserved.
+ * @adapter_status: Adapter status.
+ * @r3: Reserved.
+ */
+struct leapraid_smp_passthrough_rep {
+	u8 passthrough_flg;
+	u8 physical_port;
+	u8 r1;
+	u8 func;
+	__le16 resp_data_len;
+	u8 r2[8];
+	__le16 adapter_status;
+	u8 r3[12];
+};
+
+/**
+ * struct leapraid_sas_io_unit_ctrl_req - SAS I/O unit control request
+ *
+ * @op: Operation code.
+ * @r1: Reserved.
+ * @func: Function code.
+ * @dev_hdl: Device handle.
+ * @r2: Reserved.
+ */
+struct leapraid_sas_io_unit_ctrl_req {
+	u8 op;
+	u8 r1[2];
+	u8 func;
+	__le16 dev_hdl;
+	u8 r2[38];
+};
+
+#endif /* LEAPRAID_H */
diff --git a/drivers/scsi/leapraid/leapraid_app.c b/drivers/scsi/leapraid/leapraid_app.c
new file mode 100644
index 000000000000..742f19c07fcb
--- /dev/null
+++ b/drivers/scsi/leapraid/leapraid_app.c
@@ -0,0 +1,803 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 LeapIO Tech Inc.
+ *
+ * LeapRAID storage and RAID controller driver.
+ */
+#include <linux/compat.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+
+#include "leapraid_func.h"
+
+/* IOCTL device file name. */
+#define LEAPRAID_DEV_NAME       "leapraid_ctl"
+
+/* IOCTL version. */
+#define LEAPRAID_IOCTL_VERSION  0x07
+
+/* IOCTL commands. */
+#define LEAPRAID_ADAPTER_INFO   17
+#define LEAPRAID_COMMAND        20
+#define LEAPRAID_EVENTQUERY     21
+#define LEAPRAID_EVENTREPORT    23
+
+/**
+ * struct leapraid_ioctl_header - IOCTL command header
+ *
+ * @adapter_id : Adapter identifier.
+ * @port_number: Port identifier.
+ * @max_data_size: Maximum data size for transfer.
+ */
+struct leapraid_ioctl_header {
+	u32 adapter_id;
+	u32 port_number;
+	u32 max_data_size;
+};
+
+/**
+ * struct leapraid_ioctl_diag_reset - Diagnostic reset request
+ *
+ * @hdr: Common IOCTL header.
+ */
+struct leapraid_ioctl_diag_reset {
+	struct leapraid_ioctl_header hdr;
+};
+
+/**
+ * struct leapraid_ioctl_pci_info - PCI device information
+ *
+ * @u: Union holding PCI bus/device/function information.
+ * @u.bits.dev: PCI device number.
+ * @u.bits.func: PCI function number.
+ * @u.bits.bus: PCI bus number.
+ * @u.word: Combined representation of PCI BDF.
+ * @seg_id: PCI segment identifier.
+ */
+struct leapraid_ioctl_pci_info {
+	union {
+		struct {
+			u32 dev:5;
+			u32 func:3;
+			u32 bus:24;
+		} bits;
+		u32 word;
+	} u;
+	u32 seg_id;
+};
+
+/**
+ * struct leapraid_ioctl_adapter_info - Adapter information for IOCTL
+ *
+ * @hdr: IOCTL header.
+ * @adapter_type: Adapter type identifier.
+ * @port_number: Port number.
+ * @pci_id: PCI device ID.
+ * @revision: Revision number.
+ * @sub_dev: Subsystem device ID.
+ * @sub_vendor: Subsystem vendor ID.
+ * @r0: Reserved.
+ * @fw_ver: Firmware version.
+ * @bios_ver: BIOS version.
+ * @driver_ver: Driver version.
+ * @r1: Reserved.
+ * @scsi_id: SCSI ID.
+ * @r2: Reserved.
+ * @pci_info: PCI information structure.
+ */
+struct leapraid_ioctl_adapter_info {
+	struct leapraid_ioctl_header hdr;
+	u32 adapter_type;
+	u32 port_number;
+	u32 pci_id;
+	u32 revision;
+	u32 sub_dev;
+	u32 sub_vendor;
+	u32 r0;
+	u32 fw_ver;
+	u32 bios_ver;
+	u8 driver_ver[32];
+	u8 r1;
+	u8 scsi_id;
+	u16 r2;
+	struct leapraid_ioctl_pci_info pci_info;
+};
+
+/**
+ * struct leapraid_ioctl_command - IOCTL command structure
+ *
+ * @hdr: IOCTL header.
+ * @timeout: Command timeout.
+ * @rep_msg_buf_ptr: User pointer to reply message buffer.
+ * @c2h_buf_ptr: User pointer to card-to-host data buffer.
+ * @h2c_buf_ptr: User pointer to host-to-card data buffer.
+ * @sense_data_ptr: User pointer to sense data buffer.
+ * @max_rep_bytes: Maximum reply bytes.
+ * @c2h_size: Card-to-host data size.
+ * @h2c_size: Host-to-card data size.
+ * @max_sense_bytes: Maximum sense data bytes.
+ * @data_sge_offset: Data SGE offset.
+ * @mf: Message frame data (flexible array).
+ */
+struct leapraid_ioctl_command {
+	struct leapraid_ioctl_header hdr;
+	u32 timeout;
+	void __user *rep_msg_buf_ptr;
+	void __user *c2h_buf_ptr;
+	void __user *h2c_buf_ptr;
+	void __user *sense_data_ptr;
+	u32 max_rep_bytes;
+	u32 c2h_size;
+	u32 h2c_size;
+	u32 max_sense_bytes;
+	u32 data_sge_offset;
+	u8 mf[];
+};
+
+static int leapraid_ctl_validate_sge_offset(struct leapraid_adapter *adapter,
+					    const struct leapraid_req *req,
+					    u32 offset_bytes,
+					    size_t h2c_size,
+					    size_t c2h_size)
+{
+	size_t sge_bytes;
+
+	switch (req->func) {
+	case LEAPRAID_FUNC_SCSIIO:
+	case LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH:
+	case LEAPRAID_FUNC_SMP_PASSTHROUGH:
+	case LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH:
+	case LEAPRAID_FUNC_FW_DOWNLOAD:
+	case LEAPRAID_FUNC_FW_UPLOAD:
+		sge_bytes = LEAPRAID_IEEE_SGE64_ENTRY_SIZE;
+		break;
+	default:
+		sge_bytes = adapter->adapter_attr.use_32_dma_mask ?
+			sizeof(struct leapraid_sge_simple32) :
+			sizeof(struct leapraid_sge_simple64);
+		break;
+	}
+
+	if (h2c_size && c2h_size)
+		sge_bytes *= 2;
+
+	if (offset_bytes > LEAPRAID_REQUEST_SIZE - sge_bytes) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Invalid offset_bytes=%u for func=0x%x\n",
+			__func__, offset_bytes, req->func);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct leapraid_adapter *leapraid_ctl_lookup_adapter(int adapter_id)
+{
+	struct leapraid_adapter *adapter;
+	struct Scsi_Host *shost;
+
+	spin_lock(&leapraid_adapter_lock);
+	list_for_each_entry(adapter, &leapraid_adapter_list, list) {
+		if (adapter->adapter_attr.id == adapter_id) {
+			if (READ_ONCE(adapter->access_ctrl.host_removing))
+				break;
+			shost = adapter->shost;
+			if (!shost || !scsi_host_get(shost))
+				break;
+			spin_unlock(&leapraid_adapter_lock);
+			return adapter;
+		}
+	}
+	spin_unlock(&leapraid_adapter_lock);
+
+	return NULL;
+}
+
+static void leapraid_ctl_put_adapter(struct leapraid_adapter *adapter)
+{
+	if (adapter && adapter->shost)
+		scsi_host_put(adapter->shost);
+}
+
+static void leapraid_ctl_scsiio_cmd(struct leapraid_adapter *adapter,
+				    void *ctl_sp_mpi_req,
+				    u16 taskid,
+				    dma_addr_t h2c_dma_addr, size_t h2c_size,
+				    dma_addr_t c2h_dma_addr, size_t c2h_size,
+				    u16 dev_hdl, void *psge)
+{
+	struct leapraid_mpi_scsiio_req *scsiio_request = ctl_sp_mpi_req;
+
+	scsiio_request->sense_buffer_len = SCSI_SENSE_BUFFERSIZE;
+	scsiio_request->sense_buffer_low_add =
+		leapraid_get_sense_buffer_dma(adapter, taskid);
+	memset(&adapter->driver_cmds.ctl_cmd.sense,
+	       0, SCSI_SENSE_BUFFERSIZE);
+	leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr,
+			       h2c_size, c2h_dma_addr, c2h_size);
+	if (scsiio_request->func == LEAPRAID_FUNC_SCSIIO)
+		leapraid_fire_scsi_io(adapter, taskid, dev_hdl);
+	else
+		leapraid_fire_task(adapter, taskid);
+}
+
+static int leapraid_ctl_smp_passthrough_cmd(struct leapraid_adapter *adapter,
+					    void *ctl_sp_req,
+					    u16 taskid,
+					    dma_addr_t h2c_dma_addr,
+					    size_t h2c_size,
+					    dma_addr_t c2h_dma_addr,
+					    size_t c2h_size,
+					    void *psge, void *h2c)
+{
+	struct leapraid_smp_passthrough_req *smp_pt_req = ctl_sp_req;
+	u8 *data;
+
+	if (!adapter->adapter_attr.enable_mp)
+		smp_pt_req->physical_port = LEAPRAID_DISABLE_MP_PORT_ID;
+	if (smp_pt_req->passthrough_flg & LEAPRAID_SMP_PT_FLAG_SGL_PTR)
+		data = (u8 *)&smp_pt_req->sgl;
+	else
+		data = h2c;
+
+	if (!(smp_pt_req->passthrough_flg & LEAPRAID_SMP_PT_FLAG_SGL_PTR) &&
+	    h2c_size <= 10) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Invalid request size=%zu\n",
+			__func__, h2c_size);
+		return -EINVAL;
+	}
+
+	if (data[1] == SMP_PHY_CONTROL &&
+	    (data[10] == SMP_PHY_CONTROL_LINK_RESET ||
+	     data[10] == SMP_PHY_CONTROL_HARD_RESET))
+		adapter->reset_desc.adapter_link_resetting = 1;
+
+	leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr,
+			       h2c_size, c2h_dma_addr, c2h_size);
+	leapraid_fire_task(adapter, taskid);
+	return 0;
+}
+
+static void leapraid_ctl_sas_io_unit_ctrl_cmd(struct leapraid_adapter *adapter,
+					      void *ctl_sp_req,
+					      dma_addr_t h2c_dma_addr,
+					      size_t h2c_size,
+					      dma_addr_t c2h_dma_addr,
+					      size_t c2h_size,
+					      void *psge, u16 taskid)
+{
+	struct leapraid_sas_io_unit_ctrl_req *sas_io_unit_ctl_req = ctl_sp_req;
+
+	if (sas_io_unit_ctl_req->op == LEAPRAID_SAS_OP_PHY_HARD_RESET ||
+	    sas_io_unit_ctl_req->op == LEAPRAID_SAS_OP_PHY_LINK_RESET)
+		adapter->reset_desc.adapter_link_resetting = 1;
+	leapraid_build_mpi_sg(adapter, psge, h2c_dma_addr,
+			      h2c_size, c2h_dma_addr, c2h_size);
+	leapraid_fire_task(adapter, taskid);
+}
+
+static int leapraid_ctl_do_command(struct leapraid_adapter *adapter,
+				   struct leapraid_ioctl_command *karg,
+				   void __user *mf)
+{
+	struct leapraid_req *leap_mpi_req;
+	struct leapraid_req *ctl_sp_mpi_req;
+	u16 taskid;
+	void *h2c = NULL;
+	size_t h2c_size = 0;
+	dma_addr_t h2c_dma_addr = 0;
+	void *c2h = NULL;
+	size_t c2h_size = 0;
+	dma_addr_t c2h_dma_addr = 0;
+	void *psge;
+	unsigned long timeout;
+	u16 dev_hdl = LEAPRAID_INVALID_DEV_HANDLE;
+	bool issue_reset = false;
+	u32 data_sge_offset_bytes;
+	u32 sz;
+	int rc;
+
+	rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT,
+					  __func__);
+	if (rc)
+		return rc;
+
+	leap_mpi_req = kzalloc(LEAPRAID_REQUEST_SIZE, GFP_KERNEL);
+	if (!leap_mpi_req)
+		return -ENOMEM;
+
+	if (karg->data_sge_offset > (UINT_MAX / LEAPRAID_SGE_OFFSET_SIZE)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Invalid data_sge_offset=%u\n",
+			__func__, karg->data_sge_offset);
+		rc = -EINVAL;
+		goto out_cleanup;
+	}
+
+	data_sge_offset_bytes = karg->data_sge_offset *
+				LEAPRAID_SGE_OFFSET_SIZE;
+	if (data_sge_offset_bytes > LEAPRAID_REQUEST_SIZE) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Invalid data_sge_offset=%u\n",
+			__func__, karg->data_sge_offset);
+		rc = -EINVAL;
+		goto out_cleanup;
+	}
+
+	if (copy_from_user(leap_mpi_req, mf, data_sge_offset_bytes)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Failed to copy request message from user\n",
+			__func__);
+		rc = -EFAULT;
+		goto out_cleanup;
+	}
+
+	h2c_size = karg->h2c_size;
+	c2h_size = karg->c2h_size;
+	rc = leapraid_ctl_validate_sge_offset(adapter, leap_mpi_req,
+					      data_sge_offset_bytes,
+					      h2c_size, c2h_size);
+	if (rc)
+		goto out_cleanup;
+
+	taskid = adapter->driver_cmds.ctl_cmd.taskid;
+
+	adapter->driver_cmds.ctl_cmd.status = LEAPRAID_CMD_PENDING;
+	memset(&adapter->driver_cmds.ctl_cmd.reply, 0, LEAPRAID_REPLY_SIZE);
+	ctl_sp_mpi_req = leapraid_get_task_desc(adapter, taskid);
+	memset(ctl_sp_mpi_req, 0, LEAPRAID_REQUEST_SIZE);
+	memcpy(ctl_sp_mpi_req, leap_mpi_req, data_sge_offset_bytes);
+
+	if (ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO ||
+	    ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH ||
+	    ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH) {
+		dev_hdl = le16_to_cpu(ctl_sp_mpi_req->func_dep1);
+		if (!dev_hdl ||
+		    dev_hdl > adapter->adapter_attr.features.max_dev_handle) {
+			dev_err(&adapter->pdev->dev,
+				"%s: Invalid device handle\n", __func__);
+			rc = -EINVAL;
+			goto out_cleanup;
+		}
+	}
+
+	if (WARN_ON(ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSI_TMF)) {
+		rc = -EINVAL;
+		goto out_cleanup;
+	}
+
+	if (h2c_size) {
+		h2c = dma_alloc_coherent(&adapter->pdev->dev, h2c_size,
+					 &h2c_dma_addr, GFP_KERNEL);
+		if (!h2c) {
+			rc = -ENOMEM;
+			goto out_cleanup;
+		}
+		if (copy_from_user(h2c, karg->h2c_buf_ptr, h2c_size)) {
+			dev_err(&adapter->pdev->dev,
+				"%s: Failed to copy h2c from user\n",
+				__func__);
+			rc = -EFAULT;
+			goto out_cleanup;
+		}
+	}
+	if (c2h_size) {
+		c2h = dma_alloc_coherent(&adapter->pdev->dev, c2h_size,
+					 &c2h_dma_addr, GFP_KERNEL);
+		if (!c2h) {
+			rc = -ENOMEM;
+			goto out_cleanup;
+		}
+	}
+
+	psge = (void *)ctl_sp_mpi_req + data_sge_offset_bytes;
+	init_completion(&adapter->driver_cmds.ctl_cmd.done);
+
+	switch (ctl_sp_mpi_req->func) {
+	case LEAPRAID_FUNC_SCSIIO:
+	case LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH:
+		leapraid_ctl_scsiio_cmd(adapter, ctl_sp_mpi_req, taskid,
+					h2c_dma_addr, h2c_size,
+					c2h_dma_addr, c2h_size,
+					dev_hdl, psge);
+		break;
+	case LEAPRAID_FUNC_SMP_PASSTHROUGH:
+		if (!h2c) {
+			rc = -EINVAL;
+			goto out_cleanup;
+		}
+		rc = leapraid_ctl_smp_passthrough_cmd(adapter,
+						      ctl_sp_mpi_req, taskid,
+						      h2c_dma_addr, h2c_size,
+						      c2h_dma_addr, c2h_size,
+						      psge, h2c);
+		if (rc)
+			goto out_cleanup;
+		break;
+	case LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH:
+		leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, h2c_size,
+				       c2h_dma_addr, c2h_size);
+		leapraid_fire_task(adapter, taskid);
+		break;
+	case LEAPRAID_FUNC_FW_DOWNLOAD:
+	case LEAPRAID_FUNC_FW_UPLOAD:
+		leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, h2c_size,
+				       c2h_dma_addr, c2h_size);
+		leapraid_fire_task(adapter, taskid);
+		break;
+	case LEAPRAID_FUNC_SAS_IO_UNIT_CTRL:
+		leapraid_ctl_sas_io_unit_ctrl_cmd(adapter, ctl_sp_mpi_req,
+						  h2c_dma_addr, h2c_size,
+						  c2h_dma_addr, c2h_size,
+						  psge, taskid);
+		break;
+	default:
+		leapraid_build_mpi_sg(adapter, psge, h2c_dma_addr,
+				      h2c_size, c2h_dma_addr, c2h_size);
+		leapraid_fire_task(adapter, taskid);
+		break;
+	}
+
+	timeout = karg->timeout;
+	if (timeout < LEAPRAID_CTL_CMD_TIMEOUT)
+		timeout = LEAPRAID_CTL_CMD_TIMEOUT;
+	if (!wait_for_completion_timeout(&adapter->driver_cmds.ctl_cmd.done,
+					 timeout * HZ)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: ctl_cmd timeout, status=0x%x\n",
+			__func__, adapter->driver_cmds.ctl_cmd.status);
+		leapraid_log_req_context(adapter, taskid, ctl_sp_mpi_req);
+	}
+
+	if ((leap_mpi_req->func == LEAPRAID_FUNC_SMP_PASSTHROUGH ||
+	     leap_mpi_req->func == LEAPRAID_FUNC_SAS_IO_UNIT_CTRL) &&
+	    adapter->reset_desc.adapter_link_resetting)
+		adapter->reset_desc.adapter_link_resetting = 0;
+
+	if (!(adapter->driver_cmds.ctl_cmd.status & LEAPRAID_CMD_DONE)) {
+		issue_reset =
+			leapraid_check_reset(
+				adapter->driver_cmds.ctl_cmd.status);
+		goto reset;
+	}
+
+	if (c2h_size && copy_to_user(karg->c2h_buf_ptr, c2h, c2h_size)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Failed to copy c2h to user\n", __func__);
+		rc = -ENODATA;
+		goto out_cleanup;
+	}
+	if (karg->max_rep_bytes) {
+		sz = min_t(u32, karg->max_rep_bytes, LEAPRAID_REPLY_SIZE);
+		if (copy_to_user(karg->rep_msg_buf_ptr,
+				 &adapter->driver_cmds.ctl_cmd.reply,
+				 sz)) {
+			dev_err(&adapter->pdev->dev,
+				"%s: Failed to copy reply to user\n",
+				__func__);
+			rc = -ENODATA;
+			goto out_cleanup;
+		}
+	}
+
+	if (karg->max_sense_bytes &&
+	    (leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO ||
+	     leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH)) {
+		if (!karg->sense_data_ptr)
+			goto out_cleanup;
+
+		sz = min_t(u32, karg->max_sense_bytes, SCSI_SENSE_BUFFERSIZE);
+		if (copy_to_user(karg->sense_data_ptr,
+				 &adapter->driver_cmds.ctl_cmd.sense,
+				 sz)) {
+			dev_err(&adapter->pdev->dev,
+				"%s: Failed to copy sense data to user\n",
+				__func__);
+			rc = -ENODATA;
+			goto out_cleanup;
+		}
+	}
+reset:
+	if (issue_reset) {
+		rc = -ENODATA;
+		if (leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO ||
+		    leap_mpi_req->func ==
+		    LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH ||
+		    leap_mpi_req->func ==
+		    LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH) {
+			dev_err(&adapter->pdev->dev,
+				"Fire tgt reset, hdl=0x%04x\n",
+				le16_to_cpu(leap_mpi_req->func_dep1));
+			leapraid_issue_locked_tm(
+				adapter,
+				le16_to_cpu(leap_mpi_req->func_dep1), 0, 0, 0,
+				LEAPRAID_TM_TASKTYPE_TARGET_RESET, taskid,
+				LEAPRAID_TM_MSGFLAGS_LINK_RESET);
+		} else {
+			dev_info(&adapter->pdev->dev,
+				 "%s:%d: call hard_reset\n",
+				 __func__, __LINE__);
+			leapraid_hard_reset_handler(adapter, FULL_RESET);
+		}
+	}
+out_cleanup:
+	if (c2h)
+		dma_free_coherent(&adapter->pdev->dev, c2h_size,
+				  c2h, c2h_dma_addr);
+	if (h2c)
+		dma_free_coherent(&adapter->pdev->dev, h2c_size,
+				  h2c, h2c_dma_addr);
+	kfree(leap_mpi_req);
+	adapter->driver_cmds.ctl_cmd.status = LEAPRAID_CMD_NOT_USED;
+	return rc;
+}
+
+static int leapraid_ctl_get_adapter_info(struct leapraid_adapter *adapter,
+					 void __user *arg)
+{
+	struct leapraid_ioctl_adapter_info *karg;
+	u8 revision;
+	int rc = 0;
+
+	karg = kzalloc_obj(*karg);
+	if (!karg)
+		return -ENOMEM;
+
+	pci_read_config_byte(adapter->pdev, PCI_CLASS_REVISION, &revision);
+	karg->revision = revision;
+	karg->pci_id = adapter->pdev->device;
+	karg->sub_dev = adapter->pdev->subsystem_device;
+	karg->sub_vendor = adapter->pdev->subsystem_vendor;
+	karg->pci_info.u.bits.bus = adapter->pdev->bus->number;
+	karg->pci_info.u.bits.dev = PCI_SLOT(adapter->pdev->devfn);
+	karg->pci_info.u.bits.func = PCI_FUNC(adapter->pdev->devfn);
+	karg->pci_info.seg_id = pci_domain_nr(adapter->pdev->bus);
+	karg->fw_ver = adapter->adapter_attr.features.fw_version;
+
+	snprintf(karg->driver_ver, sizeof(karg->driver_ver), "%s-%s",
+		 LEAPRAID_DRIVER_NAME, LEAPRAID_DRIVER_VERSION);
+
+	karg->adapter_type = LEAPRAID_IOCTL_VERSION;
+	karg->bios_ver = adapter->adapter_attr.bios_version;
+	if (copy_to_user(arg, karg,
+			 sizeof(struct leapraid_ioctl_adapter_info))) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Failed to copy info to user\n", __func__);
+		rc = -EFAULT;
+		goto free_buf;
+	}
+
+free_buf:
+	kfree(karg);
+	return rc;
+}
+
+static int leapraid_ctl_ioctl_main(struct file *file, unsigned int cmd,
+				   void __user *arg)
+{
+	struct leapraid_ioctl_header ioctl_header;
+	struct leapraid_adapter *adapter = NULL;
+	struct leapraid_ioctl_command __user *uarg;
+	struct leapraid_ioctl_command karg;
+	int rc = -ENOIOCTLCMD;
+
+	if (copy_from_user(&ioctl_header, arg,
+			   sizeof(struct leapraid_ioctl_header))) {
+		pr_err("%s:%s: Failed to copy header from user\n",
+		       LEAPRAID_DRIVER_NAME, __func__);
+		return -EFAULT;
+	}
+
+	adapter = leapraid_ctl_lookup_adapter(ioctl_header.adapter_id);
+	if (!adapter)
+		return -EFAULT;
+
+	if (atomic_read(&adapter->overheat_desc.thermal_alert)) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Failed, thermal_alert=%d\n",
+			 __func__,
+			 atomic_read(&adapter->overheat_desc.thermal_alert));
+		rc = -EFAULT;
+		goto out_put;
+	}
+
+	mutex_lock(&adapter->access_ctrl.pci_access_lock);
+
+	rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_LONG,
+					  __func__);
+	if (rc)
+		goto unlock;
+
+	if (!wait_event_timeout(adapter->access_ctrl.shost_recover_wq,
+				!adapter->access_ctrl.shost_recover_async,
+				LEAPRAID_WAIT_SHOST_RECOVERY * HZ)) {
+		dev_warn(&adapter->pdev->dev,
+			 "Timeout waiting for shost recovery async\n");
+		rc = -EAGAIN;
+		goto unlock;
+	}
+
+	if (adapter->access_ctrl.pcie_recovering ||
+	    adapter->scan_dev_desc.driver_loading ||
+	    adapter->access_ctrl.host_removing) {
+		rc = -EAGAIN;
+		goto unlock;
+	}
+
+	if (file->f_flags & O_NONBLOCK) {
+		if (!mutex_trylock(&adapter->driver_cmds.ctl_cmd.mutex)) {
+			rc = -EAGAIN;
+			goto unlock;
+		}
+	} else if (mutex_lock_interruptible(&adapter->driver_cmds
+					    .ctl_cmd.mutex)) {
+		rc = -ERESTARTSYS;
+		goto unlock;
+	}
+
+	switch (_IOC_NR(cmd)) {
+	case LEAPRAID_ADAPTER_INFO:
+		if (_IOC_SIZE(cmd) ==
+		    sizeof(struct leapraid_ioctl_adapter_info))
+			rc = leapraid_ctl_get_adapter_info(adapter, arg);
+		break;
+	case LEAPRAID_COMMAND:
+		if (copy_from_user(&karg, arg, sizeof(karg))) {
+			dev_err(&adapter->pdev->dev,
+				"%s: Failed to copy data from user\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+
+		if (karg.hdr.adapter_id != ioctl_header.adapter_id) {
+			rc = -EINVAL;
+			break;
+		}
+
+		if (_IOC_SIZE(cmd) == sizeof(struct leapraid_ioctl_command)) {
+			uarg = arg;
+			rc = leapraid_ctl_do_command(adapter, &karg,
+						     &uarg->mf);
+			if (rc)
+				dev_warn(&adapter->pdev->dev,
+					 "%s: IOCTL cmd failed rc=%d\n",
+					 __func__, rc);
+		}
+		break;
+	case LEAPRAID_EVENTQUERY:
+	case LEAPRAID_EVENTREPORT:
+		rc = 0;
+		break;
+	default:
+		dev_err(&adapter->pdev->dev,
+			"Unknown IOCTL opcode=0x%08x\n", cmd);
+		break;
+	}
+	mutex_unlock(&adapter->driver_cmds.ctl_cmd.mutex);
+
+unlock:
+	mutex_unlock(&adapter->access_ctrl.pci_access_lock);
+out_put:
+	leapraid_ctl_put_adapter(adapter);
+	return rc;
+}
+
+static long leapraid_ctl_ioctl(struct file *file, unsigned int cmd,
+			       unsigned long arg)
+{
+	return leapraid_ctl_ioctl_main(file, cmd, (void __user *)arg);
+}
+
+static void leapraid_fw_mmap_open(struct vm_area_struct *vma)
+{
+	struct leapraid_adapter *adapter = vma->vm_private_data;
+
+	if (!adapter)
+		return;
+
+	atomic_inc(&adapter->fw_log_desc.mmap_refcnt);
+}
+
+static void leapraid_fw_mmap_close(struct vm_area_struct *vma)
+{
+	struct leapraid_adapter *adapter = vma->vm_private_data;
+
+	if (!adapter)
+		return;
+
+	if (atomic_dec_and_test(&adapter->fw_log_desc.mmap_refcnt))
+		wake_up(&adapter->fw_log_desc.mmap_waitq);
+	leapraid_ctl_put_adapter(adapter);
+}
+
+static const struct vm_operations_struct leapraid_fw_mmap_vm_ops = {
+	.open = leapraid_fw_mmap_open,
+	.close = leapraid_fw_mmap_close,
+};
+
+static int leapraid_fw_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct leapraid_adapter *adapter = NULL;
+	/* Userspace passes the adapter ID via vma->vm_pgoff. */
+	u32 adapter_id = vma->vm_pgoff;
+	unsigned long length;
+	int rc = -EINVAL;
+
+	length = vma->vm_end - vma->vm_start;
+
+	adapter = leapraid_ctl_lookup_adapter(adapter_id);
+	if (!adapter) {
+		pr_err("%s: No adapter found!\n", __func__);
+		return -EINVAL;
+	}
+
+	if (READ_ONCE(adapter->access_ctrl.host_removing)) {
+		rc = -EAGAIN;
+		goto out_put;
+	}
+
+	if (length > (LEAPRAID_SYS_LOG_BUF_SIZE +
+		      LEAPRAID_SYS_LOG_BUF_RESERVE)) {
+		dev_err(&adapter->pdev->dev,
+			"Requested mapping size is too large!\n");
+		rc = -EINVAL;
+		goto out_put;
+	}
+
+	if (!adapter->fw_log_desc.fw_log_buffer) {
+		dev_err(&adapter->pdev->dev, "No log buffer!\n");
+		rc = -EINVAL;
+		goto out_put;
+	}
+
+	vma->vm_pgoff = 0;
+
+	rc = dma_mmap_coherent(&adapter->pdev->dev, vma,
+			       adapter->fw_log_desc.fw_log_buffer,
+			       adapter->fw_log_desc.fw_log_buffer_dma,
+			       length);
+	if (rc) {
+		dev_err(&adapter->pdev->dev,
+			"Failed to map memory to user space!\n");
+		goto out_put;
+	}
+
+	vma->vm_private_data = adapter;
+	vma->vm_ops = &leapraid_fw_mmap_vm_ops;
+	leapraid_fw_mmap_open(vma);
+	adapter = NULL;
+
+	rc = 0;
+out_put:
+	leapraid_ctl_put_adapter(adapter);
+	return rc;
+}
+
+static const struct file_operations leapraid_ctl_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = leapraid_ctl_ioctl,
+	.mmap = leapraid_fw_mmap,
+};
+
+static struct miscdevice leapraid_ctl_dev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = LEAPRAID_DEV_NAME,
+	.fops = &leapraid_ctl_fops,
+};
+
+int leapraid_ctl_init(void)
+{
+	if (misc_register(&leapraid_ctl_dev) < 0) {
+		pr_err("%s Can't register misc device\n",
+		       LEAPRAID_DRIVER_NAME);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+void leapraid_ctl_exit(void)
+{
+	misc_deregister(&leapraid_ctl_dev);
+}
diff --git a/drivers/scsi/leapraid/leapraid_func.c b/drivers/scsi/leapraid/leapraid_func.c
new file mode 100644
index 000000000000..3e1aeceab994
--- /dev/null
+++ b/drivers/scsi/leapraid/leapraid_func.c
@@ -0,0 +1,8626 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 LeapIO Tech Inc.
+ *
+ * LeapRAID storage and RAID controller driver.
+ */
+#include <linux/module.h>
+
+#include "leapraid_func.h"
+
+static int poll_queues;
+module_param(poll_queues, int, 0444);
+MODULE_PARM_DESC(poll_queues,
+		 "specifies the number of queues for io_uring poll mode.");
+
+static int max_msix_vectors = LEAPRAID_INVALID_MSIX_VECTORS;
+module_param(max_msix_vectors, int, 0444);
+MODULE_PARM_DESC(max_msix_vectors, "max msix vectors");
+
+static void leapraid_remove_device(struct leapraid_adapter *adapter,
+				   struct leapraid_sas_dev *sas_dev);
+static void leapraid_set_led(struct leapraid_adapter *adapter,
+			     struct leapraid_sas_dev *sas_dev, bool on);
+static void leapraid_ublk_io_dev(struct leapraid_adapter *adapter,
+				 u64 sas_address,
+				 struct leapraid_card_port *port);
+static void leapraid_clear_cached_boot_dev(struct leapraid_adapter *adapter,
+					   void *dev, u32 chnl);
+static int leapraid_make_adapter_available(struct leapraid_adapter *adapter);
+static void leapraid_sync_irqs_for_cleanup(struct leapraid_adapter *adapter);
+static int leapraid_fw_log_init(struct leapraid_adapter *adapter);
+static bool leapraid_should_skip_poll_work(struct leapraid_adapter *adapter);
+static int leapraid_make_adapter_ready(struct leapraid_adapter *adapter,
+				       enum reset_type type);
+
+static noinline bool leapraid_shost_in_recovery(struct Scsi_Host *shost)
+{
+	enum scsi_host_state state;
+
+	state = READ_ONCE(shost->shost_state);
+	return state == SHOST_RECOVERY ||
+	       state == SHOST_CANCEL_RECOVERY ||
+	       state == SHOST_DEL_RECOVERY ||
+	       shost->tmf_in_progress;
+}
+
+static inline bool leapraid_is_end_dev(u32 dev_type)
+{
+	return (dev_type & LEAPRAID_DEVTYP_END_DEV) &&
+		((dev_type & LEAPRAID_DEVTYP_SSP_TGT) ||
+		 (dev_type & LEAPRAID_DEVTYP_STP_TGT) ||
+		 (dev_type & LEAPRAID_DEVTYP_SATA_DEV));
+}
+
+bool leapraid_pci_removed(struct leapraid_adapter *adapter)
+{
+	struct pci_dev *pdev = adapter->pdev;
+	u32 vendor_id;
+
+	if (pci_bus_read_config_dword(pdev->bus,
+				      pdev->devfn,
+				      PCI_VENDOR_ID,
+				      &vendor_id))
+		return true;
+
+	return (vendor_id & LEAPRAID_PCI_VENDOR_ID_MASK) !=
+		LEAPRAID_VENDOR_ID;
+}
+
+static bool leapraid_pci_active(struct leapraid_adapter *adapter)
+{
+	return !(adapter->access_ctrl.pcie_recovering ||
+		leapraid_pci_removed(adapter));
+}
+
+void *leapraid_get_reply_vaddr(struct leapraid_adapter *adapter, u32 rep_paddr)
+{
+	if (!rep_paddr)
+		return NULL;
+
+	return adapter->mem_desc.rep_msg +
+		(rep_paddr - (u32)adapter->mem_desc.rep_msg_dma);
+}
+
+void *leapraid_get_task_desc(struct leapraid_adapter *adapter, u16 taskid)
+{
+	return adapter->mem_desc.task_desc +
+			taskid * LEAPRAID_REQUEST_SIZE;
+}
+
+void *leapraid_get_sense_buffer(struct leapraid_adapter *adapter, u16 taskid)
+{
+	return adapter->mem_desc.sense_data +
+			(taskid - 1) * SCSI_SENSE_BUFFERSIZE;
+}
+
+__le32 leapraid_get_sense_buffer_dma(struct leapraid_adapter *adapter,
+				     u16 taskid)
+{
+	return cpu_to_le32(adapter->mem_desc.sense_data_dma +
+			   ((taskid - 1) * SCSI_SENSE_BUFFERSIZE));
+}
+
+void leapraid_mask_int(struct leapraid_adapter *adapter)
+{
+	u32 reg;
+
+	adapter->mask_int = 1;
+	reg = leapraid_readl(&adapter->iomem_base->host_int_mask);
+	reg |= LEAPRAID_TO_SYS_DB_MASK + LEAPRAID_REPLY_INT_MASK +
+		LEAPRAID_RESET_IRQ_MASK;
+	writel(reg, &adapter->iomem_base->host_int_mask);
+	leapraid_readl(&adapter->iomem_base->host_int_mask);
+}
+
+void leapraid_unmask_int(struct leapraid_adapter *adapter)
+{
+	u32 reg;
+
+	reg = leapraid_readl(&adapter->iomem_base->host_int_mask);
+	reg &= ~LEAPRAID_REPLY_INT_MASK;
+	writel(reg, &adapter->iomem_base->host_int_mask);
+	adapter->mask_int = 0;
+}
+
+static void leapraid_overheat_suspend(struct leapraid_adapter *adapter)
+{
+	struct workqueue_struct *wq;
+	struct Scsi_Host *shost;
+	struct pci_dev *pdev;
+
+	if (!adapter)
+		return;
+
+	pdev = adapter->pdev;
+	shost = pci_get_drvdata(pdev);
+	if (!shost) {
+		dev_warn(&pdev->dev,
+			 "Overheat suspend failed, invalid host or adapter\n");
+		return;
+	}
+
+	wq = adapter->overheat_desc.fault_overheat_wq;
+	if (!wq)
+		return;
+
+	if (atomic_cmpxchg(&adapter->overheat_desc.thermal_alert, 0, 1))
+		return;
+
+	queue_work(wq, &adapter->overheat_desc.fault_overheat_work);
+}
+
+static void leapraid_stop_adapter_on_fault(struct leapraid_adapter *adapter,
+					   u32 db)
+{
+	u32 adapter_state = db & LEAPRAID_DB_MASK;
+	bool fault_1 = false;
+	bool fault_2 = false;
+
+	fault_1 = adapter_state == LEAPRAID_DB_MASK;
+	fault_2 = adapter_state == LEAPRAID_DB_FAULT &&
+		  (db & LEAPRAID_DB_DATA_MASK) == LEAPRAID_DB_OVER_TEMPERATURE;
+
+	if (!fault_1 && !fault_2)
+		return;
+
+	if (fault_1)
+		dev_err(&adapter->pdev->dev,
+			"%s: Doorbell status 0xFFFF!\n", __func__);
+	else
+		dev_err(&adapter->pdev->dev,
+			"%s: Adapter overheating detected!\n", __func__);
+
+	leapraid_overheat_suspend(adapter);
+}
+
+u32 leapraid_get_adapter_state(struct leapraid_adapter *adapter)
+{
+	u32 db;
+	u32 adapter_state;
+
+	db = leapraid_readl(&adapter->iomem_base->db);
+	adapter_state = db & LEAPRAID_DB_MASK;
+	leapraid_stop_adapter_on_fault(adapter, db);
+	return adapter_state;
+}
+
+static bool leapraid_wait_adapter_ready(struct leapraid_adapter *adapter)
+{
+	u32 cur_state;
+	u32 cnt;
+
+	for (cnt = LEAPRAID_ADAPTER_READY_MAX_RETRY; cnt > 0; cnt--) {
+		cur_state = leapraid_get_adapter_state(adapter);
+
+		if (cur_state == LEAPRAID_DB_READY)
+			return true;
+		if (cur_state == LEAPRAID_DB_FAULT)
+			break;
+		usleep_range(LEAPRAID_ADAPTER_READY_SLEEP_MIN_US,
+			     LEAPRAID_ADAPTER_READY_SLEEP_MAX_US);
+	}
+
+	return false;
+}
+
+static int leapraid_db_wait_int_host(struct leapraid_adapter *adapter)
+{
+	u32 cnt;
+
+	for (cnt = LEAPRAID_DB_WAIT_MAX_RETRY; cnt > 0; cnt--) {
+		if (leapraid_readl(&adapter->iomem_base->host_int_status) &
+		    LEAPRAID_ADAPTER2HOST_DB_STATUS)
+			return 0;
+
+		udelay(LEAPRAID_DB_WAIT_DELAY_US);
+	}
+
+	return -EFAULT;
+}
+
+static int leapraid_db_wait_ack_and_clear_int(struct leapraid_adapter *adapter)
+{
+	u32 adapter_state;
+	u32 int_status;
+	u32 cnt;
+
+	for (cnt = LEAPRAID_ADAPTER_READY_MAX_RETRY; cnt > 0; cnt--) {
+		int_status =
+			leapraid_readl(&adapter->iomem_base->host_int_status);
+
+		if (int_status == 0xFFFFFFFF)
+			return -EFAULT;
+
+		if (!(int_status & LEAPRAID_HOST2ADAPTER_DB_STATUS))
+			return 0;
+
+		if (int_status & LEAPRAID_ADAPTER2HOST_DB_STATUS) {
+			adapter_state = leapraid_get_adapter_state(adapter);
+			if (adapter_state == LEAPRAID_DB_FAULT)
+				return -EFAULT;
+		}
+
+		usleep_range(LEAPRAID_ADAPTER_READY_SLEEP_MIN_US,
+			     LEAPRAID_ADAPTER_READY_SLEEP_MAX_US);
+	}
+
+	return -EFAULT;
+}
+
+static int leapraid_handshake_func(struct leapraid_adapter *adapter,
+				   int req_bytes, u32 *req,
+				   int rep_bytes, u16 *rep)
+{
+	int failed, i;
+
+	if (leapraid_readl(&adapter->iomem_base->db) & LEAPRAID_DB_USED) {
+		dev_err(&adapter->pdev->dev, "doorbell used\n");
+		return -EFAULT;
+	}
+
+	if (leapraid_readl(&adapter->iomem_base->host_int_status) &
+	    LEAPRAID_ADAPTER2HOST_DB_STATUS)
+		writel(0, &adapter->iomem_base->host_int_status);
+
+	writel(((LEAPRAID_FUNC_HANDSHAKE << LEAPRAID_DB_FUNC_SHIFT) |
+		((req_bytes / LEAPRAID_DWORDS_BYTE_SIZE) <<
+		LEAPRAID_DB_ADD_DWORDS_SHIFT)),
+	       &adapter->iomem_base->db);
+
+	if (leapraid_db_wait_int_host(adapter)) {
+		dev_err(&adapter->pdev->dev, "%d: Wait db interrupt timeout\n",
+			__LINE__);
+		return -EFAULT;
+	}
+
+	writel(0, &adapter->iomem_base->host_int_status);
+
+	if (leapraid_db_wait_ack_and_clear_int(adapter)) {
+		dev_err(&adapter->pdev->dev, "%d: Wait ack failure\n",
+			__LINE__);
+		return -EFAULT;
+	}
+
+	for (i = 0, failed = 0;
+	     i < req_bytes / LEAPRAID_DWORDS_BYTE_SIZE && !failed;
+	     i++) {
+		writel((u32)req[i], &adapter->iomem_base->db);
+		if (leapraid_db_wait_ack_and_clear_int(adapter))
+			failed = 1;
+	}
+	if (failed) {
+		dev_err(&adapter->pdev->dev, "%d: Wait ack failure\n",
+			__LINE__);
+		return -EFAULT;
+	}
+
+	for (i = 0; i < rep_bytes / LEAPRAID_WORD_BYTE_SIZE; i++) {
+		if (leapraid_db_wait_int_host(adapter)) {
+			dev_err(&adapter->pdev->dev,
+				"%d: Wait db interrupt timeout\n", __LINE__);
+			return -EFAULT;
+		}
+		rep[i] = (u16)(leapraid_readl(&adapter->iomem_base->db)
+			       & LEAPRAID_DB_DATA_MASK);
+		writel(0, &adapter->iomem_base->host_int_status);
+	}
+
+	if (leapraid_db_wait_int_host(adapter)) {
+		dev_err(&adapter->pdev->dev, "%d: Wait db interrupt timeout\n",
+			__LINE__);
+		return -EFAULT;
+	}
+
+	writel(0, &adapter->iomem_base->host_int_status);
+
+	return 0;
+}
+
+int leapraid_check_adapter_is_op(struct leapraid_adapter *adapter, int wait,
+				 const char *caller)
+{
+	int wait_count;
+
+	for (wait_count = wait; wait_count > 0; wait_count--) {
+		if (READ_ONCE(adapter->access_ctrl.host_removing)) {
+			dev_warn(&adapter->pdev->dev,
+				 "%s: Host is removing\n", caller);
+			return -EFAULT;
+		}
+
+		if (leapraid_pci_removed(adapter)) {
+			dev_warn(&adapter->pdev->dev,
+				 "%s: PCI device removed\n", __func__);
+			return -EFAULT;
+		}
+
+		if (leapraid_get_adapter_state(adapter) ==
+		    LEAPRAID_DB_OPERATIONAL)
+			return 0;
+
+		dev_dbg(&adapter->pdev->dev,
+			"%s: Wait for adapter to become op status(cnt=%d)\n",
+			caller, wait - wait_count);
+
+		ssleep(1);
+	}
+
+	dev_err(&adapter->pdev->dev,
+		"%s: Adapter failed to become op state, last state=%d\n",
+		caller, leapraid_get_adapter_state(adapter));
+
+	return -EFAULT;
+}
+
+struct leapraid_io_req_tracker *leapraid_get_io_tracker_from_taskid(
+		struct leapraid_adapter *adapter, u16 taskid)
+{
+	struct scsi_cmnd *scmd;
+
+	if (WARN_ON(!taskid))
+		return NULL;
+
+	if (WARN_ON(taskid > adapter->shost->can_queue))
+		return NULL;
+
+	scmd = leapraid_get_scmd_from_taskid(adapter, taskid);
+	if (scmd)
+		return scsi_cmd_priv(scmd);
+
+	return NULL;
+}
+
+static u8 leapraid_get_cb_idx(struct leapraid_adapter *adapter, u16 taskid)
+{
+	struct leapraid_driver_cmd *sp_cmd;
+	u8 cb_idx = 0xFF;
+
+	if (WARN_ON(!taskid))
+		return cb_idx;
+
+	list_for_each_entry(sp_cmd, &adapter->driver_cmds.special_cmd_list,
+			    list)
+		if (taskid == sp_cmd->taskid ||
+		    taskid == sp_cmd->hp_taskid ||
+		    taskid == sp_cmd->inter_taskid)
+			return sp_cmd->cb_idx;
+
+	WARN_ON(cb_idx == 0xFF);
+	return cb_idx;
+}
+
+struct scsi_cmnd *leapraid_get_scmd_from_taskid(
+					struct leapraid_adapter *adapter,
+					u16 taskid)
+{
+	struct leapraid_scsiio_req *leap_mpi_req;
+	struct leapraid_io_req_tracker *st;
+	struct scsi_cmnd *scmd;
+	u32 uniq_tag;
+
+	if (taskid <= 0 || taskid > adapter->shost->can_queue)
+		return NULL;
+
+	uniq_tag = adapter->mem_desc.taskid_to_uniq_tag[taskid - 1] <<
+		 BLK_MQ_UNIQUE_TAG_BITS | (taskid - 1);
+	leap_mpi_req = leapraid_get_task_desc(adapter, taskid);
+	if (!leap_mpi_req->dev_hdl)
+		return NULL;
+
+	scmd = scsi_host_find_tag(adapter->shost, uniq_tag);
+	if (scmd) {
+		st = scsi_cmd_priv(scmd);
+		if (st && st->taskid == taskid)
+			return scmd;
+	}
+
+	return NULL;
+}
+
+u16 leapraid_alloc_scsiio_taskid(struct leapraid_adapter *adapter,
+				 struct scsi_cmnd *scmd)
+{
+	struct leapraid_io_req_tracker *request;
+	u16 taskid;
+	u32 tag = scsi_cmd_to_rq(scmd)->tag;
+	u32 unique_tag;
+
+	unique_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));
+	tag = blk_mq_unique_tag_to_tag(unique_tag);
+	adapter->mem_desc.taskid_to_uniq_tag[tag] =
+		blk_mq_unique_tag_to_hwq(unique_tag);
+
+	request = scsi_cmd_priv(scmd);
+	taskid = tag + 1;
+	request->taskid = taskid;
+	request->scmd = scmd;
+	return taskid;
+}
+
+static void leapraid_check_pending_io(struct leapraid_adapter *adapter)
+{
+	if (adapter->access_ctrl.shost_recovering &&
+	    adapter->reset_desc.pending_io_cnt) {
+		if (adapter->reset_desc.pending_io_cnt == 1)
+			wake_up(&adapter->reset_desc.reset_wait_queue);
+		adapter->reset_desc.pending_io_cnt--;
+	}
+}
+
+static void leapraid_clear_io_tracker(
+		struct leapraid_adapter *adapter,
+		struct leapraid_io_req_tracker *io_tracker)
+{
+	if (!io_tracker)
+		return;
+
+	if (WARN_ON(io_tracker->taskid == 0))
+		return;
+
+	io_tracker->scmd = NULL;
+}
+
+static bool leapraid_is_fixed_taskid(struct leapraid_adapter *adapter,
+				     u16 taskid)
+{
+	struct leapraid_driver_cmds *driver_cmds = &adapter->driver_cmds;
+
+	return (taskid == driver_cmds->ctl_cmd.taskid ||
+		taskid == driver_cmds->tm_cmd.hp_taskid ||
+		taskid == driver_cmds->ctl_cmd.hp_taskid ||
+		taskid == driver_cmds->scan_dev_cmd.inter_taskid ||
+		taskid == driver_cmds->transport_cmd.inter_taskid ||
+		taskid == driver_cmds->cfg_op_cmd.inter_taskid ||
+		taskid == driver_cmds->enc_cmd.inter_taskid ||
+		taskid == driver_cmds->notify_event_cmd.inter_taskid);
+}
+
+void leapraid_free_taskid(struct leapraid_adapter *adapter, u16 taskid)
+{
+	struct leapraid_io_req_tracker *io_tracker;
+	void *task_desc;
+
+	if (leapraid_is_fixed_taskid(adapter, taskid))
+		return;
+
+	if (taskid <= adapter->shost->can_queue) {
+		io_tracker = leapraid_get_io_tracker_from_taskid(adapter,
+								 taskid);
+		if (!io_tracker) {
+			leapraid_check_pending_io(adapter);
+			return;
+		}
+
+		task_desc = leapraid_get_task_desc(adapter, taskid);
+		memset(task_desc, 0, LEAPRAID_REQUEST_SIZE);
+		leapraid_clear_io_tracker(adapter, io_tracker);
+		leapraid_check_pending_io(adapter);
+		adapter->mem_desc.taskid_to_uniq_tag[taskid - 1] = 0xFFFF;
+	}
+}
+
+static u8 leapraid_get_msix_idx(struct leapraid_adapter *adapter,
+				struct scsi_cmnd *scmd)
+{
+	if (scmd && adapter->shost->nr_hw_queues > 1) {
+		u32 tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd));
+
+		return blk_mq_unique_tag_to_hwq(tag);
+	}
+	return adapter->notification_desc.msix_cpu_map[raw_smp_processor_id()];
+}
+
+static u8 leapraid_get_and_set_msix_idx_from_taskid(
+		struct leapraid_adapter *adapter, u16 taskid)
+{
+	struct leapraid_io_req_tracker *io_tracker = NULL;
+
+	if (taskid <= adapter->shost->can_queue)
+		io_tracker = leapraid_get_io_tracker_from_taskid(adapter,
+								 taskid);
+
+	if (!io_tracker)
+		return leapraid_get_msix_idx(adapter, NULL);
+
+	io_tracker->msix_io = leapraid_get_msix_idx(adapter, io_tracker->scmd);
+
+	return io_tracker->msix_io;
+}
+
+void leapraid_fire_scsi_io(struct leapraid_adapter *adapter, u16 taskid,
+			   u16 handle)
+{
+	struct leapraid_atomic_req_desc desc;
+
+	desc.flg = LEAPRAID_REQ_DESC_FLG_SCSI_IO;
+	desc.msix_idx = leapraid_get_and_set_msix_idx_from_taskid(adapter,
+								  taskid);
+	desc.taskid = cpu_to_le16(taskid);
+	writel((__force u32)cpu_to_le32(*((u32 *)&desc)),
+	       &adapter->iomem_base->atomic_req_desc_post);
+}
+
+void leapraid_fire_hpr_task(struct leapraid_adapter *adapter, u16 taskid,
+			    u16 msix_task)
+{
+	struct leapraid_atomic_req_desc desc;
+
+	desc.flg = LEAPRAID_REQ_DESC_FLG_HPR;
+	desc.msix_idx = msix_task;
+	desc.taskid = cpu_to_le16(taskid);
+	writel((__force u32)cpu_to_le32(*((u32 *)&desc)),
+	       &adapter->iomem_base->atomic_req_desc_post);
+}
+
+void leapraid_fire_task(struct leapraid_adapter *adapter, u16 taskid)
+{
+	struct leapraid_atomic_req_desc desc;
+
+	desc.flg = LEAPRAID_REQ_DESC_FLG_DFLT_TYPE;
+	desc.msix_idx = leapraid_get_and_set_msix_idx_from_taskid(adapter,
+								  taskid);
+	desc.taskid = cpu_to_le16(taskid);
+	writel((__force u32)cpu_to_le32(*((u32 *)&desc)),
+	       &adapter->iomem_base->atomic_req_desc_post);
+}
+
+void leapraid_clean_active_scsi_cmds(struct leapraid_adapter *adapter)
+{
+	struct leapraid_io_req_tracker *io_tracker;
+	struct scsi_cmnd *scmd;
+	void *task_desc;
+	u16 taskid;
+
+	leapraid_sync_irqs_for_cleanup(adapter);
+
+	for (taskid = 1; taskid <= adapter->shost->can_queue; taskid++) {
+		scmd = leapraid_get_scmd_from_taskid(adapter, taskid);
+		if (!scmd)
+			continue;
+
+		io_tracker = scsi_cmd_priv(scmd);
+		if (io_tracker && io_tracker->taskid == 0)
+			continue;
+
+		scsi_dma_unmap(scmd);
+		task_desc = leapraid_get_task_desc(adapter, taskid);
+		memset(task_desc, 0, LEAPRAID_REQUEST_SIZE);
+		leapraid_clear_io_tracker(adapter, io_tracker);
+		if (!leapraid_pci_active(adapter) ||
+		    adapter->reset_desc.adapter_reset_results != 0 ||
+		    atomic_read(&adapter->overheat_desc.thermal_alert) ||
+		    adapter->access_ctrl.host_removing)
+			scmd->result = DID_NO_CONNECT << 16;
+		else
+			scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
+		scsi_done(scmd);
+	}
+}
+
+static void leapraid_clean_active_driver_cmd(struct leapraid_driver_cmd *cmd)
+{
+	if (cmd->status & LEAPRAID_CMD_PENDING) {
+		cmd->status |= LEAPRAID_CMD_RESET;
+		complete(&cmd->done);
+	}
+}
+
+static void leapraid_clean_active_driver_cmds(struct leapraid_adapter *adapter)
+{
+	struct leapraid_driver_cmds *driver_cmds;
+
+	driver_cmds = &adapter->driver_cmds;
+	leapraid_clean_active_driver_cmd(&driver_cmds->tm_cmd);
+	leapraid_clean_active_driver_cmd(&driver_cmds->transport_cmd);
+	leapraid_clean_active_driver_cmd(&driver_cmds->enc_cmd);
+	leapraid_clean_active_driver_cmd(&driver_cmds->notify_event_cmd);
+	leapraid_clean_active_driver_cmd(&driver_cmds->cfg_op_cmd);
+	leapraid_clean_active_driver_cmd(&driver_cmds->ctl_cmd);
+
+	if (driver_cmds->scan_dev_cmd.status & LEAPRAID_CMD_PENDING) {
+		adapter->scan_dev_desc.scan_dev_failed = 1;
+		driver_cmds->scan_dev_cmd.status |= LEAPRAID_CMD_RESET;
+		if (adapter->scan_dev_desc.driver_loading) {
+			adapter->scan_dev_desc.scan_start_failed =
+				LEAPRAID_ADAPTER_STATUS_INTERNAL_ERROR;
+			adapter->scan_dev_desc.scan_start = 0;
+		} else {
+			complete(&driver_cmds->scan_dev_cmd.done);
+		}
+	}
+}
+
+static void leapraid_clean_active_cmds(struct leapraid_adapter *adapter)
+{
+	leapraid_clean_active_driver_cmds(adapter);
+	leapraid_clean_active_fw_evt(adapter);
+	leapraid_clean_active_scsi_cmds(adapter);
+}
+
+static void leapraid_tgt_not_responding(struct leapraid_adapter *adapter,
+					u16 hdl)
+{
+	struct leapraid_starget_priv *starget_priv = NULL;
+	struct leapraid_sas_dev *sas_dev;
+	unsigned long flags = 0;
+	u32 adapter_state;
+
+	if (adapter->access_ctrl.pcie_recovering)
+		return;
+
+	adapter_state = leapraid_get_adapter_state(adapter);
+	if (adapter_state != LEAPRAID_DB_OPERATIONAL)
+		return;
+
+	if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle ||
+	    test_bit(hdl, adapter->dev_topo.pd_hdls))
+		return;
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl);
+	if (sas_dev && sas_dev->starget && sas_dev->starget->hostdata) {
+		starget_priv = sas_dev->starget->hostdata;
+		starget_priv->deleted = 1;
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+
+	if (starget_priv)
+		starget_priv->hdl = LEAPRAID_INVALID_DEV_HANDLE;
+
+	if (sas_dev)
+		leapraid_sdev_put(sas_dev);
+}
+
+static void leapraid_tgt_rst_send(struct leapraid_adapter *adapter, u16 hdl)
+{
+	struct leapraid_starget_priv *starget_priv = NULL;
+	struct leapraid_sas_dev *sas_dev;
+	struct leapraid_card_port *port;
+	u64 sas_address;
+	unsigned long flags;
+	u32 adapter_state;
+
+	if (adapter->access_ctrl.pcie_recovering)
+		return;
+
+	adapter_state = leapraid_get_adapter_state(adapter);
+	if (adapter_state != LEAPRAID_DB_OPERATIONAL)
+		return;
+
+	if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle ||
+	    test_bit(hdl, adapter->dev_topo.pd_hdls))
+		return;
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl);
+	if (sas_dev && sas_dev->starget && sas_dev->starget->hostdata) {
+		starget_priv = sas_dev->starget->hostdata;
+		starget_priv->deleted = 1;
+		sas_address = sas_dev->sas_addr;
+		port = sas_dev->card_port;
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+
+	if (starget_priv) {
+		leapraid_ublk_io_dev(adapter, sas_address, port);
+		starget_priv->hdl = LEAPRAID_INVALID_DEV_HANDLE;
+	}
+	if (sas_dev)
+		leapraid_sdev_put(sas_dev);
+}
+
+static inline void leapraid_single_mpi_sg_append(
+				struct leapraid_adapter *adapter,
+				void *sge,
+				u32 flag_and_len,
+				dma_addr_t dma_addr)
+{
+	if (adapter->adapter_attr.use_32_dma_mask) {
+		struct leapraid_sge_simple32 *sge32 = sge;
+
+		sge32->flg_and_len =
+			cpu_to_le32(flag_and_len |
+				    (LEAPRAID_SGE_FLG_32 |
+				     LEAPRAID_SGE_FLG_SYSTEM_ADDR) <<
+				    LEAPRAID_SGE_FLG_SHIFT);
+		sge32->addr = cpu_to_le32(dma_addr);
+	} else {
+		struct leapraid_sge_simple64 *sge64 = sge;
+
+		sge64->flg_and_len =
+			cpu_to_le32(flag_and_len |
+				    (LEAPRAID_SGE_FLG_64 |
+				     LEAPRAID_SGE_FLG_SYSTEM_ADDR) <<
+				    LEAPRAID_SGE_FLG_SHIFT);
+		sge64->addr = cpu_to_le64(dma_addr);
+	}
+}
+
+static inline void leapraid_single_ieee_sg_append(void *sge, u8 flag,
+						  u8 next_chain_offset,
+						  u32 len,
+						  dma_addr_t dma_addr)
+{
+	struct leapraid_chain64_ieee_sg *ieee_sg = sge;
+
+	ieee_sg->flg = flag;
+	ieee_sg->next_chain_offset = next_chain_offset;
+	ieee_sg->len = cpu_to_le32(len);
+	ieee_sg->addr = cpu_to_le64(dma_addr);
+}
+
+static void leapraid_build_nodata_mpi_sg(struct leapraid_adapter *adapter,
+					 void *sge)
+{
+	leapraid_single_mpi_sg_append(adapter,
+				      sge,
+				      (LEAPRAID_SGE_FLG_LAST_ONE |
+					LEAPRAID_SGE_FLG_EOB |
+					LEAPRAID_SGE_FLG_EOL |
+					LEAPRAID_SGE_FLG_SIMPLE_ONE) <<
+					LEAPRAID_SGE_FLG_SHIFT,
+					LEAPRAID_SGE_NO_DATA_ADDR);
+}
+
+void leapraid_build_mpi_sg(struct leapraid_adapter *adapter, void *sge,
+			   dma_addr_t h2c_dma_addr, size_t h2c_size,
+			   dma_addr_t c2h_dma_addr, size_t c2h_size)
+{
+	if (h2c_size && !c2h_size) {
+		leapraid_single_mpi_sg_append(adapter,
+					      sge,
+					      ((LEAPRAID_SGE_FLG_SIMPLE_ONE |
+						LEAPRAID_SGE_FLG_LAST_ONE |
+						LEAPRAID_SGE_FLG_EOB |
+						LEAPRAID_SGE_FLG_EOL |
+						LEAPRAID_SGE_FLG_H2C) <<
+					       LEAPRAID_SGE_FLG_SHIFT) |
+					       h2c_size,
+					      h2c_dma_addr);
+	} else if (!h2c_size && c2h_size) {
+		leapraid_single_mpi_sg_append(adapter,
+					      sge,
+					      ((LEAPRAID_SGE_FLG_SIMPLE_ONE |
+						LEAPRAID_SGE_FLG_LAST_ONE |
+						LEAPRAID_SGE_FLG_EOB |
+						LEAPRAID_SGE_FLG_EOL) <<
+						LEAPRAID_SGE_FLG_SHIFT) |
+						c2h_size,
+					      c2h_dma_addr);
+	} else if (h2c_size && c2h_size) {
+		leapraid_single_mpi_sg_append(adapter,
+					      sge,
+					      ((LEAPRAID_SGE_FLG_SIMPLE_ONE |
+						LEAPRAID_SGE_FLG_EOB |
+						LEAPRAID_SGE_FLG_H2C) <<
+					       LEAPRAID_SGE_FLG_SHIFT) |
+					      h2c_size,
+					      h2c_dma_addr);
+		if (adapter->adapter_attr.use_32_dma_mask)
+			sge += sizeof(struct leapraid_sge_simple32);
+		else
+			sge += sizeof(struct leapraid_sge_simple64);
+		leapraid_single_mpi_sg_append(adapter,
+					      sge,
+					      ((LEAPRAID_SGE_FLG_SIMPLE_ONE |
+						LEAPRAID_SGE_FLG_LAST_ONE |
+						LEAPRAID_SGE_FLG_EOB |
+						LEAPRAID_SGE_FLG_EOL) <<
+						LEAPRAID_SGE_FLG_SHIFT) |
+						c2h_size,
+					      c2h_dma_addr);
+	} else {
+		leapraid_build_nodata_mpi_sg(adapter, sge);
+	}
+}
+
+void leapraid_build_ieee_nodata_sg(struct leapraid_adapter *adapter, void *sge)
+{
+	leapraid_single_ieee_sg_append(sge,
+				       (LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE |
+					LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR |
+					LEAPRAID_IEEE_SGE_FLG_EOL),
+					0,
+					0,
+					LEAPRAID_SGE_NO_DATA_ADDR);
+}
+
+int leapraid_build_scmd_ieee_sg(struct leapraid_adapter *adapter,
+				struct scsi_cmnd *scmd, u16 taskid)
+{
+	struct leapraid_scsiio_req *scsiio_req;
+	struct leapraid_io_req_tracker *io_tracker;
+	struct scatterlist *scmd_sg_cur;
+	int sg_entries_left;
+	void *sg_entry_cur;
+	void *host_chain;
+	dma_addr_t host_chain_dma;
+	u8 host_chain_cursor;
+	u32 sg_entries_in_cur_seg;
+	u32 chain_offset_in_cur_seg;
+	u32 chain_len_in_cur_seg;
+
+	io_tracker = scsi_cmd_priv(scmd);
+	scsiio_req = leapraid_get_task_desc(adapter, taskid);
+	scmd_sg_cur = scsi_sglist(scmd);
+	sg_entries_left = scsi_dma_map(scmd);
+	if (sg_entries_left < 0)
+		return -ENOMEM;
+	sg_entry_cur = &scsiio_req->sgl;
+	if (sg_entries_left <= LEAPRAID_SGL_INLINE_THRESHOLD)
+		goto fill_last_seg;
+
+	scsiio_req->chain_offset = LEAPRAID_CHAIN_OFFSET_DWORDS;
+	leapraid_single_ieee_sg_append(sg_entry_cur,
+				       LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE |
+				       LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR,
+				       0, sg_dma_len(scmd_sg_cur),
+				       sg_dma_address(scmd_sg_cur));
+	scmd_sg_cur = sg_next(scmd_sg_cur);
+	sg_entry_cur += LEAPRAID_IEEE_SGE64_ENTRY_SIZE;
+	sg_entries_left--;
+
+	host_chain_cursor = 0;
+	host_chain = io_tracker->chain +
+		host_chain_cursor * LEAPRAID_CHAIN_SEG_SIZE;
+	host_chain_dma = io_tracker->chain_dma +
+		host_chain_cursor * LEAPRAID_CHAIN_SEG_SIZE;
+	host_chain_cursor += 1;
+	for (;;) {
+		sg_entries_in_cur_seg =
+			(sg_entries_left <= LEAPRAID_MAX_SGES_IN_CHAIN) ?
+				sg_entries_left : LEAPRAID_MAX_SGES_IN_CHAIN;
+		chain_offset_in_cur_seg =
+			(sg_entries_left == (int)sg_entries_in_cur_seg) ?
+				0 : sg_entries_in_cur_seg;
+		chain_len_in_cur_seg = sg_entries_in_cur_seg *
+			LEAPRAID_IEEE_SGE64_ENTRY_SIZE;
+		if (chain_offset_in_cur_seg)
+			chain_len_in_cur_seg += LEAPRAID_IEEE_SGE64_ENTRY_SIZE;
+
+		leapraid_single_ieee_sg_append(
+			sg_entry_cur,
+			LEAPRAID_IEEE_SGE_FLG_CHAIN_ONE |
+			LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR,
+			chain_offset_in_cur_seg, chain_len_in_cur_seg,
+			host_chain_dma);
+		sg_entry_cur = host_chain;
+		if (!chain_offset_in_cur_seg)
+			goto fill_last_seg;
+
+		while (sg_entries_in_cur_seg) {
+			leapraid_single_ieee_sg_append(
+				sg_entry_cur,
+				LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE |
+				LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR,
+				0, sg_dma_len(scmd_sg_cur),
+				sg_dma_address(scmd_sg_cur));
+			scmd_sg_cur = sg_next(scmd_sg_cur);
+			sg_entry_cur += LEAPRAID_IEEE_SGE64_ENTRY_SIZE;
+			sg_entries_left--;
+			sg_entries_in_cur_seg--;
+		}
+		host_chain = io_tracker->chain +
+			host_chain_cursor * LEAPRAID_CHAIN_SEG_SIZE;
+		host_chain_dma = io_tracker->chain_dma +
+			host_chain_cursor * LEAPRAID_CHAIN_SEG_SIZE;
+		host_chain_cursor += 1;
+	}
+
+fill_last_seg:
+	while (sg_entries_left > 0) {
+		u32 flags = LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE |
+			LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR;
+		if (sg_entries_left == 1)
+			flags |= LEAPRAID_IEEE_SGE_FLG_EOL;
+		leapraid_single_ieee_sg_append(sg_entry_cur, flags,
+					       0, sg_dma_len(scmd_sg_cur),
+					       sg_dma_address(scmd_sg_cur));
+		scmd_sg_cur = sg_next(scmd_sg_cur);
+		sg_entry_cur += LEAPRAID_IEEE_SGE64_ENTRY_SIZE;
+		sg_entries_left--;
+	}
+	return 0;
+}
+
+void leapraid_build_ieee_sg(struct leapraid_adapter *adapter, void *sge,
+			    dma_addr_t h2c_dma_addr, size_t h2c_size,
+			    dma_addr_t c2h_dma_addr, size_t c2h_size)
+{
+	u32 base_flag;
+	u32 flag;
+
+	base_flag = LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE |
+			LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR;
+
+	if (h2c_size && !c2h_size) {
+		flag = base_flag | LEAPRAID_IEEE_SGE_FLG_EOL;
+
+		leapraid_single_ieee_sg_append(sge,
+					       flag,
+					       0,
+					       h2c_size,
+					       h2c_dma_addr);
+	} else if (!h2c_size && c2h_size) {
+		flag = base_flag | LEAPRAID_IEEE_SGE_FLG_EOL;
+
+		leapraid_single_ieee_sg_append(sge,
+					       flag,
+					       0,
+					       c2h_size,
+					       c2h_dma_addr);
+	} else if (h2c_size && c2h_size) {
+		leapraid_single_ieee_sg_append(sge,
+					       base_flag,
+					       0,
+					       h2c_size,
+					       h2c_dma_addr);
+		sge += LEAPRAID_IEEE_SGE64_ENTRY_SIZE;
+
+		flag = base_flag | LEAPRAID_IEEE_SGE_FLG_EOL;
+		leapraid_single_ieee_sg_append(sge,
+					       flag,
+					       0,
+					       c2h_size,
+					       c2h_dma_addr);
+	} else {
+		leapraid_build_ieee_nodata_sg(adapter, sge);
+	}
+}
+
+struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_from_tgt(
+		struct leapraid_adapter *adapter,
+		struct leapraid_starget_priv *tgt_priv)
+{
+	assert_spin_locked(&adapter->dev_topo.sas_dev_lock);
+	if (tgt_priv->sas_dev)
+		leapraid_sdev_get(tgt_priv->sas_dev);
+
+	return tgt_priv->sas_dev;
+}
+
+struct leapraid_sas_dev *leapraid_get_sas_dev_from_tgt(
+		struct leapraid_adapter *adapter,
+		struct leapraid_starget_priv *tgt_priv)
+{
+	struct leapraid_sas_dev *sas_dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	sas_dev = leapraid_hold_lock_get_sas_dev_from_tgt(adapter, tgt_priv);
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+	return sas_dev;
+}
+
+static struct leapraid_card_port *leapraid_get_port_by_id(
+		struct leapraid_adapter *adapter,
+		u8 port_id, bool skip_dirty)
+{
+	struct leapraid_card_port *port;
+	struct leapraid_card_port *dirty_port = NULL;
+
+	if (!adapter->adapter_attr.enable_mp)
+		port_id = LEAPRAID_DISABLE_MP_PORT_ID;
+
+	list_for_each_entry(port, &adapter->dev_topo.card_port_list, list) {
+		if (port->port_id != port_id)
+			continue;
+
+		if (!(port->flg & LEAPRAID_CARD_PORT_FLG_DIRTY))
+			return port;
+
+		if (skip_dirty && !dirty_port)
+			dirty_port = port;
+	}
+
+	if (dirty_port)
+		return dirty_port;
+
+	if (unlikely(!adapter->adapter_attr.enable_mp)) {
+		port = kzalloc_obj(*port, GFP_ATOMIC);
+		if (!port) {
+			dev_warn(&adapter->pdev->dev,
+				 "%s: Failed to alloc port\n", __func__);
+			return NULL;
+		}
+
+		port->port_id = LEAPRAID_DISABLE_MP_PORT_ID;
+		list_add_tail(&port->list, &adapter->dev_topo.card_port_list);
+		return port;
+	}
+
+	return NULL;
+}
+
+struct leapraid_vphy *leapraid_get_vphy_by_phy(struct leapraid_card_port *port,
+					       u32 phy_seq_num)
+{
+	struct leapraid_vphy *vphy;
+
+	if (!port || !port->vphys_mask)
+		return NULL;
+
+	list_for_each_entry(vphy, &port->vphys_list, list) {
+		if (vphy->phy_mask & BIT(phy_seq_num))
+			return vphy;
+	}
+
+	return NULL;
+}
+
+struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_addr_and_rphy(
+		struct leapraid_adapter *adapter,
+		u64 sas_address,
+		struct sas_rphy *rphy)
+{
+	struct leapraid_sas_dev *sas_dev;
+
+	assert_spin_locked(&adapter->dev_topo.sas_dev_lock);
+	list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_list, list)
+		if (sas_dev->sas_addr == sas_address &&
+		    sas_dev->rphy == rphy) {
+			leapraid_sdev_get(sas_dev);
+			return sas_dev;
+		}
+
+	list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_init_list,
+			    list)
+		if (sas_dev->sas_addr == sas_address &&
+		    sas_dev->rphy == rphy) {
+			leapraid_sdev_get(sas_dev);
+			return sas_dev;
+		}
+
+	return NULL;
+}
+
+struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_addr(
+		struct leapraid_adapter *adapter,
+		u64 sas_address, struct leapraid_card_port *port)
+{
+	struct leapraid_sas_dev *sas_dev;
+
+	if (!port) {
+		dev_warn(&adapter->pdev->dev, "%s: Invalid port\n", __func__);
+		return NULL;
+	}
+
+	assert_spin_locked(&adapter->dev_topo.sas_dev_lock);
+	list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_list, list)
+		if (sas_dev->sas_addr == sas_address &&
+		    sas_dev->card_port == port) {
+			leapraid_sdev_get(sas_dev);
+			return sas_dev;
+		}
+
+	list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_init_list,
+			    list)
+		if (sas_dev->sas_addr == sas_address &&
+		    sas_dev->card_port == port) {
+			leapraid_sdev_get(sas_dev);
+			return sas_dev;
+		}
+
+	return NULL;
+}
+
+struct leapraid_sas_dev *leapraid_get_sas_dev_by_addr(
+		struct leapraid_adapter *adapter,
+		u64 sas_address, struct leapraid_card_port *port)
+{
+	struct leapraid_sas_dev *sas_dev;
+	unsigned long flags;
+
+	if (!port) {
+		dev_warn(&adapter->pdev->dev, "%s: Invalid port\n", __func__);
+		return NULL;
+	}
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_addr(adapter, sas_address,
+							 port);
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+	return sas_dev;
+}
+
+struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_hdl(
+		struct leapraid_adapter *adapter, u16 hdl)
+{
+	struct leapraid_sas_dev *sas_dev;
+
+	assert_spin_locked(&adapter->dev_topo.sas_dev_lock);
+	list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_list, list)
+		if (sas_dev->hdl == hdl) {
+			leapraid_sdev_get(sas_dev);
+			return sas_dev;
+		}
+
+	list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_init_list,
+			    list)
+		if (sas_dev->hdl == hdl) {
+			leapraid_sdev_get(sas_dev);
+			return sas_dev;
+		}
+
+	return NULL;
+}
+
+struct leapraid_sas_dev *leapraid_get_sas_dev_by_hdl(
+		struct leapraid_adapter *adapter, u16 hdl)
+{
+	struct leapraid_sas_dev *sas_dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl);
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+	return sas_dev;
+}
+
+void leapraid_sas_dev_remove(struct leapraid_adapter *adapter,
+			     struct leapraid_sas_dev *sas_dev)
+{
+	unsigned long flags;
+	bool del_from_list;
+
+	if (!sas_dev) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Invalid SAS device\n", __func__);
+		return;
+	}
+
+	del_from_list = false;
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	if (!list_empty(&sas_dev->list)) {
+		list_del_init(&sas_dev->list);
+		del_from_list = true;
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+
+	if (del_from_list) {
+		leapraid_clear_cached_boot_dev(adapter, sas_dev, 0);
+		leapraid_sdev_put(sas_dev);
+	}
+}
+
+static void leapraid_sas_dev_remove_by_hdl(struct leapraid_adapter *adapter,
+					   u16 hdl)
+{
+	struct leapraid_sas_dev *sas_dev;
+	unsigned long flags;
+	bool del_from_list;
+
+	if (adapter->access_ctrl.shost_recovering)
+		return;
+
+	del_from_list = false;
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl);
+	if (sas_dev && (!list_empty(&sas_dev->list))) {
+		list_del_init(&sas_dev->list);
+		del_from_list = true;
+		leapraid_sdev_put(sas_dev);
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+
+	if (del_from_list) {
+		leapraid_remove_device(adapter, sas_dev);
+		leapraid_sdev_put(sas_dev);
+	}
+}
+
+void leapraid_sas_dev_remove_by_sas_address(struct leapraid_adapter *adapter,
+					    u64 sas_address,
+					    struct leapraid_card_port *port)
+{
+	struct leapraid_sas_dev *sas_dev;
+	unsigned long flags;
+	bool del_from_list;
+
+	if (adapter->access_ctrl.shost_recovering)
+		return;
+
+	del_from_list = false;
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_addr(adapter, sas_address,
+							 port);
+	if (sas_dev && (!list_empty(&sas_dev->list))) {
+		list_del_init(&sas_dev->list);
+		del_from_list = true;
+		leapraid_sdev_put(sas_dev);
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+
+	if (del_from_list) {
+		leapraid_remove_device(adapter, sas_dev);
+		leapraid_sdev_put(sas_dev);
+	}
+}
+
+struct leapraid_raid_volume *leapraid_raid_volume_find_by_id(
+		struct leapraid_adapter *adapter, uint id, uint channel)
+{
+	struct leapraid_raid_volume *raid_volume;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+	list_for_each_entry(raid_volume, &adapter->dev_topo.raid_volume_list,
+			    list) {
+		if (raid_volume->id == id && raid_volume->channel == channel) {
+			leapraid_raid_volume_get(raid_volume);
+			spin_unlock_irqrestore(
+				&adapter->dev_topo.raid_volume_lock, flags);
+			return raid_volume;
+		}
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags);
+
+	return NULL;
+}
+
+struct leapraid_raid_volume *leapraid_raid_volume_find_by_hdl(
+		struct leapraid_adapter *adapter, u16 hdl)
+{
+	struct leapraid_raid_volume *raid_volume;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+	list_for_each_entry(raid_volume, &adapter->dev_topo.raid_volume_list,
+			    list) {
+		if (raid_volume->hdl == hdl) {
+			leapraid_raid_volume_get(raid_volume);
+			spin_unlock_irqrestore(
+				&adapter->dev_topo.raid_volume_lock, flags);
+			return raid_volume;
+		}
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags);
+
+	return NULL;
+}
+
+static struct leapraid_raid_volume *leapraid_raid_volume_find_by_wwid(
+		struct leapraid_adapter *adapter, u64 wwid)
+{
+	struct leapraid_raid_volume *raid_volume;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+	list_for_each_entry(raid_volume, &adapter->dev_topo.raid_volume_list,
+			    list) {
+		if (raid_volume->wwid == wwid) {
+			leapraid_raid_volume_get(raid_volume);
+			spin_unlock_irqrestore(
+				&adapter->dev_topo.raid_volume_lock, flags);
+			return raid_volume;
+		}
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags);
+
+	return NULL;
+}
+
+static void leapraid_raid_volume_add(struct leapraid_adapter *adapter,
+				     struct leapraid_raid_volume *raid_volume)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+	leapraid_raid_volume_get(raid_volume);
+	list_add_tail(&raid_volume->list, &adapter->dev_topo.raid_volume_list);
+	spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags);
+}
+
+void leapraid_raid_volume_remove(struct leapraid_adapter *adapter,
+				 struct leapraid_raid_volume *raid_volume)
+{
+	unsigned long flags;
+	bool del_from_list = false;
+
+	if (!raid_volume) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Invalid RAID volume\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+	if (!list_empty(&raid_volume->list)) {
+		list_del_init(&raid_volume->list);
+		del_from_list = true;
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags);
+
+	leapraid_clear_cached_boot_dev(adapter, raid_volume, RAID_CHANNEL);
+	if (del_from_list)
+		leapraid_raid_volume_put(raid_volume);
+}
+
+static struct leapraid_enc_node *leapraid_enc_find_by_hdl(
+		struct leapraid_adapter *adapter, u16 hdl)
+{
+	struct leapraid_enc_node *enc_dev;
+
+	list_for_each_entry(enc_dev, &adapter->dev_topo.enc_list, list)
+		if (le16_to_cpu(enc_dev->pg0.enc_hdl) == hdl)
+			return enc_dev;
+
+	return NULL;
+}
+
+struct leapraid_topo_node *leapraid_exp_find_by_sas_address(
+		struct leapraid_adapter *adapter,
+		u64 sas_address, struct leapraid_card_port *port)
+{
+	struct leapraid_topo_node *sas_exp;
+
+	if (!port) {
+		dev_warn(&adapter->pdev->dev, "%s: Invalid port\n", __func__);
+		return NULL;
+	}
+
+	list_for_each_entry(sas_exp, &adapter->dev_topo.exp_list, list)
+		if (sas_exp->sas_address == sas_address &&
+		    sas_exp->card_port == port)
+			return sas_exp;
+
+	dev_warn(&adapter->pdev->dev,
+		 "%s: No expander found for SAS addr=0x%016llx port=%p\n",
+		 __func__, (unsigned long long)sas_address, port);
+	return NULL;
+}
+
+bool leapraid_scmd_find_by_tgt(struct leapraid_adapter *adapter, uint id,
+			       uint channel)
+{
+	struct scsi_cmnd *scmd;
+	int taskid;
+
+	for (taskid = 1; taskid <= adapter->shost->can_queue; taskid++) {
+		scmd = leapraid_get_scmd_from_taskid(adapter, taskid);
+		if (!scmd)
+			continue;
+
+		if (scmd->device->id == id && scmd->device->channel == channel)
+			return true;
+	}
+
+	return false;
+}
+
+bool leapraid_scmd_find_by_lun(struct leapraid_adapter *adapter, uint id,
+			       unsigned int lun, uint channel)
+{
+	struct scsi_cmnd *scmd;
+	int taskid;
+
+	for (taskid = 1; taskid <= adapter->shost->can_queue; taskid++) {
+		scmd = leapraid_get_scmd_from_taskid(adapter, taskid);
+		if (!scmd)
+			continue;
+
+		if (scmd->device->id == id &&
+		    scmd->device->channel == channel &&
+		    scmd->device->lun == lun)
+			return true;
+	}
+
+	return false;
+}
+
+static struct leapraid_topo_node *leapraid_exp_find_by_hdl(
+		struct leapraid_adapter *adapter, u16 hdl)
+{
+	struct leapraid_topo_node *sas_exp;
+
+	list_for_each_entry(sas_exp, &adapter->dev_topo.exp_list, list)
+		if (sas_exp->hdl == hdl)
+			return sas_exp;
+
+	return NULL;
+}
+
+static enum leapraid_card_port_checking_flg leapraid_get_card_port_feature(
+		struct leapraid_card_port *old_card_port,
+		struct leapraid_card_port *card_port,
+		struct leapraid_card_port_feature *feature)
+{
+	feature->dirty_flg =
+		old_card_port->flg & LEAPRAID_CARD_PORT_FLG_DIRTY;
+	feature->same_addr =
+		old_card_port->sas_address == card_port->sas_address;
+	feature->exact_phy =
+		old_card_port->phy_mask == card_port->phy_mask;
+	feature->phy_overlap =
+		old_card_port->phy_mask & card_port->phy_mask;
+	feature->same_port =
+		old_card_port->port_id == card_port->port_id;
+	feature->cur_chking_old_port = old_card_port;
+
+	if (!feature->dirty_flg || !feature->same_addr)
+		return CARD_PORT_SKIP_CHECKING;
+
+	return CARD_PORT_FURTHER_CHECKING_NEEDED;
+}
+
+static bool leapraid_process_card_port_feature(
+		struct leapraid_card_port_feature *feature)
+{
+	struct leapraid_card_port *old_card_port;
+
+	old_card_port = feature->cur_chking_old_port;
+	if (feature->exact_phy) {
+		feature->checking_state = SAME_PORT_WITH_NOTHING_CHANGED;
+		feature->expected_old_port = old_card_port;
+		return true;
+	}
+
+	if (feature->phy_overlap) {
+		if (feature->same_port) {
+			feature->checking_state =
+				SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS;
+			feature->expected_old_port = old_card_port;
+		} else if (feature->checking_state !=
+			   SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS) {
+			feature->checking_state =
+				SAME_ADDR_WITH_PARTIALLY_CHANGED_PHYS;
+			feature->expected_old_port = old_card_port;
+		}
+	} else if (feature->checking_state !=
+		   SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS &&
+		   feature->checking_state !=
+		   SAME_ADDR_WITH_PARTIALLY_CHANGED_PHYS) {
+		feature->checking_state = SAME_ADDR_ONLY;
+		feature->expected_old_port = old_card_port;
+		feature->same_addr_port_count++;
+	}
+
+	return false;
+}
+
+static int leapraid_check_card_port(
+		struct leapraid_adapter *adapter,
+		struct leapraid_card_port *card_port,
+		struct leapraid_card_port **expected_card_port,
+		int *count)
+{
+	struct leapraid_card_port *old_card_port;
+	struct leapraid_card_port_feature feature;
+
+	*expected_card_port = NULL;
+	memset(&feature, 0, sizeof(struct leapraid_card_port_feature));
+	feature.expected_old_port = NULL;
+	feature.same_addr_port_count = 0;
+	feature.checking_state = NEW_CARD_PORT;
+
+	list_for_each_entry(old_card_port, &adapter->dev_topo.card_port_list,
+			    list) {
+		if (leapraid_get_card_port_feature(old_card_port, card_port,
+						   &feature))
+			continue;
+
+		if (leapraid_process_card_port_feature(&feature))
+			break;
+	}
+
+	if (feature.checking_state == SAME_ADDR_ONLY)
+		*count = feature.same_addr_port_count;
+
+	*expected_card_port = feature.expected_old_port;
+	return feature.checking_state;
+}
+
+static void leapraid_del_phy_part_of_anther_port(
+		struct leapraid_adapter *adapter,
+		struct leapraid_card_port *card_port_table, int index,
+		u8 port_count, int offset)
+{
+	struct leapraid_topo_node *card_topo_node;
+	bool found = false;
+	int i;
+
+	card_topo_node = &adapter->dev_topo.card;
+	for (i = 0; i < port_count; i++) {
+		if (i == index)
+			continue;
+
+		if (card_port_table[i].phy_mask & BIT(offset)) {
+			leapraid_transport_detach_phy_to_port(
+				adapter,
+				card_topo_node,
+				&card_topo_node->card_phy[offset]);
+			found = true;
+			break;
+		}
+	}
+
+	if (!found)
+		card_port_table[index].phy_mask |= BIT(offset);
+}
+
+static void leapraid_add_or_del_phys_from_existing_port(
+		struct leapraid_adapter *adapter,
+		struct leapraid_card_port *card_port,
+		struct leapraid_card_port *card_port_table,
+		int index, u8 port_count)
+{
+	struct leapraid_topo_node *card_topo_node;
+	u32 phy_mask_diff;
+	u32 offset;
+
+	card_topo_node = &adapter->dev_topo.card;
+	phy_mask_diff = card_port->phy_mask ^
+		card_port_table[index].phy_mask;
+	for (offset = 0; offset < adapter->dev_topo.card.phys_num; offset++) {
+		if (!(phy_mask_diff & BIT(offset)))
+			continue;
+
+		if (!(card_port_table[index].phy_mask & BIT(offset))) {
+			leapraid_del_phy_part_of_anther_port(adapter,
+							     card_port_table,
+							     index, port_count,
+							     offset);
+			continue;
+		}
+
+		if (card_topo_node->card_phy[offset].phy_is_assigned)
+			leapraid_transport_detach_phy_to_port(
+				adapter,
+				card_topo_node,
+				&card_topo_node->card_phy[offset]);
+
+		leapraid_transport_attach_phy_to_port(
+			adapter,
+			card_topo_node,
+			&card_topo_node->card_phy[offset],
+			card_port->sas_address,
+			card_port);
+	}
+}
+
+struct leapraid_sas_dev *leapraid_get_next_sas_dev_from_init_list(
+		struct leapraid_adapter *adapter)
+{
+	struct leapraid_sas_dev *sas_dev = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	if (!list_empty(&adapter->dev_topo.sas_dev_init_list)) {
+		sas_dev = list_first_entry(&adapter->dev_topo.sas_dev_init_list,
+					   struct leapraid_sas_dev,
+					   list);
+		leapraid_sdev_get(sas_dev);
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+	return sas_dev;
+}
+
+static bool leapraid_check_boot_dev_internal(u64 sas_address, u64 dev_name,
+					     u64 enc_lid, u16 slot,
+					     struct leapraid_boot_dev *boot_dev,
+					     u8 form)
+{
+	struct leapraid_boot_dev_format_sas_wwid *wwid;
+	struct leapraid_boot_dev_format_enc_slot *es;
+	struct leapraid_boot_dev_format_dev_name *dn;
+	void *pg_dev;
+
+	if (!boot_dev)
+		return false;
+
+	pg_dev = boot_dev->pg_dev;
+	switch (form & LEAPRAID_BOOTDEV_FORM_MASK) {
+	case LEAPRAID_BOOTDEV_FORM_SAS_WWID:
+		wwid = pg_dev;
+
+		if (!sas_address)
+			return false;
+
+		return sas_address == le64_to_cpu(wwid->sas_addr);
+	case LEAPRAID_BOOTDEV_FORM_ENC_SLOT:
+		es = pg_dev;
+
+		if (!enc_lid)
+			return false;
+
+		return (enc_lid == le64_to_cpu(es->enc_lid) &&
+			slot == le16_to_cpu(es->slot_num));
+	case LEAPRAID_BOOTDEV_FORM_DEV_NAME:
+		dn = pg_dev;
+
+		if (!dev_name)
+			return false;
+
+		return dev_name == le64_to_cpu(dn->dev_name);
+	case LEAPRAID_BOOTDEV_FORM_NONE:
+	default:
+		return false;
+	}
+}
+
+void leapraid_boot_dev_get(void *dev, u32 chnl)
+{
+	if (!dev)
+		return;
+
+	if (chnl == RAID_CHANNEL)
+		leapraid_raid_volume_get((struct leapraid_raid_volume *)dev);
+	else
+		leapraid_sdev_get((struct leapraid_sas_dev *)dev);
+}
+
+void leapraid_boot_dev_put(void *dev, u32 chnl)
+{
+	if (!dev)
+		return;
+
+	if (chnl == RAID_CHANNEL)
+		leapraid_raid_volume_put((struct leapraid_raid_volume *)dev);
+	else
+		leapraid_sdev_put((struct leapraid_sas_dev *)dev);
+}
+
+static void leapraid_try_set_boot_dev(struct leapraid_boot_dev *boot_dev,
+				      u64 sas_addr, u64 dev_name,
+				      u64 enc_lid, u16 slot,
+				      void *dev, u32 chnl)
+{
+	bool matched = false;
+
+	if (boot_dev->dev)
+		return;
+
+	matched = leapraid_check_boot_dev_internal(sas_addr, dev_name, enc_lid,
+						   slot, boot_dev,
+						   boot_dev->form);
+	if (matched) {
+		leapraid_boot_dev_get(dev, chnl);
+		boot_dev->dev = dev;
+		boot_dev->chnl = chnl;
+	}
+}
+
+static void leapraid_clear_boot_dev(struct leapraid_adapter *adapter,
+				    struct leapraid_boot_dev *boot_dev,
+				    void *dev, u32 chnl)
+{
+	void *cached_dev;
+	u32 cached_chnl;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->boot_devs.lock, flags);
+	if (boot_dev->dev != dev || boot_dev->chnl != chnl)
+		goto out_unlock;
+
+	cached_dev = boot_dev->dev;
+	cached_chnl = boot_dev->chnl;
+	boot_dev->dev = NULL;
+	boot_dev->chnl = 0;
+	spin_unlock_irqrestore(&adapter->boot_devs.lock, flags);
+	leapraid_boot_dev_put(cached_dev, cached_chnl);
+	return;
+
+out_unlock:
+	spin_unlock_irqrestore(&adapter->boot_devs.lock, flags);
+}
+
+static void leapraid_clear_cached_boot_dev(struct leapraid_adapter *adapter,
+					   void *dev, u32 chnl)
+{
+	leapraid_clear_boot_dev(adapter,
+				&adapter->boot_devs.requested_boot_dev,
+				dev, chnl);
+	leapraid_clear_boot_dev(adapter,
+				&adapter->boot_devs.requested_alt_boot_dev,
+				dev, chnl);
+	leapraid_clear_boot_dev(adapter,
+				&adapter->boot_devs.current_boot_dev,
+				dev, chnl);
+}
+
+static void leapraid_check_boot_dev(struct leapraid_adapter *adapter,
+				    void *dev, u32 chnl)
+{
+	struct leapraid_raid_volume *raid_volume;
+	struct leapraid_sas_dev *sas_dev;
+	u64 sas_addr;
+	u64 dev_name = 0;
+	u64 enc_lid = 0;
+	u16 slot = 0;
+
+	if (!adapter->scan_dev_desc.driver_loading)
+		return;
+
+	switch (chnl) {
+	case RAID_CHANNEL:
+		raid_volume = dev;
+		sas_addr = raid_volume->wwid;
+		break;
+	default:
+		sas_dev = dev;
+		sas_addr = sas_dev->sas_addr;
+		dev_name = sas_dev->dev_name;
+		enc_lid = sas_dev->enc_lid;
+		slot = sas_dev->slot;
+		break;
+	}
+
+	leapraid_try_set_boot_dev(&adapter->boot_devs.requested_boot_dev,
+				  sas_addr, dev_name, enc_lid,
+				  slot, dev, chnl);
+	leapraid_try_set_boot_dev(&adapter->boot_devs.requested_alt_boot_dev,
+				  sas_addr, dev_name, enc_lid,
+				  slot, dev, chnl);
+	leapraid_try_set_boot_dev(&adapter->boot_devs.current_boot_dev,
+				  sas_addr, dev_name, enc_lid,
+				  slot, dev, chnl);
+}
+
+static const char *leapraid_func_name(u8 func)
+{
+	switch (func) {
+	case LEAPRAID_FUNC_SCSIIO:
+		return "SCSIIO";
+	case LEAPRAID_FUNC_SCSI_TMF:
+		return "SCSI_TMF";
+	case LEAPRAID_FUNC_ADAPTER_INIT:
+		return "ADAPTER_INIT";
+	case LEAPRAID_FUNC_GET_ADAPTER_FEATURES:
+		return "GET_ADAPTER_FEATURES";
+	case LEAPRAID_FUNC_CONFIG_OP:
+		return "CONFIG_OP";
+	case LEAPRAID_FUNC_SCAN_DEV:
+		return "SCAN_DEV";
+	case LEAPRAID_FUNC_EVENT_NOTIFY:
+		return "EVENT_NOTIFY";
+	case LEAPRAID_FUNC_FW_DOWNLOAD:
+		return "FW_DOWNLOAD";
+	case LEAPRAID_FUNC_FW_UPLOAD:
+		return "FW_UPLOAD";
+	case LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH:
+		return "SCSIIO_RAID_PASSTHROUGH";
+	case LEAPRAID_FUNC_SCSI_ENC_PROCESSOR:
+		return "SCSI_ENC_PROCESSOR";
+	case LEAPRAID_FUNC_SMP_PASSTHROUGH:
+		return "SMP_PASSTHROUGH";
+	case LEAPRAID_FUNC_SAS_IO_UNIT_CTRL:
+		return "SAS_IO_UNIT_CTRL";
+	case LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH:
+		return "SCSIIO_SATA_PASSTHROUGH";
+	case LEAPRAID_FUNC_ADAPTER_UNIT_RESET:
+		return "ADAPTER_UNIT_RESET";
+	case LEAPRAID_FUNC_HANDSHAKE:
+		return "HANDSHAKE";
+	case LEAPRAID_FUNC_LOGBUF_INIT:
+		return "LOGBUF_INIT";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static const char *leapraid_cfg_action_name(u8 action)
+{
+	switch (action) {
+	case LEAPRAID_CFG_ACT_PAGE_HEADER:
+		return "PAGE_HEADER";
+	case LEAPRAID_CFG_ACT_PAGE_READ_CUR:
+		return "PAGE_READ_CUR";
+	case LEAPRAID_CFG_ACT_PAGE_WRITE_CUR:
+		return "PAGE_WRITE_CUR";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static const char *leapraid_cfg_page_type_name(u8 page_type)
+{
+	switch (page_type) {
+	case LEAPRAID_CFG_PT_IO_UNIT:
+		return "IO_UNIT";
+	case LEAPRAID_CFG_PT_ADAPTER:
+		return "ADAPTER";
+	case LEAPRAID_CFG_PT_BIOS:
+		return "BIOS";
+	case LEAPRAID_CFG_PT_RAID_VOLUME:
+		return "RAID_VOLUME";
+	case LEAPRAID_CFG_PT_MANUFACTURING:
+		return "MANUFACTURING";
+	case LEAPRAID_CFG_PT_RAID_PHYSDISK:
+		return "RAID_PHYSDISK";
+	case LEAPRAID_CFG_PT_EXTENDED:
+		return "EXTENDED";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static const char *leapraid_cfg_ext_page_type_name(u8 ext_page_type)
+{
+	switch (ext_page_type) {
+	case LEAPRAID_CFG_EXTPT_SAS_IO_UNIT:
+		return "SAS_IO_UNIT";
+	case LEAPRAID_CFG_EXTPT_SAS_EXP:
+		return "SAS_EXPANDER";
+	case LEAPRAID_CFG_EXTPT_SAS_DEV:
+		return "SAS_DEVICE";
+	case LEAPRAID_CFG_EXTPT_SAS_PHY:
+		return "SAS_PHY";
+	case LEAPRAID_CFG_EXTPT_ENC:
+		return "ENCLOSURE";
+	case LEAPRAID_CFG_EXTPT_RAID_CONFIG:
+		return "RAID_CONFIG";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static const char *leapraid_sep_action_name(u8 action)
+{
+	switch (action) {
+	case LEAPRAID_SEP_REQ_ACT_WRITE_STATUS:
+		return "WRITE_STATUS";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static const char *leapraid_sas_op_name(u8 op)
+{
+	switch (op) {
+	case LEAPRAID_SAS_OP_PHY_LINK_RESET:
+		return "PHY_LINK_RESET";
+	case LEAPRAID_SAS_OP_PHY_HARD_RESET:
+		return "PHY_HARD_RESET";
+	case LEAPRAID_SAS_OP_SET_PARAMETER:
+		return "SET_PARAMETER";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static const char *leapraid_tm_type_name(u8 task_type)
+{
+	switch (task_type) {
+	case LEAPRAID_TM_TASKTYPE_ABORT_TASK:
+		return "ABORT_TASK";
+	case LEAPRAID_TM_TASKTYPE_ABRT_TASK_SET:
+		return "ABORT_TASK_SET";
+	case LEAPRAID_TM_TASKTYPE_TARGET_RESET:
+		return "TARGET_RESET";
+	case LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET:
+		return "LOGICAL_UNIT_RESET";
+	case LEAPRAID_TM_TASKTYPE_CLEAR_TASK_SET:
+		return "CLEAR_TASK_SET";
+	case LEAPRAID_TM_TASKTYPE_QUERY_TASK:
+		return "QUERY_TASK";
+	case LEAPRAID_TM_TASKTYPE_CLEAR_ACA:
+		return "CLEAR_ACA";
+	case LEAPRAID_TM_TASKTYPE_QUERY_TASK_SET:
+		return "QUERY_TASK_SET";
+	case LEAPRAID_TM_TASKTYPE_QUERY_ASYNC_EVENT:
+		return "QUERY_ASYNC_EVENT";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+void leapraid_log_req_context(struct leapraid_adapter *adapter, u16 smid,
+			      const void *req_data)
+{
+	const struct leapraid_req *req = req_data;
+
+	if (!adapter || !adapter->pdev || !req_data)
+		return;
+
+	switch (req->func) {
+	case LEAPRAID_FUNC_CONFIG_OP: {
+		const struct leapraid_cfg_req *cfg_req = req_data;
+
+		dev_err(&adapter->pdev->dev,
+			"cfg-req: smid=%u func=0x%02x(%s) action=0x%02x(%s)\n",
+			smid, req->func, leapraid_func_name(req->func),
+			cfg_req->action,
+			leapraid_cfg_action_name(cfg_req->action));
+		dev_err(&adapter->pdev->dev,
+			"cfg-req: page_type=0x%02x(%s) page_num=%u\n",
+			cfg_req->header.page_type,
+			leapraid_cfg_page_type_name(cfg_req->header.page_type),
+			cfg_req->header.page_num);
+		if (cfg_req->header.page_type == LEAPRAID_CFG_PT_EXTENDED)
+			dev_err(&adapter->pdev->dev,
+				"cfg-req: ext_page_type=0x%02x(%s)\n",
+				cfg_req->ext_page_type,
+				leapraid_cfg_ext_page_type_name(
+					cfg_req->ext_page_type));
+		dev_err(&adapter->pdev->dev, "cfg-req: page_addr=0x%08x\n",
+			le32_to_cpu(cfg_req->page_addr));
+		break;
+	}
+	case LEAPRAID_FUNC_SCSI_TMF: {
+		const struct leapraid_scsi_tm_req *tm_req = req_data;
+
+		dev_err(&adapter->pdev->dev,
+			"scsi_tm:: smid=%u func=0x%02x(%s) task=0x%02x(%s)\n",
+			smid, req->func, leapraid_func_name(req->func),
+			tm_req->task_type,
+			leapraid_tm_type_name(tm_req->task_type));
+		dev_err(&adapter->pdev->dev,
+			"scsi_tm:: dev_hdl=0x%04x task_mid=%u\n",
+			le16_to_cpu(tm_req->dev_hdl),
+			le16_to_cpu(tm_req->task_mid));
+		break;
+	}
+	case LEAPRAID_FUNC_SCSI_ENC_PROCESSOR: {
+		const struct leapraid_sep_req *sep_req = req_data;
+
+		dev_err(&adapter->pdev->dev,
+			"sep: smid=%u func=0x%02x(%s) action=0x%02x(%s)\n",
+			smid, req->func, leapraid_func_name(req->func),
+			sep_req->act,
+			leapraid_sep_action_name(sep_req->act));
+		dev_err(&adapter->pdev->dev,
+			"sep: dev_hdl=0x%04x slot=%u enc_hdl=0x%04x\n",
+			le16_to_cpu(sep_req->dev_hdl),
+			le16_to_cpu(sep_req->slot),
+			le16_to_cpu(sep_req->enc_hdl));
+		break;
+	}
+	case LEAPRAID_FUNC_SAS_IO_UNIT_CTRL: {
+		const struct leapraid_io_unit_ctrl_req *io_req = req_data;
+
+		dev_err(&adapter->pdev->dev,
+			"ctl_cmd: smid=%u func=0x%02x(%s) action=0x%02x(%s)\n",
+			smid, req->func, leapraid_func_name(req->func),
+			io_req->op, leapraid_sas_op_name(io_req->op));
+		dev_err(&adapter->pdev->dev,
+			"ctl_cmd: dev_hdl=0x%04x param=0x%02x\n",
+			le16_to_cpu(io_req->dev_hdl),
+			io_req->adapter_para);
+		break;
+	}
+	case LEAPRAID_FUNC_SMP_PASSTHROUGH: {
+		const struct leapraid_smp_passthrough_req *smp_req = req_data;
+
+		dev_err(&adapter->pdev->dev,
+			"smp_cmd: smid=%u func=0x%02x(%s) action=0x%02x\n",
+			smid, req->func, leapraid_func_name(req->func),
+			smp_req->passthrough_flg);
+		dev_err(&adapter->pdev->dev,
+			"smp_cmd: port=%u req_len=%u\n",
+			smp_req->physical_port,
+			le16_to_cpu(smp_req->req_data_len));
+		dev_err(&adapter->pdev->dev, "smp_cmd: sas_addr=0x%016llx\n",
+			(unsigned long long)le64_to_cpu(smp_req->sas_address));
+		break;
+	}
+	default:
+		dev_err(&adapter->pdev->dev,
+			"cmd: smid=%u func=0x%02x(%s)\n",
+			smid, req->func, leapraid_func_name(req->func));
+		break;
+	}
+}
+
+static void leapraid_build_and_fire_cfg_req(
+		struct leapraid_adapter *adapter,
+		struct leapraid_cfg_req *leap_mpi_cfgp_req,
+		struct leapraid_cfg_rep *leap_mpi_cfgp_rep)
+{
+	struct leapraid_cfg_req *local_leap_cfg_req;
+	u16 smid;
+
+	memset(leap_mpi_cfgp_rep, 0, sizeof(struct leapraid_cfg_rep));
+	memset(&adapter->driver_cmds.cfg_op_cmd.reply, 0,
+	       sizeof(struct leapraid_cfg_rep));
+	adapter->driver_cmds.cfg_op_cmd.status = LEAPRAID_CMD_PENDING;
+	smid = adapter->driver_cmds.cfg_op_cmd.inter_taskid;
+	local_leap_cfg_req = leapraid_get_task_desc(adapter, smid);
+	memcpy(local_leap_cfg_req, leap_mpi_cfgp_req,
+	       sizeof(struct leapraid_cfg_req));
+	init_completion(&adapter->driver_cmds.cfg_op_cmd.done);
+	leapraid_fire_task(adapter, smid);
+	wait_for_completion_timeout(&adapter->driver_cmds.cfg_op_cmd.done,
+				    LEAPRAID_CFG_OP_TIMEOUT * HZ);
+}
+
+static int leapraid_req_cfg_func(struct leapraid_adapter *adapter,
+				 struct leapraid_cfg_req *leap_mpi_cfgp_req,
+				 struct leapraid_cfg_rep *leap_mpi_cfgp_rep,
+				 void *target_cfg_pg, void *real_cfg_pg_addr,
+				 u16 target_real_cfg_pg_sz)
+{
+	u32 adapter_status = UINT_MAX;
+	bool issue_reset = false;
+	u16 smid;
+	u8 retry_cnt;
+	int rc;
+
+	retry_cnt = 0;
+	mutex_lock(&adapter->driver_cmds.cfg_op_cmd.mutex);
+	smid = adapter->driver_cmds.cfg_op_cmd.inter_taskid;
+retry:
+	if (retry_cnt) {
+		if (retry_cnt > LEAPRAID_CFG_REQ_RETRY_TIMES) {
+			rc = -EFAULT;
+			goto out_cleanup;
+		}
+		dev_warn(&adapter->pdev->dev,
+			 "cfg-req: Retry request, cnt=%u\n", retry_cnt);
+	}
+
+	rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT,
+					  __func__);
+	if (rc) {
+		dev_err(&adapter->pdev->dev,
+			"cfg-req: Adapter not operational\n");
+		goto out_cleanup;
+	}
+
+	leapraid_build_and_fire_cfg_req(adapter, leap_mpi_cfgp_req,
+					leap_mpi_cfgp_rep);
+	if (!(adapter->driver_cmds.cfg_op_cmd.status & LEAPRAID_CMD_DONE)) {
+		retry_cnt++;
+		if (adapter->driver_cmds.cfg_op_cmd.status &
+		    LEAPRAID_CMD_RESET) {
+			dev_warn(&adapter->pdev->dev,
+				 "cfg-req: CMD fail due to hard reset\n");
+			goto retry;
+		}
+
+		if (adapter->access_ctrl.shost_recovering ||
+		    adapter->access_ctrl.pcie_recovering) {
+			dev_err(&adapter->pdev->dev,
+				"cfg-req: pending in %s, status=0x%x\n",
+				adapter->access_ctrl.shost_recovering ?
+				"shost recovery" : "pcie recovery",
+				adapter->driver_cmds.cfg_op_cmd.status);
+			leapraid_log_req_context(adapter, smid,
+						 leap_mpi_cfgp_req);
+			issue_reset = false;
+			rc = -EFAULT;
+		} else {
+			dev_err(&adapter->pdev->dev,
+				"cfg-req: timeout, status=0x%x, reset\n",
+				adapter->driver_cmds.cfg_op_cmd.status);
+			leapraid_log_req_context(adapter, smid,
+						 leap_mpi_cfgp_req);
+			issue_reset = true;
+		}
+
+		goto out_cleanup;
+	}
+
+	if (adapter->driver_cmds.cfg_op_cmd.status &
+	    LEAPRAID_CMD_REPLY_VALID) {
+		memcpy(leap_mpi_cfgp_rep,
+		       &adapter->driver_cmds.cfg_op_cmd.reply,
+		       sizeof(struct leapraid_cfg_rep));
+		adapter_status = le16_to_cpu(
+			leap_mpi_cfgp_rep->adapter_status) &
+			LEAPRAID_ADAPTER_STATUS_MASK;
+		if (adapter_status == LEAPRAID_ADAPTER_STATUS_SUCCESS &&
+		    target_cfg_pg && real_cfg_pg_addr &&
+		    target_real_cfg_pg_sz &&
+		    leap_mpi_cfgp_req->action ==
+		    LEAPRAID_CFG_ACT_PAGE_READ_CUR)
+			memcpy(target_cfg_pg, real_cfg_pg_addr,
+			       target_real_cfg_pg_sz);
+		if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) {
+			if (adapter_status !=
+			    LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_PAGE)
+				dev_err(&adapter->pdev->dev,
+					"cfg-rep: adapter_status=0x%x\n",
+					adapter_status);
+			rc = -EFAULT;
+		}
+	} else {
+		dev_err(&adapter->pdev->dev, "cfg-rep: Reply invalid\n");
+		rc = -EFAULT;
+	}
+
+out_cleanup:
+	adapter->driver_cmds.cfg_op_cmd.status = LEAPRAID_CMD_NOT_USED;
+	mutex_unlock(&adapter->driver_cmds.cfg_op_cmd.mutex);
+	if (issue_reset) {
+		if (adapter->scan_dev_desc.first_scan_dev_fired) {
+			dev_warn(&adapter->pdev->dev,
+				 "%s:%d cfg-req: Failure, issuing reset\n",
+				 __func__, __LINE__);
+			leapraid_hard_reset_handler(adapter, FULL_RESET);
+		} else {
+			dev_warn(&adapter->pdev->dev,
+				 "cfg-req: CMD fail in init, skip reset\n");
+		}
+		rc = -EFAULT;
+	}
+	return rc;
+}
+
+static int leapraid_request_cfg_pg_header(
+		struct leapraid_adapter *adapter,
+		struct leapraid_cfg_req *leap_mpi_cfgp_req,
+		struct leapraid_cfg_rep *leap_mpi_cfgp_rep)
+{
+	return leapraid_req_cfg_func(adapter, leap_mpi_cfgp_req,
+				     leap_mpi_cfgp_rep, NULL, NULL, 0);
+}
+
+static int leapraid_request_cfg_pg(struct leapraid_adapter *adapter,
+				   struct leapraid_cfg_req *leap_mpi_cfgp_req,
+				   struct leapraid_cfg_rep *leap_mpi_cfgp_rep,
+				   void *target_cfg_pg, void *real_cfg_pg_addr,
+				   u16 target_real_cfg_pg_sz)
+{
+	return leapraid_req_cfg_func(adapter, leap_mpi_cfgp_req,
+				     leap_mpi_cfgp_rep, target_cfg_pg,
+				     real_cfg_pg_addr, target_real_cfg_pg_sz);
+}
+
+int leapraid_op_config_page(struct leapraid_adapter *adapter,
+			    void *target_cfg_pg, union cfg_param_1 cfgp1,
+			    union cfg_param_2 cfgp2,
+			    enum config_page_action cfg_op)
+{
+	struct leapraid_cfg_req leap_mpi_cfgp_req;
+	struct leapraid_cfg_rep leap_mpi_cfgp_rep;
+	u16 real_cfg_pg_sz;
+	void *real_cfg_pg_addr;
+	dma_addr_t real_cfg_pg_dma = 0;
+	u32 __page_size;
+	int rc;
+
+	memset(&leap_mpi_cfgp_req, 0, sizeof(struct leapraid_cfg_req));
+	leap_mpi_cfgp_req.func = LEAPRAID_FUNC_CONFIG_OP;
+	leap_mpi_cfgp_req.action = LEAPRAID_CFG_ACT_PAGE_HEADER;
+
+	switch (cfg_op) {
+	case GET_BIOS_PG3:
+		leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_BIOS;
+		leap_mpi_cfgp_req.header.page_num =
+			LEAPRAID_CFG_PAGE_NUM_BIOS3;
+		__page_size = sizeof(struct leapraid_bios_page3);
+		break;
+	case GET_BIOS_PG2:
+		leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_BIOS;
+		leap_mpi_cfgp_req.header.page_num =
+			LEAPRAID_CFG_PAGE_NUM_BIOS2;
+		__page_size = sizeof(struct leapraid_bios_page2);
+		break;
+	case GET_MANUFACTURING_PG0:
+		leap_mpi_cfgp_req.header.page_type =
+			LEAPRAID_CFG_PT_MANUFACTURING;
+		leap_mpi_cfgp_req.header.page_num =
+			LEAPRAID_CFG_PAGE_NUM_MANU0;
+		__page_size = sizeof(struct leapraid_manufacturing_p0);
+		break;
+	case GET_SAS_DEVICE_PG0:
+		leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED;
+		leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_SAS_DEV;
+		leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_DEV0;
+		__page_size = sizeof(struct leapraid_sas_dev_p0);
+		break;
+	case GET_SAS_IOUNIT_PG0:
+		leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED;
+		leap_mpi_cfgp_req.ext_page_type =
+			LEAPRAID_CFG_EXTPT_SAS_IO_UNIT;
+		leap_mpi_cfgp_req.header.page_num =
+			LEAPRAID_CFG_PAGE_NUM_IOUNIT0;
+		__page_size = cfgp1.size;
+		break;
+	case GET_SAS_IOUNIT_PG1:
+		leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED;
+		leap_mpi_cfgp_req.ext_page_type =
+			LEAPRAID_CFG_EXTPT_SAS_IO_UNIT;
+		leap_mpi_cfgp_req.header.page_num =
+			LEAPRAID_CFG_PAGE_NUM_IOUNIT1;
+		__page_size = cfgp1.size;
+		break;
+	case GET_SAS_EXPANDER_PG0:
+		leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED;
+		leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_SAS_EXP;
+		leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_EXP0;
+		__page_size = sizeof(struct leapraid_exp_p0);
+		break;
+	case GET_SAS_EXPANDER_PG1:
+		leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED;
+		leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_SAS_EXP;
+		leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_EXP1;
+		__page_size = sizeof(struct leapraid_exp_p1);
+		break;
+	case GET_SAS_ENCLOSURE_PG0:
+		leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED;
+		leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_ENC;
+		leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_ENC0;
+		__page_size = sizeof(struct leapraid_enc_p0);
+		break;
+	case GET_PHY_PG0:
+		leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED;
+		leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_SAS_PHY;
+		leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_PHY0;
+		__page_size = sizeof(struct leapraid_sas_phy_p0);
+		break;
+	case GET_RAID_VOLUME_PG0:
+		leap_mpi_cfgp_req.header.page_type =
+			LEAPRAID_CFG_PT_RAID_VOLUME;
+		leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_VOL0;
+		__page_size = cfgp1.size;
+		break;
+	case GET_RAID_VOLUME_PG1:
+		leap_mpi_cfgp_req.header.page_type =
+			LEAPRAID_CFG_PT_RAID_VOLUME;
+		leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_VOL1;
+		__page_size = sizeof(struct leapraid_raidvol_p1);
+		break;
+	case GET_PHY_DISK_PG0:
+		leap_mpi_cfgp_req.header.page_type =
+			LEAPRAID_CFG_PT_RAID_PHYSDISK;
+		leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_PD0;
+		__page_size = sizeof(struct leapraid_raidpd_p0);
+		break;
+	default:
+		dev_err(&adapter->pdev->dev,
+			"Unsupported config page action=%d!\n", cfg_op);
+		return -EINVAL;
+	}
+
+	leapraid_build_nodata_mpi_sg(adapter,
+				     &leap_mpi_cfgp_req.page_buf_sge);
+	rc = leapraid_request_cfg_pg_header(adapter,
+					    &leap_mpi_cfgp_req,
+					    &leap_mpi_cfgp_rep);
+	if (rc) {
+		dev_err(&adapter->pdev->dev,
+			"cfg-req: Header failed rc=%dn", rc);
+		return rc;
+	}
+
+	if (cfg_op == GET_SAS_DEVICE_PG0 ||
+	    cfg_op == GET_SAS_EXPANDER_PG0 ||
+	    cfg_op == GET_SAS_ENCLOSURE_PG0 ||
+	    cfg_op == GET_RAID_VOLUME_PG1)
+		leap_mpi_cfgp_req.page_addr = cpu_to_le32(cfgp1.form |
+							  cfgp2.handle);
+	else if (cfg_op == GET_PHY_DISK_PG0)
+		leap_mpi_cfgp_req.page_addr = cpu_to_le32(cfgp1.form |
+							  cfgp2.form_specific);
+	else if (cfg_op == GET_RAID_VOLUME_PG0)
+		leap_mpi_cfgp_req.page_addr =
+			cpu_to_le32(cfgp2.handle |
+				    LEAPRAID_RAID_VOL_CFG_PGAD_HDL);
+	else if (cfg_op == GET_SAS_EXPANDER_PG1)
+		leap_mpi_cfgp_req.page_addr =
+			cpu_to_le32(cfgp2.handle |
+				    (cfgp1.phy_number <<
+				     LEAPRAID_SAS_EXP_CFG_PGAD_PHYNUM_SHIFT) |
+				    LEAPRAID_SAS_EXP_CFG_PGAD_HDL_PHY_NUM);
+	else if (cfg_op == GET_PHY_PG0)
+		leap_mpi_cfgp_req.page_addr = cpu_to_le32(cfgp1.phy_number |
+			LEAPRAID_SAS_PHY_CFG_PGAD_PHY_NUMBER);
+
+	leap_mpi_cfgp_req.action = LEAPRAID_CFG_ACT_PAGE_READ_CUR;
+
+	leap_mpi_cfgp_req.header.page_num = leap_mpi_cfgp_rep.header.page_num;
+	leap_mpi_cfgp_req.header.page_type =
+		leap_mpi_cfgp_rep.header.page_type;
+	leap_mpi_cfgp_req.header.page_len = leap_mpi_cfgp_rep.header.page_len;
+	leap_mpi_cfgp_req.ext_page_len = leap_mpi_cfgp_rep.ext_page_len;
+	leap_mpi_cfgp_req.ext_page_type = leap_mpi_cfgp_rep.ext_page_type;
+
+	real_cfg_pg_sz = (leap_mpi_cfgp_req.header.page_len) ?
+		leap_mpi_cfgp_req.header.page_len * sizeof(u32) :
+		le16_to_cpu(leap_mpi_cfgp_rep.ext_page_len) * sizeof(u32);
+	real_cfg_pg_addr = dma_alloc_coherent(&adapter->pdev->dev,
+					      real_cfg_pg_sz,
+					      &real_cfg_pg_dma,
+					      GFP_KERNEL);
+	if (!real_cfg_pg_addr)
+		return -ENOMEM;
+
+	if (leap_mpi_cfgp_req.action == LEAPRAID_CFG_ACT_PAGE_WRITE_CUR) {
+		leapraid_single_mpi_sg_append(adapter,
+					      &leap_mpi_cfgp_req.page_buf_sge,
+					      ((LEAPRAID_SGE_FLG_SIMPLE_ONE |
+						LEAPRAID_SGE_FLG_LAST_ONE |
+						LEAPRAID_SGE_FLG_EOB |
+						LEAPRAID_SGE_FLG_EOL |
+						LEAPRAID_SGE_FLG_H2C) <<
+						LEAPRAID_SGE_FLG_SHIFT) |
+					       real_cfg_pg_sz,
+					      real_cfg_pg_dma);
+		memcpy(real_cfg_pg_addr, target_cfg_pg,
+		       min_t(u16, real_cfg_pg_sz, __page_size));
+	} else {
+		memset(target_cfg_pg, 0, __page_size);
+		leapraid_single_mpi_sg_append(adapter,
+					      &leap_mpi_cfgp_req.page_buf_sge,
+					      ((LEAPRAID_SGE_FLG_SIMPLE_ONE |
+						LEAPRAID_SGE_FLG_LAST_ONE |
+						LEAPRAID_SGE_FLG_EOB |
+						LEAPRAID_SGE_FLG_EOL) <<
+						LEAPRAID_SGE_FLG_SHIFT) |
+					       real_cfg_pg_sz,
+					      real_cfg_pg_dma);
+		memset(real_cfg_pg_addr, 0,
+		       min_t(u16, real_cfg_pg_sz, __page_size));
+	}
+
+	rc = leapraid_request_cfg_pg(adapter,
+				     &leap_mpi_cfgp_req,
+				     &leap_mpi_cfgp_rep,
+				     target_cfg_pg,
+				     real_cfg_pg_addr,
+				     min_t(u16, real_cfg_pg_sz, __page_size));
+	if (rc) {
+		u32 status;
+
+		status = le16_to_cpu(leap_mpi_cfgp_rep.adapter_status) &
+				     LEAPRAID_ADAPTER_STATUS_MASK;
+		if (status != LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_PAGE)
+			dev_err(&adapter->pdev->dev,
+				"cfg-req: rc=%d, pg_info: 0x%x, 0x%x, %d\n",
+				rc, leap_mpi_cfgp_req.header.page_type,
+				leap_mpi_cfgp_req.ext_page_type,
+				leap_mpi_cfgp_req.header.page_num);
+	}
+
+	if (real_cfg_pg_addr)
+		dma_free_coherent(&adapter->pdev->dev,
+				  real_cfg_pg_sz,
+				  real_cfg_pg_addr,
+				  real_cfg_pg_dma);
+
+	return rc;
+}
+
+static int leapraid_cfg_find_vol_in_page(
+		struct leapraid_raid_cfg_p0 *raid_cfg_p0,
+		u16 pd_hdl, u16 *vol_hdl)
+{
+	u16 elements = raid_cfg_p0->elements_num;
+	int i;
+
+	for (i = 0; i < elements; i++) {
+		struct leapraid_raid_cfg_p0_element *elem;
+		u16 type;
+
+		elem = &raid_cfg_p0->cfg_element[i];
+
+		type = le16_to_cpu(elem->element_flg) &
+		       LEAPRAID_RAIDCFG_P0_EFLG_MASK_ELEMENT_TYPE;
+
+		switch (type) {
+		case LEAPRAID_RAIDCFG_P0_EFLG_VOL_PHYS_DISK_ELEMENT:
+		case LEAPRAID_RAIDCFG_P0_EFLG_OCE_ELEMENT: {
+			u16 phys_hdl;
+
+			phys_hdl = le16_to_cpu(elem->phys_disk_dev_hdl);
+			if (phys_hdl == pd_hdl) {
+				*vol_hdl = le16_to_cpu(elem->vol_dev_hdl);
+				return 0;
+			}
+			break;
+		}
+		case LEAPRAID_RAIDCFG_P0_EFLG_HOT_SPARE_ELEMENT:
+			*vol_hdl = 0;
+			return 0;
+
+		default:
+			break;
+		}
+	}
+
+	return -ENOENT;
+}
+
+static int leapraid_cfg_get_volume_hdl_dispatch(
+		struct leapraid_adapter *adapter,
+		struct leapraid_cfg_req *cfg_req,
+		struct leapraid_cfg_rep *cfg_rep,
+		struct leapraid_raid_cfg_p0 *raid_cfg_p0,
+		void *real_cfg_pg_addr,
+		u16 real_cfg_pg_sz,
+		u16 raid_cfg_p0_sz,
+		u16 pd_hdl, u16 *vol_hdl)
+{
+	u16 adapter_status;
+	int config_num;
+	int rc;
+
+	config_num = 0xFF;
+	while (true) {
+		cfg_req->page_addr =
+			cpu_to_le32(config_num +
+				    LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP);
+		rc = leapraid_request_cfg_pg(
+			adapter, cfg_req, cfg_rep,
+			raid_cfg_p0, real_cfg_pg_addr,
+			min_t(u16, real_cfg_pg_sz, raid_cfg_p0_sz));
+		adapter_status = le16_to_cpu(cfg_rep->adapter_status) &
+			LEAPRAID_ADAPTER_STATUS_MASK;
+		if (rc) {
+			if (adapter_status ==
+			    LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_PAGE) {
+				*vol_hdl = 0;
+				return 0;
+			}
+			return rc;
+		}
+
+		if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS)
+			return LEAPRAID_OPERATION_FAILED;
+
+		rc = leapraid_cfg_find_vol_in_page(raid_cfg_p0,
+						   pd_hdl,
+						   vol_hdl);
+		if (rc != -ENOENT)
+			return rc;
+
+		config_num = raid_cfg_p0->cfg_num;
+	}
+	return 0;
+}
+
+int leapraid_cfg_get_volume_hdl(struct leapraid_adapter *adapter,
+				u16 pd_hdl, u16 *vol_hdl)
+{
+	struct leapraid_raid_cfg_p0 *raid_cfg_p0;
+	struct leapraid_cfg_req cfg_req;
+	struct leapraid_cfg_rep cfg_rep;
+	dma_addr_t real_cfg_pg_dma = 0;
+	void *real_cfg_pg_addr;
+	u16 real_cfg_pg_sz;
+	int rc, raid_cfg_p0_sz;
+
+	*vol_hdl = 0;
+	memset(&cfg_req, 0, sizeof(struct leapraid_cfg_req));
+	cfg_req.func = LEAPRAID_FUNC_CONFIG_OP;
+	cfg_req.action = LEAPRAID_CFG_ACT_PAGE_HEADER;
+	cfg_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED;
+	cfg_req.ext_page_type = LEAPRAID_CFG_EXTPT_RAID_CONFIG;
+	cfg_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_VOL0;
+
+	leapraid_build_nodata_mpi_sg(adapter, &cfg_req.page_buf_sge);
+	rc = leapraid_request_cfg_pg_header(adapter, &cfg_req, &cfg_rep);
+	if (rc)
+		return rc;
+
+	cfg_req.action = LEAPRAID_CFG_ACT_PAGE_READ_CUR;
+	raid_cfg_p0_sz = le16_to_cpu(cfg_rep.ext_page_len) *
+		LEAPRAID_CFG_UNIT_SIZE;
+	raid_cfg_p0 = kmalloc(raid_cfg_p0_sz, GFP_KERNEL);
+	if (!raid_cfg_p0)
+		return -ENOMEM;
+
+	real_cfg_pg_sz = (cfg_req.header.page_len) ?
+		cfg_req.header.page_len * LEAPRAID_CFG_UNIT_SIZE :
+		le16_to_cpu(cfg_rep.ext_page_len) * LEAPRAID_CFG_UNIT_SIZE;
+
+	real_cfg_pg_addr = dma_alloc_coherent(&adapter->pdev->dev,
+					      real_cfg_pg_sz, &real_cfg_pg_dma,
+					      GFP_KERNEL);
+	if (!real_cfg_pg_addr) {
+		rc = -ENOMEM;
+		goto out_free;
+	}
+
+	memset(raid_cfg_p0, 0, raid_cfg_p0_sz);
+	leapraid_single_mpi_sg_append(adapter,
+				      &cfg_req.page_buf_sge,
+				      ((LEAPRAID_SGE_FLG_SIMPLE_ONE |
+					LEAPRAID_SGE_FLG_LAST_ONE |
+					LEAPRAID_SGE_FLG_EOB |
+					LEAPRAID_SGE_FLG_EOL) <<
+				       LEAPRAID_SGE_FLG_SHIFT) |
+				      real_cfg_pg_sz,
+				      real_cfg_pg_dma);
+	memset(real_cfg_pg_addr, 0,
+	       min_t(u16, real_cfg_pg_sz, raid_cfg_p0_sz));
+
+	rc = leapraid_cfg_get_volume_hdl_dispatch(adapter,
+						  &cfg_req, &cfg_rep,
+						  raid_cfg_p0,
+						  real_cfg_pg_addr,
+						  real_cfg_pg_sz,
+						  raid_cfg_p0_sz,
+						  pd_hdl, vol_hdl);
+
+out_free:
+	if (real_cfg_pg_addr)
+		dma_free_coherent(&adapter->pdev->dev,
+				  real_cfg_pg_sz, real_cfg_pg_addr,
+				  real_cfg_pg_dma);
+	kfree(raid_cfg_p0);
+	return rc;
+}
+
+static int leapraid_get_adapter_phys(struct leapraid_adapter *adapter,
+				     u8 *nr_phys)
+{
+	struct leapraid_sas_io_unit_p0 sas_io_unit_page0;
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	int rc;
+
+	*nr_phys = 0;
+	cfgp1.size = sizeof(struct leapraid_sas_io_unit_p0);
+	rc = leapraid_op_config_page(adapter, &sas_io_unit_page0, cfgp1,
+				     cfgp2, GET_SAS_IOUNIT_PG0);
+	if (rc)
+		return rc;
+
+	*nr_phys = sas_io_unit_page0.phy_num;
+
+	return 0;
+}
+
+static int leapraid_cfg_get_number_pds(struct leapraid_adapter *adapter,
+				       u16 hdl, u8 *num_pds)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_raidvol_p0 raidvol_p0;
+	int rc;
+
+	*num_pds = 0;
+	cfgp1.size = sizeof(struct leapraid_raidvol_p0);
+	cfgp2.handle = hdl;
+	rc = leapraid_op_config_page(adapter, &raidvol_p0, cfgp1,
+				     cfgp2, GET_RAID_VOLUME_PG0);
+	if (!rc)
+		*num_pds = raidvol_p0.num_phys_disks;
+
+	return rc;
+}
+
+int leapraid_cfg_get_volume_wwid(struct leapraid_adapter *adapter,
+				 u16 vol_hdl, u64 *wwid)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_raidvol_p1 raidvol_p1;
+	int rc;
+
+	*wwid = 0;
+	cfgp1.form = LEAPRAID_RAID_VOL_CFG_PGAD_HDL;
+	cfgp2.handle = vol_hdl;
+	rc = leapraid_op_config_page(adapter, &raidvol_p1, cfgp1,
+				     cfgp2, GET_RAID_VOLUME_PG1);
+	if (!rc)
+		*wwid = le64_to_cpu(raidvol_p1.wwid);
+
+	return rc;
+}
+
+static int leapraid_get_sas_io_unit_page0(
+		struct leapraid_adapter *adapter,
+		struct leapraid_sas_io_unit_p0 *sas_io_unit_p0,
+		u16 sas_iou_pg0_sz)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+
+	cfgp1.size = sas_iou_pg0_sz;
+	return leapraid_op_config_page(adapter, sas_io_unit_p0, cfgp1,
+				       cfgp2, GET_SAS_IOUNIT_PG0);
+}
+
+static int leapraid_get_sas_address(struct leapraid_adapter *adapter,
+				    u16 hdl, u64 *sas_address)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_sas_dev_p0 sas_dev_p0;
+
+	*sas_address = 0;
+	cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL;
+	cfgp2.handle = hdl;
+	if (leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1,
+				    cfgp2, GET_SAS_DEVICE_PG0))
+		return -ENXIO;
+
+	if (hdl <= adapter->dev_topo.card.phys_num &&
+	    (!(le32_to_cpu(sas_dev_p0.dev_info) & LEAPRAID_DEVTYP_SEP)))
+		*sas_address = adapter->dev_topo.card.sas_address;
+	else
+		*sas_address = le64_to_cpu(sas_dev_p0.sas_address);
+
+	return 0;
+}
+
+int leapraid_get_volume_cap(struct leapraid_adapter *adapter,
+			    struct leapraid_raid_volume *raid_volume)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_raidvol_p0 *raidvol_p0;
+	struct leapraid_sas_dev_p0 sas_dev_p0;
+	struct leapraid_raidpd_p0 raidpd_p0;
+	u8 num_pds;
+	u16 sz;
+	int rc = 0;
+
+	if (leapraid_cfg_get_number_pds(adapter, raid_volume->hdl,
+					&num_pds) || !num_pds)
+		return -EFAULT;
+
+	raid_volume->pd_num = num_pds;
+	sz = offsetof(struct leapraid_raidvol_p0, phys_disk) +
+		(num_pds * sizeof(struct leapraid_raidvol0_phys_disk));
+	raidvol_p0 = kzalloc(sz, GFP_KERNEL);
+	if (!raidvol_p0)
+		return -ENOMEM;
+
+	cfgp1.size = sz;
+	cfgp2.handle = raid_volume->hdl;
+	if (leapraid_op_config_page(adapter, raidvol_p0, cfgp1, cfgp2,
+				    GET_RAID_VOLUME_PG0)) {
+		rc = -EFAULT;
+		goto out_cleanup;
+	}
+
+	raid_volume->vol_type = raidvol_p0->volume_type;
+	cfgp1.form = LEAPRAID_PHYSDISK_CFG_PGAD_PHYSDISKNUM;
+	cfgp2.form_specific = raidvol_p0->phys_disk[0].phys_disk_num;
+	if (!(leapraid_op_config_page(adapter, &raidpd_p0, cfgp1, cfgp2,
+				      GET_PHY_DISK_PG0))) {
+		cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL;
+		cfgp2.handle = le16_to_cpu(raidpd_p0.dev_hdl);
+		if (!(leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1,
+					      cfgp2, GET_SAS_DEVICE_PG0)))
+			raid_volume->dev_info =
+				le32_to_cpu(sas_dev_p0.dev_info);
+	}
+
+out_cleanup:
+	kfree(raidvol_p0);
+	return rc;
+}
+
+static bool leapraid_should_skip_poll_work(struct leapraid_adapter *adapter)
+{
+	unsigned long flags;
+	bool skip;
+
+	spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags);
+	skip = adapter->access_ctrl.shost_recovering ||
+		adapter->access_ctrl.pcie_recovering ||
+		adapter->access_ctrl.host_removing;
+	spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags);
+
+	return skip;
+}
+
+static void leapraid_fw_log_work(struct work_struct *work)
+{
+	struct leapraid_adapter *adapter = container_of(work,
+		struct leapraid_adapter, fw_log_desc.fw_log_work.work);
+	struct leapraid_fw_log_info *infom;
+	struct leapraid_reg_base __iomem *iomem_base;
+	unsigned long flags;
+
+	if (leapraid_should_skip_poll_work(adapter))
+		goto scheduled_timer;
+
+	infom = (struct leapraid_fw_log_info *)
+		(adapter->fw_log_desc.fw_log_buffer +
+		 LEAPRAID_SYS_LOG_BUF_SIZE);
+
+	iomem_base = adapter->iomem_base;
+	if (adapter->fw_log_desc.fw_log_init_flag == 0) {
+		infom->user_position =
+			leapraid_readl(&iomem_base->host_log_buf_pos);
+		infom->adapter_position =
+			leapraid_readl(&iomem_base->adapter_log_buf_pos);
+		adapter->fw_log_desc.fw_log_init_flag++;
+	}
+
+	writel(infom->user_position, &iomem_base->host_log_buf_pos);
+	infom->adapter_position =
+		leapraid_readl(&iomem_base->adapter_log_buf_pos);
+
+scheduled_timer:
+	spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags);
+	if (adapter->fw_log_desc.fw_log_wq)
+		queue_delayed_work(
+			adapter->fw_log_desc.fw_log_wq,
+			&adapter->fw_log_desc.fw_log_work,
+			msecs_to_jiffies(LEAPRAID_PCIE_LOG_POLLING_INTERVAL));
+	spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags);
+}
+
+void leapraid_fw_log_stop(struct leapraid_adapter *adapter)
+{
+	struct workqueue_struct *wq;
+	unsigned long flags;
+
+	if (!adapter->fw_log_desc.open_pcie_trace)
+		return;
+
+	spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags);
+	wq = adapter->fw_log_desc.fw_log_wq;
+	adapter->fw_log_desc.fw_log_wq = NULL;
+	spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags);
+	if (wq) {
+		if (!cancel_delayed_work_sync(&adapter->fw_log_desc
+					      .fw_log_work))
+			flush_workqueue(wq);
+		destroy_workqueue(wq);
+	}
+}
+
+void leapraid_fw_log_start(struct leapraid_adapter *adapter)
+{
+	unsigned long flags;
+
+	if (!adapter->fw_log_desc.open_pcie_trace)
+		return;
+
+	if (adapter->fw_log_desc.fw_log_wq)
+		return;
+
+	INIT_DELAYED_WORK(&adapter->fw_log_desc.fw_log_work,
+			  leapraid_fw_log_work);
+	snprintf(adapter->fw_log_desc.fw_log_wq_name,
+		 sizeof(adapter->fw_log_desc.fw_log_wq_name),
+		 "poll_%s%u_fw_log",
+		 LEAPRAID_DRIVER_NAME, adapter->adapter_attr.id);
+	adapter->fw_log_desc.fw_log_wq =
+		create_singlethread_workqueue(
+			adapter->fw_log_desc.fw_log_wq_name);
+	if (!adapter->fw_log_desc.fw_log_wq)
+		return;
+
+	spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags);
+	if (adapter->fw_log_desc.fw_log_wq)
+		queue_delayed_work(
+			adapter->fw_log_desc.fw_log_wq,
+			&adapter->fw_log_desc.fw_log_work,
+			msecs_to_jiffies(LEAPRAID_PCIE_LOG_POLLING_INTERVAL));
+	spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags);
+}
+
+static void leapraid_check_scheduled_fault_work(struct work_struct *work)
+{
+	struct leapraid_adapter *adapter;
+	unsigned long flags;
+	u32 adapter_state;
+	int rc;
+
+	adapter = container_of(work, struct leapraid_adapter,
+			       reset_desc.fault_reset_work.work);
+
+	if (leapraid_should_skip_poll_work(adapter))
+		goto scheduled_timer;
+
+	adapter_state = leapraid_get_adapter_state(adapter);
+	if (adapter_state != LEAPRAID_DB_OPERATIONAL) {
+		dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset 0x%x\n",
+			 __func__, __LINE__, adapter_state);
+		rc = leapraid_hard_reset_handler(adapter, FULL_RESET);
+		adapter_state = leapraid_get_adapter_state(adapter);
+		if (rc && adapter_state != LEAPRAID_DB_OPERATIONAL) {
+			dev_err(&adapter->pdev->dev,
+				"%s: Hard reset failed, state=0x%x rc=%d\n",
+				__func__, adapter_state, rc);
+			return;
+		}
+	}
+
+scheduled_timer:
+	spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags);
+	if (adapter->reset_desc.fault_reset_wq)
+		queue_delayed_work(
+			adapter->reset_desc.fault_reset_wq,
+			&adapter->reset_desc.fault_reset_work,
+			msecs_to_jiffies(LEAPRAID_FAULT_POLLING_INTERVAL));
+	spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags);
+}
+
+void leapraid_check_scheduled_fault_start(struct leapraid_adapter *adapter)
+{
+	unsigned long flags;
+
+	if (adapter->reset_desc.fault_reset_wq)
+		return;
+
+	INIT_DELAYED_WORK(&adapter->reset_desc.fault_reset_work,
+			  leapraid_check_scheduled_fault_work);
+	snprintf(adapter->reset_desc.fault_reset_wq_name,
+		 sizeof(adapter->reset_desc.fault_reset_wq_name),
+		 "poll_%s%u_status",
+		 LEAPRAID_DRIVER_NAME, adapter->adapter_attr.id);
+	adapter->reset_desc.fault_reset_wq =
+		create_singlethread_workqueue(
+			adapter->reset_desc.fault_reset_wq_name);
+	if (!adapter->reset_desc.fault_reset_wq) {
+		dev_err(&adapter->pdev->dev,
+			"Create single thread workqueue failed!\n");
+		return;
+	}
+
+	spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags);
+	if (adapter->reset_desc.fault_reset_wq)
+		queue_delayed_work(
+			adapter->reset_desc.fault_reset_wq,
+			&adapter->reset_desc.fault_reset_work,
+			msecs_to_jiffies(LEAPRAID_FAULT_POLLING_INTERVAL));
+	spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags);
+}
+
+void leapraid_check_scheduled_fault_stop(struct leapraid_adapter *adapter)
+{
+	struct workqueue_struct *wq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags);
+	wq = adapter->reset_desc.fault_reset_wq;
+	adapter->reset_desc.fault_reset_wq = NULL;
+	spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags);
+
+	if (!wq)
+		return;
+
+	if (!cancel_delayed_work_sync(&adapter->reset_desc.fault_reset_work))
+		flush_workqueue(wq);
+	destroy_workqueue(wq);
+}
+
+static void leapraid_overheat_work(struct work_struct *work)
+{
+	struct leapraid_overheat_desc *desc;
+	struct leapraid_adapter *adapter;
+	struct workqueue_struct *wq;
+	struct Scsi_Host *shost;
+	struct pci_dev *pdev;
+	unsigned long flags;
+
+	desc = container_of(work, struct leapraid_overheat_desc,
+			    fault_overheat_work);
+	adapter = container_of(desc, struct leapraid_adapter, overheat_desc);
+	pdev = adapter->pdev;
+	shost = pci_get_drvdata(pdev);
+
+	if (!shost) {
+		dev_err(&pdev->dev,
+			"Overheat processing failed: invalid host/adapter\n");
+		atomic_set(&adapter->overheat_desc.thermal_alert, 0);
+		wake_up(&adapter->scan_dev_desc.wait_driver_loading);
+		return;
+	}
+
+	if (adapter->access_ctrl.host_removing) {
+		atomic_set(&adapter->overheat_desc.thermal_alert, 0);
+		wake_up(&adapter->scan_dev_desc.wait_driver_loading);
+		return;
+	}
+
+	adapter->access_ctrl.host_removing = 1;
+	adapter->access_ctrl.shost_recover_async = 0;
+	adapter->scan_dev_desc.scan_start = 0;
+	adapter->scan_dev_desc.wait_scan_dev_done = 0;
+	adapter->scan_dev_desc.driver_loading = 0;
+	wake_up(&adapter->access_ctrl.shost_recover_wq);
+	wake_up(&adapter->scan_dev_desc.wait_driver_loading);
+
+	leapraid_mask_int(adapter);
+
+	leapraid_check_scheduled_fault_stop(adapter);
+	leapraid_fw_log_stop(adapter);
+	leapraid_mq_polling_pause(adapter);
+
+	leapraid_clean_active_cmds(adapter);
+	spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags);
+	wq = adapter->fw_evt_s.fw_evt_thread;
+	adapter->fw_evt_s.fw_evt_thread = NULL;
+	spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags);
+	if (wq)
+		destroy_workqueue(wq);
+
+	sas_remove_host(shost);
+	leapraid_cleanup_lists(adapter);
+	atomic_set(&adapter->overheat_desc.thermal_alert, 0);
+	wake_up(&adapter->scan_dev_desc.wait_driver_loading);
+	dev_err(&pdev->dev, "%s: Suspend adapter due to overheat\n", __func__);
+}
+
+static void leapraid_overheat_init(struct leapraid_adapter *adapter)
+{
+	if (adapter->overheat_desc.fault_overheat_wq)
+		return;
+
+	atomic_set(&adapter->overheat_desc.thermal_alert, 0);
+	snprintf(adapter->overheat_desc.fault_overheat_wq_name,
+		 sizeof(adapter->overheat_desc.fault_overheat_wq_name),
+		 "driver_%s%u_overheat",
+		 LEAPRAID_DRIVER_NAME,
+		 adapter->adapter_attr.id);
+	adapter->overheat_desc.fault_overheat_wq =
+		create_singlethread_workqueue(
+			adapter->overheat_desc.fault_overheat_wq_name);
+	if (!adapter->overheat_desc.fault_overheat_wq) {
+		dev_err(&adapter->pdev->dev,
+			"Failed to create overheat workqueue\n");
+		return;
+	}
+
+	INIT_WORK(&adapter->overheat_desc.fault_overheat_work,
+		  leapraid_overheat_work);
+}
+
+void leapraid_overheat_cleanup(struct leapraid_adapter *adapter)
+{
+	struct workqueue_struct *wq;
+
+	wq = xchg(&adapter->overheat_desc.fault_overheat_wq, NULL);
+	if (!wq)
+		return;
+
+	cancel_work_sync(&adapter->overheat_desc.fault_overheat_work);
+	destroy_workqueue(wq);
+}
+
+static void leapraid_fw_work(struct leapraid_adapter *adapter,
+			     struct leapraid_fw_evt_work *fw_evt);
+
+static void leapraid_fw_evt_free(struct kref *r)
+{
+	struct leapraid_fw_evt_work *fw_evt;
+
+	fw_evt = container_of(r, struct leapraid_fw_evt_work, refcnt);
+
+	kfree(fw_evt->evt_data);
+	kfree(fw_evt);
+}
+
+static void leapraid_fw_evt_get(struct leapraid_fw_evt_work *fw_evt)
+{
+	kref_get(&fw_evt->refcnt);
+}
+
+static void leapraid_fw_evt_put(struct leapraid_fw_evt_work *fw_work)
+{
+	kref_put(&fw_work->refcnt, leapraid_fw_evt_free);
+}
+
+static struct leapraid_fw_evt_work *leapraid_alloc_fw_evt_work(void)
+{
+	struct leapraid_fw_evt_work *fw_evt =
+		kzalloc(sizeof(*fw_evt), GFP_ATOMIC);
+
+	if (fw_evt)
+		kref_init(&fw_evt->refcnt);
+
+	return fw_evt;
+}
+
+static void leapraid_run_fw_evt_work(struct work_struct *work)
+{
+	struct leapraid_fw_evt_work *fw_evt =
+		container_of(work, struct leapraid_fw_evt_work, work);
+
+	leapraid_fw_work(fw_evt->adapter, fw_evt);
+}
+
+static void leapraid_fw_evt_add(struct leapraid_adapter *adapter,
+				struct leapraid_fw_evt_work *fw_evt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags);
+	if (adapter->access_ctrl.host_removing ||
+	    adapter->access_ctrl.pcie_recovering ||
+	    !adapter->fw_evt_s.fw_evt_thread) {
+		spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags);
+		return;
+	}
+
+	leapraid_fw_evt_get(fw_evt);
+	INIT_LIST_HEAD(&fw_evt->list);
+	list_add_tail(&fw_evt->list, &adapter->fw_evt_s.fw_evt_list);
+	INIT_WORK(&fw_evt->work, leapraid_run_fw_evt_work);
+	leapraid_fw_evt_get(fw_evt);
+	queue_work(adapter->fw_evt_s.fw_evt_thread, &fw_evt->work);
+	spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags);
+}
+
+static void leapraid_del_fw_evt_from_list(struct leapraid_adapter *adapter,
+					  struct leapraid_fw_evt_work *fw_evt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags);
+	if (!list_empty(&fw_evt->list)) {
+		list_del_init(&fw_evt->list);
+		leapraid_fw_evt_put(fw_evt);
+	}
+	spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags);
+}
+
+static struct leapraid_fw_evt_work *leapraid_next_fw_evt(
+		struct leapraid_adapter *adapter)
+{
+	struct leapraid_fw_evt_work *fw_evt = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags);
+	if (!list_empty(&adapter->fw_evt_s.fw_evt_list)) {
+		fw_evt = list_first_entry(&adapter->fw_evt_s.fw_evt_list,
+					  struct leapraid_fw_evt_work, list);
+		list_del_init(&fw_evt->list);
+	}
+	spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags);
+	return fw_evt;
+}
+
+void leapraid_clean_active_fw_evt(struct leapraid_adapter *adapter)
+{
+	struct leapraid_fw_evt_work *fw_evt;
+	unsigned long flags;
+	bool in_fw_evt_context;
+	bool rc;
+
+	if ((list_empty(&adapter->fw_evt_s.fw_evt_list) &&
+	     !adapter->fw_evt_s.cur_evt) || !adapter->fw_evt_s.fw_evt_thread)
+		return;
+
+	adapter->fw_evt_s.fw_evt_cleanup = 1;
+	if (adapter->access_ctrl.shost_recovering &&
+	    adapter->fw_evt_s.cur_evt)
+		adapter->fw_evt_s.cur_evt->ignore = 1;
+
+	while ((fw_evt = leapraid_next_fw_evt(adapter))) {
+		rc = cancel_work_sync(&fw_evt->work);
+		if (rc)
+			leapraid_fw_evt_put(fw_evt);
+		leapraid_fw_evt_put(fw_evt);
+	}
+
+	spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags);
+	fw_evt = adapter->fw_evt_s.cur_evt;
+	if (fw_evt) {
+		in_fw_evt_context = adapter->fw_evt_s.cur_evt_task == current;
+		leapraid_fw_evt_get(fw_evt);
+	}
+	spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags);
+	if (fw_evt) {
+		if (!in_fw_evt_context)
+			cancel_work_sync(&fw_evt->work);
+		leapraid_fw_evt_put(fw_evt);
+	}
+
+	adapter->fw_evt_s.fw_evt_cleanup = 0;
+}
+
+static void leapraid_internal_dev_ublk(struct scsi_device *sdev,
+				       struct leapraid_sdev_priv *sdev_priv)
+{
+	int rc;
+
+	sdev_printk(KERN_WARNING, sdev,
+		    "hdl 0x%04x: Now internal unblkg dev\n",
+		    sdev_priv->starget_priv->hdl);
+	sdev_priv->block = 0;
+	rc = scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING);
+	if (rc == -EINVAL) {
+		sdev_printk(KERN_WARNING, sdev,
+			    "hdl 0x%04x: unblkg failed, rc=%d\n",
+			    sdev_priv->starget_priv->hdl, rc);
+		sdev_priv->block = 1;
+		rc = scsi_internal_device_block_nowait(sdev);
+		if (rc)
+			sdev_printk(KERN_WARNING, sdev,
+				    "hdl 0x%04x: Earlier ublkg err, rc=%d\n",
+				    sdev_priv->starget_priv->hdl, rc);
+
+		sdev_priv->block = 0;
+		rc = scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING);
+		if (rc)
+			sdev_printk(KERN_WARNING, sdev,
+				    "hdl 0x%04x: ublkg failed again, rc=%d\n",
+				    sdev_priv->starget_priv->hdl, rc);
+	}
+}
+
+static void leapraid_ublk_io_dev(struct leapraid_adapter *adapter,
+				 u64 sas_addr,
+				 struct leapraid_card_port *card_port)
+{
+	struct leapraid_sdev_priv *sdev_priv;
+	struct scsi_device *sdev;
+
+	shost_for_each_device(sdev, adapter->shost) {
+		sdev_priv = sdev->hostdata;
+		if (!sdev_priv || !sdev_priv->starget_priv)
+			continue;
+
+		if (sdev_priv->starget_priv->sas_address != sas_addr)
+			continue;
+
+		if (sdev_priv->starget_priv->card_port != card_port)
+			continue;
+
+		if (sdev_priv->block)
+			leapraid_internal_dev_ublk(sdev, sdev_priv);
+
+		scsi_device_set_state(sdev, SDEV_OFFLINE);
+	}
+}
+
+static void leapraid_ublk_io_all_dev(struct leapraid_adapter *adapter)
+{
+	struct leapraid_sdev_priv *sdev_priv;
+	struct leapraid_starget_priv *stgt_priv;
+	struct scsi_device *sdev;
+
+	shost_for_each_device(sdev, adapter->shost) {
+		sdev_priv = sdev->hostdata;
+
+		if (!sdev_priv)
+			continue;
+
+		stgt_priv = sdev_priv->starget_priv;
+		if (!stgt_priv || stgt_priv->deleted)
+			continue;
+
+		if (!sdev_priv->block)
+			continue;
+
+		sdev_printk(KERN_WARNING, sdev, "hdl 0x%04x: blkg...\n",
+			    sdev_priv->starget_priv->hdl);
+		leapraid_internal_dev_ublk(sdev, sdev_priv);
+		continue;
+	}
+}
+
+static void leapraid_internal_dev_blk(
+		struct scsi_device *sdev,
+		struct leapraid_sdev_priv *sdev_priv)
+{
+	int rc;
+
+	sdev_printk(KERN_INFO, sdev, "Internal blkg hdl 0x%04x\n",
+		    sdev_priv->starget_priv->hdl);
+	sdev_priv->block = 1;
+	rc = scsi_internal_device_block_nowait(sdev);
+	if (rc == -EINVAL)
+		sdev_printk(KERN_WARNING, sdev,
+			    "hdl 0x%04x: blkg failed, rc=%d\n",
+			    rc, sdev_priv->starget_priv->hdl);
+}
+
+static void leapraid_imm_blkio_to_end_dev(struct leapraid_adapter *adapter,
+					  struct leapraid_sas_port *sas_port)
+{
+	struct leapraid_sdev_priv *sdev_priv;
+	struct leapraid_sas_dev *sas_dev;
+	struct scsi_device *sdev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_addr(
+		adapter,
+		sas_port->remote_identify.sas_address,
+		sas_port->card_port);
+
+	if (sas_dev) {
+		shost_for_each_device(sdev, adapter->shost) {
+			sdev_priv = sdev->hostdata;
+			if (!sdev_priv)
+				continue;
+
+			if (sdev_priv->starget_priv->hdl != sas_dev->hdl)
+				continue;
+
+			if (sdev_priv->block)
+				continue;
+
+			if (sas_dev && sas_dev->pend_sas_rphy_add)
+				continue;
+
+			if (sdev_priv->sep) {
+				sdev_printk(KERN_INFO, sdev,
+					    "skip dev blk: sep hdl 0x%04x\n",
+					    sdev_priv->starget_priv->hdl);
+				continue;
+			}
+
+			leapraid_internal_dev_blk(sdev, sdev_priv);
+		}
+
+		leapraid_sdev_put(sas_dev);
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+}
+
+static void leapraid_imm_blkio_set_end_dev_blk_hdls(
+		struct leapraid_adapter *adapter,
+		struct leapraid_topo_node *topo_node_exp)
+{
+	struct leapraid_sas_port *sas_port;
+
+	list_for_each_entry(sas_port,
+			    &topo_node_exp->sas_port_list, port_list) {
+		if (sas_port->remote_identify.device_type == SAS_END_DEVICE)
+			leapraid_imm_blkio_to_end_dev(adapter, sas_port);
+	}
+}
+
+static void leapraid_imm_blkio_to_kids_attchd_to_ex(
+		struct leapraid_adapter *adapter,
+		struct leapraid_topo_node *topo_node_exp);
+
+static void leapraid_imm_blkio_to_sib_exp(
+		struct leapraid_adapter *adapter,
+		struct leapraid_topo_node *topo_node_exp)
+{
+	struct leapraid_topo_node *topo_node_exp_sib;
+	struct leapraid_sas_port *sas_port;
+
+	list_for_each_entry(sas_port,
+			    &topo_node_exp->sas_port_list, port_list) {
+		if (sas_port->remote_identify.device_type ==
+			 SAS_EDGE_EXPANDER_DEVICE ||
+		    sas_port->remote_identify.device_type ==
+			 SAS_FANOUT_EXPANDER_DEVICE) {
+			topo_node_exp_sib =
+				leapraid_exp_find_by_sas_address(
+					adapter,
+					sas_port->remote_identify.sas_address,
+					sas_port->card_port);
+			leapraid_imm_blkio_to_kids_attchd_to_ex(
+				adapter,
+				topo_node_exp_sib);
+		}
+	}
+}
+
+static void leapraid_imm_blkio_to_kids_attchd_to_ex(
+				struct leapraid_adapter *adapter,
+				struct leapraid_topo_node *topo_node_exp)
+{
+	if (!topo_node_exp)
+		return;
+
+	leapraid_imm_blkio_set_end_dev_blk_hdls(adapter, topo_node_exp);
+
+	leapraid_imm_blkio_to_sib_exp(adapter, topo_node_exp);
+}
+
+static void leapraid_report_sdev_directly(struct leapraid_adapter *adapter,
+					  struct leapraid_sas_dev *sas_dev)
+{
+	struct leapraid_sas_port *sas_port;
+
+	sas_port = leapraid_transport_port_add(adapter,
+					       sas_dev->hdl,
+					       sas_dev->parent_sas_addr,
+					       sas_dev->card_port);
+	if (!sas_port) {
+		leapraid_sas_dev_remove(adapter, sas_dev);
+		return;
+	}
+
+	if (!sas_dev->starget) {
+		if (!adapter->scan_dev_desc.driver_loading) {
+			leapraid_transport_port_remove(
+				adapter,
+				sas_dev->sas_addr,
+				sas_dev->parent_sas_addr,
+				sas_dev->card_port);
+			leapraid_sas_dev_remove(adapter, sas_dev);
+		}
+		return;
+	}
+}
+
+static struct leapraid_sas_dev *leapraid_init_sas_dev(
+		struct leapraid_adapter *adapter,
+		struct leapraid_sas_dev_p0 *sas_dev_pg0,
+		struct leapraid_card_port *card_port, u16 hdl,
+		u64 parent_sas_addr, u64 sas_addr, u32 dev_info)
+{
+	struct leapraid_sas_dev *sas_dev;
+	struct leapraid_enc_node *enc_dev;
+	unsigned long flags;
+
+	sas_dev = kzalloc_obj(*sas_dev);
+	if (!sas_dev)
+		return NULL;
+
+	kref_init(&sas_dev->refcnt);
+	sas_dev->hdl = hdl;
+	sas_dev->dev_info = dev_info;
+	sas_dev->sas_addr = sas_addr;
+	sas_dev->card_port = card_port;
+	sas_dev->parent_sas_addr = parent_sas_addr;
+	sas_dev->phy = sas_dev_pg0->phy_num;
+	sas_dev->enc_hdl = le16_to_cpu(sas_dev_pg0->enc_hdl);
+	sas_dev->dev_name = le64_to_cpu(sas_dev_pg0->dev_name);
+	sas_dev->port_connection = sas_dev_pg0->max_port_connections;
+	sas_dev->slot = sas_dev->enc_hdl ? le16_to_cpu(sas_dev_pg0->slot) : 0;
+	if (le16_to_cpu(sas_dev_pg0->flg) &
+	    LEAPRAID_SAS_DEV_P0_FLG_ENC_LEVEL_VALID) {
+		sas_dev->enc_level = sas_dev_pg0->enc_level;
+		memcpy(sas_dev->connector_name,
+		       sas_dev_pg0->connector_name,
+		       LEAPRAID_SAS_DEV_P0_CON_NAME_LEN);
+		sas_dev->connector_name[LEAPRAID_SAS_DEV_P0_CON_NAME_LEN] =
+			'\0';
+	} else {
+		sas_dev->enc_level = 0;
+		sas_dev->connector_name[0] = '\0';
+	}
+	if (sas_dev->enc_hdl) {
+		spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags);
+		enc_dev = leapraid_enc_find_by_hdl(adapter, sas_dev->enc_hdl);
+		if (enc_dev)
+			sas_dev->enc_lid = le64_to_cpu(enc_dev->pg0.enc_lid);
+		spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags);
+	}
+	dev_info(&adapter->pdev->dev,
+		 "add dev: hdl=0x%x, SAS addr=0x%016llx, port connect=0x%x\n",
+		 hdl, sas_dev->sas_addr, sas_dev->port_connection);
+
+	return sas_dev;
+}
+
+static void leapraid_add_dev(struct leapraid_adapter *adapter, u16 hdl)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_sas_dev_p0 sas_dev_pg0;
+	struct leapraid_card_port *card_port;
+	struct leapraid_sas_dev *sas_dev;
+	unsigned long flags;
+	u64 parent_sas_addr;
+	u32 dev_info;
+	u64 sas_addr;
+	u8 port_id;
+
+	cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL;
+	cfgp2.handle = hdl;
+	if (leapraid_op_config_page(adapter, &sas_dev_pg0,
+				    cfgp1, cfgp2, GET_SAS_DEVICE_PG0))
+		return;
+
+	dev_info = le32_to_cpu(sas_dev_pg0.dev_info);
+	if (!(leapraid_is_end_dev(dev_info)))
+		return;
+
+	sas_addr = le64_to_cpu(sas_dev_pg0.sas_address);
+	if (!(le16_to_cpu(sas_dev_pg0.flg) &
+	      LEAPRAID_SAS_DEV_P0_FLG_DEV_PRESENT))
+		return;
+
+	port_id = sas_dev_pg0.physical_port;
+	card_port = leapraid_get_port_by_id(adapter, port_id, false);
+	if (!card_port)
+		return;
+
+	sas_dev = leapraid_get_sas_dev_by_addr(adapter, sas_addr, card_port);
+	if (sas_dev) {
+		leapraid_sdev_put(sas_dev);
+		return;
+	}
+
+	if (leapraid_get_sas_address(adapter,
+				     le16_to_cpu(sas_dev_pg0.parent_dev_hdl),
+				     &parent_sas_addr))
+		return;
+
+	sas_dev = leapraid_init_sas_dev(adapter, &sas_dev_pg0, card_port,
+					hdl, parent_sas_addr, sas_addr,
+					dev_info);
+	if (!sas_dev)
+		return;
+
+	if (adapter->scan_dev_desc.wait_scan_dev_done) {
+		spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+		leapraid_sdev_get(sas_dev);
+		list_add_tail(&sas_dev->list,
+			      &adapter->dev_topo.sas_dev_init_list);
+		leapraid_check_boot_dev(adapter, sas_dev, 0);
+		spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+	} else {
+		spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+		leapraid_sdev_get(sas_dev);
+		list_add_tail(&sas_dev->list, &adapter->dev_topo.sas_dev_list);
+		spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+		leapraid_report_sdev_directly(adapter, sas_dev);
+	}
+	leapraid_sdev_put(sas_dev);
+}
+
+static void leapraid_remove_device(struct leapraid_adapter *adapter,
+				   struct leapraid_sas_dev *sas_dev)
+{
+	struct leapraid_starget_priv *starget_priv;
+
+	leapraid_clear_cached_boot_dev(adapter, sas_dev, 0);
+	if (sas_dev->led_on) {
+		leapraid_set_led(adapter, sas_dev, false);
+		sas_dev->led_on = 0;
+	}
+
+	if (sas_dev->starget && sas_dev->starget->hostdata) {
+		starget_priv = sas_dev->starget->hostdata;
+		starget_priv->deleted = 1;
+		leapraid_ublk_io_dev(adapter,
+				     sas_dev->sas_addr, sas_dev->card_port);
+		starget_priv->hdl = LEAPRAID_INVALID_DEV_HANDLE;
+	}
+
+	leapraid_transport_port_remove(adapter,
+				       sas_dev->sas_addr,
+				       sas_dev->parent_sas_addr,
+				       sas_dev->card_port);
+
+	dev_info(&adapter->pdev->dev,
+		 "remove dev: hdl=0x%04x, SAS addr=0x%016llx\n",
+		 sas_dev->hdl, (unsigned long long)sas_dev->sas_addr);
+}
+
+static struct leapraid_vphy *leapraid_alloc_vphy(
+		struct leapraid_adapter *adapter,
+		u8 port_id, u8 phy_num)
+{
+	struct leapraid_card_port *port;
+	struct leapraid_vphy *vphy;
+
+	port = leapraid_get_port_by_id(adapter, port_id, false);
+	if (!port)
+		return NULL;
+
+	vphy = leapraid_get_vphy_by_phy(port, phy_num);
+	if (vphy)
+		return vphy;
+	vphy = kzalloc_obj(*vphy);
+	if (!vphy)
+		return NULL;
+
+	if (!port->vphys_mask)
+		INIT_LIST_HEAD(&port->vphys_list);
+
+	port->vphys_mask |= BIT(phy_num);
+	vphy->phy_mask |= BIT(phy_num);
+	list_add_tail(&vphy->list, &port->vphys_list);
+	return vphy;
+}
+
+static int leapraid_add_port_to_card_port_list(
+					struct leapraid_adapter *adapter,
+					u8 port_id, bool refresh)
+{
+	struct leapraid_card_port *card_port;
+
+	card_port = leapraid_get_port_by_id(adapter, port_id, false);
+	if (card_port)
+		return 0;
+	card_port = kzalloc_obj(*card_port);
+	if (!card_port)
+		return -ENOMEM;
+
+	card_port->port_id = port_id;
+	dev_dbg(&adapter->pdev->dev,
+		"port: %d is added to card_port list\n",
+		card_port->port_id);
+
+	if (refresh && adapter->access_ctrl.shost_recovering)
+		card_port->flg = LEAPRAID_CARD_PORT_FLG_NEW;
+	list_add_tail(&card_port->list, &adapter->dev_topo.card_port_list);
+	return 0;
+}
+
+static int leapraid_add_card_phy(struct leapraid_adapter *adapter,
+				 struct leapraid_sas_io_unit_p0 *iou,
+				 int i)
+{
+	struct leapraid_sas_phy_p0 phy_pg0;
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_topo_node *card = &adapter->dev_topo.card;
+	u8 port_id;
+
+	cfgp1.phy_number = i;
+
+	if (leapraid_op_config_page(adapter, &phy_pg0,
+				    cfgp1, cfgp2, GET_PHY_PG0))
+		return -EINVAL;
+
+	port_id = iou->phy_info[i].port;
+
+	if (leapraid_add_port_to_card_port_list(adapter, port_id, false))
+		return -EINVAL;
+
+	if ((le32_to_cpu(phy_pg0.phy_info) &
+	     LEAPRAID_SAS_PHYINFO_VPHY) &&
+	    ((phy_pg0.neg_link_rate >>
+	      LEAPRAID_SAS_NEG_LINK_RATE_SHIFT) >=
+	     LEAPRAID_SAS_NEG_LINK_RATE_1_5)) {
+		if (!leapraid_alloc_vphy(adapter, port_id, i))
+			return -ENOMEM;
+
+		card->card_phy[i].vphy = 1;
+	}
+
+	card->card_phy[i].hdl = card->hdl;
+	card->card_phy[i].phy_id = i;
+	card->card_phy[i].card_port =
+		leapraid_get_port_by_id(adapter, port_id, false);
+
+	leapraid_transport_add_card_phy(adapter,
+					&card->card_phy[i],
+					&phy_pg0,
+					card->parent_dev);
+
+	return 0;
+}
+
+static int leapraid_refresh_card_phy(struct leapraid_adapter *adapter,
+				     struct leapraid_sas_io_unit_p0 *iou,
+				     int i)
+{
+	struct leapraid_topo_node *card = &adapter->dev_topo.card;
+	struct leapraid_sas_phy_p0 phy_pg0;
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	u16 attached_hdl;
+	u32 dev_info;
+	u8 link_rate;
+	u8 port_id;
+
+	link_rate = iou->phy_info[i].neg_link_rate >>
+		LEAPRAID_SAS_NEG_LINK_RATE_SHIFT;
+	port_id = iou->phy_info[i].port;
+	if (leapraid_add_port_to_card_port_list(adapter, port_id, true))
+		return -EINVAL;
+
+	dev_info = le32_to_cpu(iou->phy_info[i].controller_phy_dev_info);
+	if (dev_info & LEAPRAID_DEVTYP_SEP &&
+	    link_rate >= LEAPRAID_SAS_NEG_LINK_RATE_1_5) {
+		cfgp1.phy_number = i;
+
+		if (leapraid_op_config_page(adapter, &phy_pg0,
+					    cfgp1, cfgp2, GET_PHY_PG0))
+			return 0;
+
+		if (le32_to_cpu(phy_pg0.phy_info) &
+		    LEAPRAID_SAS_PHYINFO_VPHY) {
+			if (!leapraid_alloc_vphy(adapter, port_id, i))
+				return -ENOMEM;
+			card->card_phy[i].vphy = 1;
+		}
+	}
+
+	card->card_phy[i].hdl = card->hdl;
+
+	attached_hdl = le16_to_cpu(iou->phy_info[i].attached_dev_hdl);
+	if (attached_hdl && link_rate < LEAPRAID_SAS_NEG_LINK_RATE_1_5)
+		link_rate = LEAPRAID_SAS_NEG_LINK_RATE_1_5;
+
+	card->card_phy[i].card_port =
+		leapraid_get_port_by_id(adapter, port_id, false);
+
+	if (!card->card_phy[i].phy) {
+		cfgp1.phy_number = i;
+		if (leapraid_op_config_page(adapter, &phy_pg0,
+					    cfgp1, cfgp2, GET_PHY_PG0))
+			return 0;
+
+		card->card_phy[i].phy_id = i;
+
+		leapraid_transport_add_card_phy(adapter,
+						&card->card_phy[i],
+						&phy_pg0,
+						card->parent_dev);
+		return 0;
+	}
+
+	leapraid_transport_update_links(adapter,
+					card->sas_address,
+					attached_hdl,
+					i,
+					link_rate,
+					card->card_phy[i].card_port);
+
+	return 0;
+}
+
+static void leapraid_sas_host_add(struct leapraid_adapter *adapter,
+				  bool refresh)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_sas_dev_p0 sas_dev_pg0;
+	struct leapraid_enc_p0 enc_pg0;
+	struct leapraid_sas_io_unit_p0 *sas_iou_pg0;
+	u16 sas_iou_pg0_sz;
+	u8 phys_num;
+	int i;
+	int rc;
+
+	if (!refresh) {
+		if (leapraid_get_adapter_phys(adapter, &phys_num) || !phys_num)
+			return;
+
+		adapter->dev_topo.card.card_phy =
+			kcalloc(phys_num,
+				sizeof(struct leapraid_card_phy), GFP_KERNEL);
+		if (!adapter->dev_topo.card.card_phy)
+			return;
+
+		adapter->dev_topo.card.phys_num = phys_num;
+	}
+
+	sas_iou_pg0_sz =
+		offsetof(struct leapraid_sas_io_unit_p0, phy_info) +
+			 (adapter->dev_topo.card.phys_num *
+			  sizeof(struct leapraid_sas_io_unit0_phy_info));
+	sas_iou_pg0 = kzalloc(sas_iou_pg0_sz, GFP_KERNEL);
+	if (!sas_iou_pg0)
+		return;
+
+	if (leapraid_get_sas_io_unit_page0(adapter,
+					   sas_iou_pg0,
+					   sas_iou_pg0_sz))
+		goto out_free;
+
+	adapter->dev_topo.card.parent_dev = &adapter->shost->shost_gendev;
+	adapter->dev_topo.card.hdl =
+		le16_to_cpu(sas_iou_pg0->phy_info[0].controller_dev_hdl);
+	for (i = 0; i < adapter->dev_topo.card.phys_num; i++) {
+		if (!refresh) /* add */
+			rc = leapraid_add_card_phy(adapter, sas_iou_pg0, i);
+		else /* refresh */
+			rc = leapraid_refresh_card_phy(adapter,
+						       sas_iou_pg0,
+						       i);
+		if (rc)
+			goto out_free;
+	}
+
+	if (!refresh) {
+		cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL;
+		cfgp2.handle = adapter->dev_topo.card.hdl;
+		if (leapraid_op_config_page(adapter, &sas_dev_pg0, cfgp1,
+					    cfgp2, GET_SAS_DEVICE_PG0))
+			goto out_free;
+
+		adapter->dev_topo.card.enc_hdl =
+			le16_to_cpu(sas_dev_pg0.enc_hdl);
+		adapter->dev_topo.card.sas_address =
+			le64_to_cpu(sas_dev_pg0.sas_address);
+		dev_info(&adapter->pdev->dev,
+			 "add host: hdl=0x%04x, SAS addr=0x%016llx, phy=%d\n",
+			 adapter->dev_topo.card.hdl,
+			 (unsigned long long)adapter->dev_topo.card.sas_address,
+			 adapter->dev_topo.card.phys_num);
+
+		if (adapter->dev_topo.card.enc_hdl) {
+			cfgp1.form = LEAPRAID_SAS_ENC_CFG_PGAD_HDL;
+			cfgp2.handle = adapter->dev_topo.card.enc_hdl;
+			if (!(leapraid_op_config_page(adapter, &enc_pg0,
+						      cfgp1, cfgp2,
+						      GET_SAS_ENCLOSURE_PG0)))
+				adapter->dev_topo.card.enc_lid =
+					le64_to_cpu(enc_pg0.enc_lid);
+		}
+	}
+out_free:
+	kfree(sas_iou_pg0);
+}
+
+static int leapraid_internal_exp_add(struct leapraid_adapter *adapter,
+				     struct leapraid_exp_p0 *exp_pg0,
+				     union cfg_param_1 *cfgp1,
+				     union cfg_param_2 *cfgp2,
+				     u16 hdl)
+{
+	struct leapraid_topo_node *topo_node_exp;
+	struct leapraid_sas_port *sas_port = NULL;
+	struct leapraid_enc_node *enc_dev;
+	struct leapraid_exp_p1 exp_pg1;
+	int ret;
+	int rc;
+	unsigned long flags;
+	u8 port_id;
+	u16 parent_handle;
+	u64 sas_addr_parent;
+	int i;
+
+	port_id = exp_pg0->physical_port;
+	parent_handle = le16_to_cpu(exp_pg0->parent_dev_hdl);
+
+	rc = leapraid_get_sas_address(adapter,
+				      parent_handle, &sas_addr_parent);
+	if (rc)
+		return rc;
+	topo_node_exp = kzalloc_obj(*topo_node_exp);
+	if (!topo_node_exp)
+		return -ENOMEM;
+
+	topo_node_exp->hdl = hdl;
+	topo_node_exp->phys_num = exp_pg0->phy_num;
+	topo_node_exp->sas_address_parent = sas_addr_parent;
+	topo_node_exp->sas_address = le64_to_cpu(exp_pg0->sas_address);
+	topo_node_exp->card_port =
+		leapraid_get_port_by_id(adapter, port_id, false);
+	if (!topo_node_exp->card_port) {
+		rc = -EPERM;
+		goto out_fail;
+	}
+
+	dev_info(&adapter->pdev->dev,
+		 "add exp: saddr=0x%016llx, hdl=0x%04x, phdl=0x%04x, phy=%d\n",
+		 (unsigned long long)topo_node_exp->sas_address,
+		 hdl, parent_handle,
+		 topo_node_exp->phys_num);
+	if (!topo_node_exp->phys_num) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Invalid PHY num from exp_pg0\n", __func__);
+		rc = -EPERM;
+		goto out_fail;
+	}
+
+	topo_node_exp->card_phy =
+		kcalloc(topo_node_exp->phys_num,
+			sizeof(struct leapraid_card_phy), GFP_KERNEL);
+	if (!topo_node_exp->card_phy) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Failed to alloc expander phy array, count=%u\n",
+			__func__, topo_node_exp->phys_num);
+		rc = -EPERM;
+		goto out_fail;
+	}
+
+	INIT_LIST_HEAD(&topo_node_exp->sas_port_list);
+	sas_port = leapraid_transport_port_add(adapter, hdl, sas_addr_parent,
+					       topo_node_exp->card_port);
+	if (!sas_port) {
+		rc = -EPERM;
+		goto out_fail;
+	}
+
+	topo_node_exp->parent_dev = &sas_port->rphy->dev;
+	topo_node_exp->rphy = sas_port->rphy;
+	for (i = 0; i < topo_node_exp->phys_num; i++) {
+		cfgp1->phy_number = i;
+		cfgp2->handle = hdl;
+		if (leapraid_op_config_page(adapter, &exp_pg1, *cfgp1, *cfgp2,
+					    GET_SAS_EXPANDER_PG1)) {
+			dev_err(&adapter->pdev->dev,
+				"%s: Failed to get exp_pg1, phy=%d\n",
+				__func__, i);
+			rc = -EPERM;
+			goto out_fail;
+		}
+
+		topo_node_exp->card_phy[i].hdl = hdl;
+		topo_node_exp->card_phy[i].phy_id = i;
+		topo_node_exp->card_phy[i].card_port =
+			leapraid_get_port_by_id(adapter, port_id, false);
+		ret = leapraid_transport_add_exp_phy(
+				adapter,
+				&topo_node_exp->card_phy[i],
+				&exp_pg1,
+				topo_node_exp->parent_dev);
+		if (ret) {
+			rc = -EPERM;
+			goto out_fail;
+		}
+	}
+
+	if (topo_node_exp->enc_hdl) {
+		spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags);
+		enc_dev = leapraid_enc_find_by_hdl(adapter,
+						   topo_node_exp->enc_hdl);
+		if (enc_dev)
+			topo_node_exp->enc_lid =
+				le64_to_cpu(enc_dev->pg0.enc_lid);
+		spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags);
+	}
+
+	spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+	list_add_tail(&topo_node_exp->list, &adapter->dev_topo.exp_list);
+	spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags);
+	return 0;
+
+out_fail:
+	if (sas_port)
+		leapraid_transport_port_remove(adapter,
+					       topo_node_exp->sas_address,
+					       sas_addr_parent,
+					       topo_node_exp->card_port);
+	kfree(topo_node_exp->card_phy);
+	kfree(topo_node_exp);
+	return rc;
+}
+
+static int leapraid_exp_add(struct leapraid_adapter *adapter, u16 hdl)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_topo_node *topo_node_exp;
+	struct leapraid_exp_p0 exp_pg0;
+	u16 parent_handle;
+	u64 sas_addr, sas_addr_parent;
+	unsigned long flags;
+	u8 port_id;
+	int rc;
+
+	if (!hdl) {
+		dev_warn(&adapter->pdev->dev, "%s: Invalid hdl\n", __func__);
+		return -EPERM;
+	}
+
+	if (adapter->access_ctrl.shost_recovering ||
+	    adapter->access_ctrl.pcie_recovering) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Failed, shost_recovering=%d pcie_recovering=%d\n",
+			 __func__, adapter->access_ctrl.shost_recovering,
+			adapter->access_ctrl.pcie_recovering);
+		return -EPERM;
+	}
+
+	cfgp1.form = LEAPRAID_SAS_EXP_CFD_PGAD_HDL;
+	cfgp2.handle = hdl;
+	if (leapraid_op_config_page(adapter, &exp_pg0, cfgp1, cfgp2,
+				    GET_SAS_EXPANDER_PG0))
+		return -EPERM;
+
+	parent_handle = le16_to_cpu(exp_pg0.parent_dev_hdl);
+	if (leapraid_get_sas_address(adapter, parent_handle, &sas_addr_parent))
+		return -EPERM;
+
+	port_id = exp_pg0.physical_port;
+	if (sas_addr_parent != adapter->dev_topo.card.sas_address) {
+		spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+		topo_node_exp =
+			leapraid_exp_find_by_sas_address(
+				adapter,
+				sas_addr_parent,
+				leapraid_get_port_by_id(adapter,
+							port_id,
+							false));
+		spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock,
+				       flags);
+		if (!topo_node_exp) {
+			rc = leapraid_exp_add(adapter, parent_handle);
+			if (rc != 0)
+				return rc;
+		}
+	}
+
+	spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+	sas_addr = le64_to_cpu(exp_pg0.sas_address);
+	topo_node_exp =
+		leapraid_exp_find_by_sas_address(
+			adapter,
+			sas_addr,
+			leapraid_get_port_by_id(adapter, port_id, false));
+	spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags);
+
+	if (topo_node_exp)
+		return 0;
+
+	return leapraid_internal_exp_add(adapter, &exp_pg0, &cfgp1,
+					 &cfgp2, hdl);
+}
+
+static void leapraid_exp_node_rm(struct leapraid_adapter *adapter,
+				 struct leapraid_topo_node *topo_node_exp)
+{
+	struct leapraid_sas_port *sas_port, *sas_port_next;
+	unsigned long flags;
+	int port_id;
+
+	list_for_each_entry_safe(sas_port, sas_port_next,
+				 &topo_node_exp->sas_port_list,
+				 port_list) {
+		if (adapter->access_ctrl.shost_recovering)
+			return;
+
+		switch (sas_port->remote_identify.device_type) {
+		case SAS_END_DEVICE:
+			leapraid_sas_dev_remove_by_sas_address(
+				adapter,
+				sas_port->remote_identify.sas_address,
+				sas_port->card_port);
+			break;
+		case SAS_EDGE_EXPANDER_DEVICE:
+		case SAS_FANOUT_EXPANDER_DEVICE:
+			leapraid_exp_rm(
+				adapter,
+				sas_port->remote_identify.sas_address,
+				sas_port->card_port);
+			break;
+		default:
+			break;
+		}
+	}
+
+	port_id = topo_node_exp->card_port->port_id;
+	leapraid_transport_port_remove(adapter, topo_node_exp->sas_address,
+				       topo_node_exp->sas_address_parent,
+				       topo_node_exp->card_port);
+	dev_info(&adapter->pdev->dev,
+		 "removing exp: port=%d, SAS addr=0x%016llx, hdl=0x%04x\n",
+		 port_id, (unsigned long long)topo_node_exp->sas_address,
+		 topo_node_exp->hdl);
+	spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+	list_del(&topo_node_exp->list);
+	spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags);
+	kfree(topo_node_exp->card_phy);
+	kfree(topo_node_exp);
+}
+
+void leapraid_exp_rm(struct leapraid_adapter *adapter, u64 sas_addr,
+		     struct leapraid_card_port *port)
+{
+	struct leapraid_topo_node *topo_node_exp;
+	unsigned long flags;
+
+	if (adapter->access_ctrl.shost_recovering)
+		return;
+
+	if (!port)
+		return;
+
+	spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+	topo_node_exp = leapraid_exp_find_by_sas_address(adapter,
+							 sas_addr,
+							 port);
+	spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags);
+
+	if (topo_node_exp)
+		leapraid_exp_node_rm(adapter, topo_node_exp);
+}
+
+static void leapraid_internal_sas_topo_chg_evt(
+		struct leapraid_adapter *adapter,
+		struct leapraid_card_port *card_port,
+		struct leapraid_topo_node *topo_node_exp,
+		struct leapraid_fw_evt_work *fw_evt,
+		u64 sas_addr, u8 max_phys)
+{
+	struct leapraid_evt_data_sas_topo_change_list *evt_data;
+	u8 phy_number;
+	u8 link_rate;
+	u16 reason_code;
+	u16 hdl;
+	int i;
+
+	evt_data = fw_evt->evt_data;
+	for (i = 0; i < evt_data->entry_num; i++) {
+		if (fw_evt->ignore)
+			return;
+
+		if (adapter->access_ctrl.host_removing ||
+		    adapter->access_ctrl.pcie_recovering)
+			return;
+
+		phy_number = evt_data->start_phy_num + i;
+		if (phy_number >= max_phys)
+			continue;
+
+		reason_code = evt_data->phy[i].phy_status &
+			LEAPRAID_EVT_SAS_TOPO_RC_MASK;
+
+		hdl = le16_to_cpu(evt_data->phy[i].attached_dev_hdl);
+		if (!hdl ||
+		    hdl > adapter->adapter_attr.features.max_dev_handle) {
+			dev_warn(&adapter->pdev->dev,
+				 "%s: Invalid device handle\n", __func__);
+			continue;
+		}
+		link_rate = evt_data->phy[i].link_rate >>
+			LEAPRAID_SAS_NEG_LINK_RATE_SHIFT;
+		switch (reason_code) {
+		case LEAPRAID_EVT_SAS_TOPO_RC_TARG_ADDED:
+			if (adapter->access_ctrl.shost_recovering)
+				break;
+			leapraid_transport_update_links(adapter, sas_addr,
+							hdl, phy_number,
+							link_rate, card_port);
+			if (link_rate < LEAPRAID_SAS_NEG_LINK_RATE_1_5)
+				break;
+			leapraid_add_dev(adapter, hdl);
+			break;
+		case LEAPRAID_EVT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
+			leapraid_sas_dev_remove_by_hdl(adapter, hdl);
+			break;
+		}
+	}
+
+	if (evt_data->exp_status == LEAPRAID_EVT_SAS_TOPO_ES_NOT_RESPONDING &&
+	    topo_node_exp)
+		leapraid_exp_rm(adapter, sas_addr, card_port);
+}
+
+static void leapraid_sas_topo_chg_evt(struct leapraid_adapter *adapter,
+				      struct leapraid_fw_evt_work *fw_evt)
+{
+	struct leapraid_topo_node *topo_node_exp;
+	struct leapraid_card_port *card_port;
+	struct leapraid_evt_data_sas_topo_change_list *evt_data;
+	u16 phdl;
+	u8 max_phys;
+	u64 sas_addr;
+	unsigned long flags;
+
+	if (adapter->access_ctrl.shost_recovering ||
+	    adapter->access_ctrl.host_removing ||
+	    adapter->access_ctrl.pcie_recovering)
+		return;
+
+	evt_data = fw_evt->evt_data;
+	leapraid_sas_host_add(adapter, adapter->dev_topo.card.phys_num > 0);
+
+	if (fw_evt->ignore)
+		return;
+
+	phdl = le16_to_cpu(evt_data->exp_dev_hdl);
+	card_port = leapraid_get_port_by_id(adapter,
+					    evt_data->physical_port,
+					    false);
+	if (evt_data->exp_status == LEAPRAID_EVT_SAS_TOPO_ES_ADDED &&
+	    leapraid_exp_add(adapter, phdl) != 0)
+		return;
+
+	spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+	topo_node_exp = leapraid_exp_find_by_hdl(adapter, phdl);
+	if (topo_node_exp) {
+		sas_addr = topo_node_exp->sas_address;
+		max_phys = topo_node_exp->phys_num;
+		card_port = topo_node_exp->card_port;
+	} else if (phdl < adapter->dev_topo.card.phys_num) {
+		sas_addr = adapter->dev_topo.card.sas_address;
+		max_phys = adapter->dev_topo.card.phys_num;
+	} else {
+		spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock,
+				       flags);
+		return;
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags);
+
+	leapraid_internal_sas_topo_chg_evt(adapter, card_port,
+					   topo_node_exp, fw_evt,
+					   sas_addr, max_phys);
+}
+
+static void leapraid_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach)
+{
+	sdev->no_uld_attach = no_uld_attach ? 1 : 0;
+	sdev_printk(KERN_INFO, sdev,
+		    "%s RAID component to upper layer\n",
+		    sdev->no_uld_attach ? "hide" : "expose");
+	WARN_ON(scsi_device_reprobe(sdev));
+}
+
+static void leapraid_sas_pd_add(
+		struct leapraid_adapter *adapter,
+		struct leapraid_evt_data_ir_change *evt_data)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_sas_dev_p0 sas_dev_p0;
+	struct leapraid_sas_dev *sas_dev;
+	u64 sas_address;
+	u16 parent_hdl;
+	u16 hdl;
+
+	hdl = le16_to_cpu(evt_data->phys_disk_dev_hdl);
+	if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Invalid device handle\n", __func__);
+		return;
+	}
+
+	set_bit(hdl, adapter->dev_topo.pd_hdls);
+	sas_dev = leapraid_get_sas_dev_by_hdl(adapter, hdl);
+	if (sas_dev) {
+		leapraid_sdev_put(sas_dev);
+		dev_warn(&adapter->pdev->dev,
+			 "Dev handle 0x%x already exists\n", hdl);
+		return;
+	}
+
+	cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL;
+	cfgp2.handle = hdl;
+	if (leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1, cfgp2,
+				    GET_SAS_DEVICE_PG0)) {
+		dev_warn(&adapter->pdev->dev, "Failed to read dev page0\n");
+		return;
+	}
+
+	parent_hdl = le16_to_cpu(sas_dev_p0.parent_dev_hdl);
+	if (!leapraid_get_sas_address(adapter, parent_hdl, &sas_address))
+		leapraid_transport_update_links(adapter, sas_address, hdl,
+						sas_dev_p0.phy_num,
+						LEAPRAID_SAS_NEG_LINK_RATE_1_5,
+			 leapraid_get_port_by_id(adapter,
+						 sas_dev_p0.physical_port,
+						 false));
+	leapraid_add_dev(adapter, hdl);
+}
+
+static void leapraid_sas_pd_delete(
+		struct leapraid_adapter *adapter,
+		struct leapraid_evt_data_ir_change *evt_data)
+{
+	u16 hdl;
+
+	hdl = le16_to_cpu(evt_data->phys_disk_dev_hdl);
+	leapraid_sas_dev_remove_by_hdl(adapter, hdl);
+}
+
+static void leapraid_sas_pd_hide(
+		struct leapraid_adapter *adapter,
+		struct leapraid_evt_data_ir_change *evt_data)
+{
+	struct leapraid_starget_priv *starget_priv;
+	struct scsi_target *starget = NULL;
+	struct leapraid_sas_dev *sas_dev;
+	unsigned long flags;
+	u64 volume_wwid = 0;
+	u16 volume_hdl;
+	u16 hdl;
+
+	hdl = le16_to_cpu(evt_data->phys_disk_dev_hdl);
+	if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Invalid device handle\n", __func__);
+		return;
+	}
+	leapraid_cfg_get_volume_hdl(adapter, hdl, &volume_hdl);
+	if (volume_hdl)
+		leapraid_cfg_get_volume_wwid(adapter,
+					     volume_hdl,
+					     &volume_wwid);
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl);
+	if (!sas_dev) {
+		spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+		return;
+	}
+
+	set_bit(hdl, adapter->dev_topo.pd_hdls);
+	if (sas_dev->starget && sas_dev->starget->hostdata) {
+		starget = sas_dev->starget;
+		starget_priv = starget->hostdata;
+		starget_priv->flg |= LEAPRAID_TGT_FLG_RAID_MEMBER;
+		sas_dev->volume_hdl = volume_hdl;
+		sas_dev->volume_wwid = volume_wwid;
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+	if (starget) {
+		starget_for_each_device(starget,
+					(void *)LEAPRAID_NO_ULD_ATTACH_FLAG,
+					leapraid_reprobe_lun);
+	}
+
+	leapraid_sdev_put(sas_dev);
+}
+
+static void leapraid_sas_pd_expose(
+		struct leapraid_adapter *adapter,
+		struct leapraid_evt_data_ir_change *evt_data)
+{
+	struct leapraid_starget_priv *starget_priv;
+	struct scsi_target *starget = NULL;
+	struct leapraid_sas_dev *sas_dev;
+	unsigned long flags;
+	u16 hdl;
+
+	hdl = le16_to_cpu(evt_data->phys_disk_dev_hdl);
+	if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Invalid device handle\n", __func__);
+		return;
+	}
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl);
+	if (!sas_dev) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s:%d: sas_dev not found, hdl=0x%x\n",
+			 __func__, __LINE__, hdl);
+		spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+		return;
+	}
+
+	sas_dev->volume_hdl = 0;
+	sas_dev->volume_wwid = 0;
+	clear_bit(hdl, adapter->dev_topo.pd_hdls);
+	if (sas_dev->starget && sas_dev->starget->hostdata) {
+		starget = sas_dev->starget;
+		starget_priv = starget->hostdata;
+		starget_priv->flg &= ~LEAPRAID_TGT_FLG_RAID_MEMBER;
+		sas_dev->led_on = 0;
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+
+	if (starget) {
+		starget_for_each_device(starget,
+					NULL,
+					leapraid_reprobe_lun);
+	}
+
+	leapraid_sdev_put(sas_dev);
+}
+
+static void leapraid_sas_vol_visibility(
+		struct leapraid_adapter *adapter,
+		struct leapraid_evt_data_ir_change *evt_data)
+{
+	struct leapraid_raid_volume *raid_volume;
+	struct scsi_device *sdev;
+	bool reprobe_flg = false;
+	bool sdev_held = false;
+	unsigned long flags;
+	u16 hdl;
+	u8 rc;
+
+	hdl = le16_to_cpu(evt_data->vol_dev_hdl);
+	rc = evt_data->reason_code;
+	raid_volume = leapraid_raid_volume_find_by_hdl(adapter, hdl);
+	if (!raid_volume) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s:%d: Volume handle 0x%x not found\n",
+			 __func__, __LINE__, hdl);
+		return;
+	}
+
+	spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+	sdev = raid_volume->sdev;
+	if (!sdev) {
+		spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock,
+				       flags);
+		leapraid_raid_volume_put(raid_volume);
+		dev_warn(&adapter->pdev->dev,
+			 "%s:%d: Volume handle 0x%x has no sdev\n",
+			 __func__, __LINE__, hdl);
+		return;
+	}
+
+	if (sdev->no_uld_attach &&
+	    rc == LEAPRAID_EVT_IR_RC_VOLUME_UNHIDE) {
+		sdev->no_uld_attach = 0;
+		reprobe_flg = true;
+	} else if (!sdev->no_uld_attach &&
+		   rc == LEAPRAID_EVT_IR_RC_VOLUME_HIDE) {
+		sdev->no_uld_attach = 1;
+		reprobe_flg = true;
+	}
+
+	if (reprobe_flg && !scsi_device_get(sdev))
+		sdev_held = true;
+	spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags);
+	leapraid_raid_volume_put(raid_volume);
+
+	if (!reprobe_flg) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s:rc(0x%x): Request matches, skipping\n",
+			 __func__, rc);
+		return;
+	}
+
+	if (!sdev_held) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Failed to hold sdev for reprobe, hdl=0x%x\n",
+			 __func__, hdl);
+		return;
+	}
+
+	if (sdev->no_uld_attach)
+		sdev_printk(KERN_INFO, sdev, "hide vol\n");
+	else
+		sdev_printk(KERN_INFO, sdev, "unhide vol\n");
+
+	WARN_ON(scsi_device_reprobe(sdev));
+	scsi_device_put(sdev);
+}
+
+static void leapraid_sas_volume_add(
+		struct leapraid_adapter *adapter,
+		struct leapraid_evt_data_ir_change *evt_data)
+{
+	struct leapraid_raid_volume *raid_volume;
+	unsigned long flags;
+	u64 wwid;
+	u16 hdl;
+
+	hdl = le16_to_cpu(evt_data->vol_dev_hdl);
+
+	if (leapraid_cfg_get_volume_wwid(adapter, hdl, &wwid)) {
+		dev_warn(&adapter->pdev->dev, "Failed to read volume page1\n");
+		return;
+	}
+
+	if (!wwid) {
+		dev_warn(&adapter->pdev->dev, "Invalid WWID(handle=0x%x)\n",
+			 hdl);
+		return;
+	}
+
+	raid_volume = leapraid_raid_volume_find_by_wwid(adapter, wwid);
+	if (raid_volume) {
+		dev_warn(&adapter->pdev->dev,
+			 "Volume handle 0x%x already exists\n", hdl);
+		leapraid_raid_volume_put(raid_volume);
+		return;
+	}
+
+	raid_volume = kzalloc(sizeof(*raid_volume), GFP_KERNEL);
+	if (!raid_volume)
+		return;
+
+	INIT_LIST_HEAD(&raid_volume->list);
+	kref_init(&raid_volume->refcnt);
+	raid_volume->id = adapter->dev_topo.sas_id++;
+	raid_volume->channel = RAID_CHANNEL;
+	raid_volume->hdl = hdl;
+	raid_volume->wwid = wwid;
+	leapraid_raid_volume_add(adapter, raid_volume);
+	if (!adapter->scan_dev_desc.wait_scan_dev_done) {
+		if (scsi_add_device(adapter->shost, RAID_CHANNEL,
+				    raid_volume->id, 0))
+			leapraid_raid_volume_remove(adapter, raid_volume);
+		dev_info(&adapter->pdev->dev,
+			 "add RAID volume: hdl=0x%x, wwid=0x%llx\n", hdl, wwid);
+	} else {
+		spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+		leapraid_check_boot_dev(adapter, raid_volume, RAID_CHANNEL);
+		spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock,
+				       flags);
+	}
+	leapraid_raid_volume_put(raid_volume);
+}
+
+static void leapraid_sas_volume_delete_by_ptr(
+		struct leapraid_adapter *adapter,
+		struct leapraid_raid_volume *raid_volume)
+{
+	struct leapraid_starget_priv *starget_priv;
+	struct scsi_target *starget = NULL;
+	unsigned long flags;
+	bool in_list;
+
+	if (!raid_volume)
+		return;
+
+	spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+	in_list = !list_empty(&raid_volume->list);
+	if (in_list && raid_volume->starget) {
+		starget = raid_volume->starget;
+		starget_priv = starget->hostdata;
+		if (starget_priv)
+			starget_priv->deleted = 1;
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags);
+
+	if (!in_list)
+		return;
+
+	dev_info(&adapter->pdev->dev,
+		 "delete RAID volume: hdl=0x%x, wwid=0x%llx\n",
+		 raid_volume->hdl, raid_volume->wwid);
+	leapraid_raid_volume_remove(adapter, raid_volume);
+
+	if (starget)
+		scsi_remove_target(&starget->dev);
+}
+
+static void leapraid_sas_volume_delete(struct leapraid_adapter *adapter,
+				       u16 hdl)
+{
+	struct leapraid_raid_volume *raid_volume;
+
+	raid_volume = leapraid_raid_volume_find_by_hdl(adapter, hdl);
+	if (!raid_volume) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s:%d: Volume handle 0x%x not found\n",
+			 __func__, __LINE__, hdl);
+		return;
+	}
+
+	leapraid_sas_volume_delete_by_ptr(adapter, raid_volume);
+	leapraid_raid_volume_put(raid_volume);
+}
+
+static void leapraid_sas_ir_chg_evt(struct leapraid_adapter *adapter,
+				    struct leapraid_fw_evt_work *fw_evt)
+{
+	struct leapraid_evt_data_ir_change *evt_data;
+
+	evt_data = fw_evt->evt_data;
+
+	switch (evt_data->reason_code) {
+	case LEAPRAID_EVT_IR_RC_VOLUME_ADD:
+		leapraid_sas_volume_add(adapter, evt_data);
+		break;
+	case LEAPRAID_EVT_IR_RC_VOLUME_DELETE:
+		leapraid_sas_volume_delete(adapter,
+					   le16_to_cpu(evt_data->vol_dev_hdl));
+		break;
+	case LEAPRAID_EVT_IR_RC_PD_HIDDEN_TO_ADD:
+		leapraid_sas_pd_add(adapter, evt_data);
+		break;
+	case LEAPRAID_EVT_IR_RC_PD_UNHIDDEN_TO_DELETE:
+		leapraid_sas_pd_delete(adapter, evt_data);
+		break;
+	case LEAPRAID_EVT_IR_RC_PD_CREATED_TO_HIDE:
+		leapraid_sas_pd_hide(adapter, evt_data);
+		break;
+	case LEAPRAID_EVT_IR_RC_PD_DELETED_TO_EXPOSE:
+		leapraid_sas_pd_expose(adapter, evt_data);
+		break;
+	case LEAPRAID_EVT_IR_RC_VOLUME_HIDE:
+	case LEAPRAID_EVT_IR_RC_VOLUME_UNHIDE:
+		leapraid_sas_vol_visibility(adapter, evt_data);
+		break;
+	default:
+		break;
+	}
+}
+
+static void leapraid_sas_enc_dev_stat_add_node(
+		struct leapraid_adapter *adapter, u16 hdl)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_enc_node *enc_node;
+	struct leapraid_enc_node *enc_exist;
+	unsigned long flags;
+	int rc;
+
+	enc_node = kzalloc_obj(*enc_node);
+	if (!enc_node)
+		return;
+
+	cfgp1.form = LEAPRAID_SAS_ENC_CFG_PGAD_HDL;
+	cfgp2.handle = hdl;
+	rc = leapraid_op_config_page(adapter, &enc_node->pg0, cfgp1, cfgp2,
+				     GET_SAS_ENCLOSURE_PG0);
+	if (rc) {
+		kfree(enc_node);
+		return;
+	}
+
+	spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags);
+	enc_exist = leapraid_enc_find_by_hdl(adapter, hdl);
+	if (enc_exist) {
+		spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags);
+		kfree(enc_node);
+		return;
+	}
+	list_add_tail(&enc_node->list, &adapter->dev_topo.enc_list);
+	spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags);
+}
+
+static void leapraid_sas_enc_dev_stat_del_node(
+		struct leapraid_adapter *adapter, u16 hdl)
+{
+	struct leapraid_enc_node *enc_node;
+	unsigned long flags;
+
+	if (!hdl)
+		return;
+
+	spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags);
+	enc_node = leapraid_enc_find_by_hdl(adapter, hdl);
+	if (enc_node)
+		list_del(&enc_node->list);
+	spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags);
+
+	kfree(enc_node);
+}
+
+static void leapraid_sas_enc_dev_stat_chg_evt(
+		struct leapraid_adapter *adapter,
+		struct leapraid_fw_evt_work *fw_evt)
+{
+	struct leapraid_evt_data_sas_enc_dev_status_change *evt_data;
+	u16 enc_hdl;
+
+	if (adapter->access_ctrl.shost_recovering)
+		return;
+
+	evt_data = fw_evt->evt_data;
+	enc_hdl = le16_to_cpu(evt_data->enc_hdl);
+	switch (evt_data->reason_code) {
+	case LEAPRAID_EVT_SAS_ENCL_RC_ADDED:
+		if (enc_hdl)
+			leapraid_sas_enc_dev_stat_add_node(adapter, enc_hdl);
+		break;
+	case LEAPRAID_EVT_SAS_ENCL_RC_NOT_RESPONDING:
+		leapraid_sas_enc_dev_stat_del_node(adapter, enc_hdl);
+		break;
+	default:
+		break;
+	}
+}
+
+static void leapraid_remove_unresp_sas_end_dev(
+		struct leapraid_adapter *adapter)
+{
+	struct leapraid_sas_dev *sas_dev, *sas_dev_next;
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	list_for_each_entry_safe(sas_dev, sas_dev_next,
+				 &adapter->dev_topo.sas_dev_init_list, list) {
+		if (sas_dev->rphy || sas_dev->pend_sas_rphy_add)
+			continue;
+
+		list_del_init(&sas_dev->list);
+		leapraid_clear_cached_boot_dev(adapter, sas_dev, 0);
+		leapraid_sdev_put(sas_dev);
+	}
+	list_for_each_entry_safe(sas_dev, sas_dev_next,
+				 &adapter->dev_topo.sas_dev_list, list) {
+		if (!sas_dev->resp)
+			list_move_tail(&sas_dev->list, &head);
+		else
+			sas_dev->resp = 0;
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+
+	list_for_each_entry_safe(sas_dev, sas_dev_next, &head, list) {
+		leapraid_remove_device(adapter, sas_dev);
+		list_del_init(&sas_dev->list);
+		leapraid_sdev_put(sas_dev);
+	}
+
+	dev_warn(&adapter->pdev->dev,
+		 "Unresponsive SAS end devices removed\n");
+}
+
+static void leapraid_remove_unresp_raid_volumes(
+		struct leapraid_adapter *adapter)
+{
+	unsigned long flags;
+	struct leapraid_raid_volume *raid_volume, *raid_volume_next;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+	list_for_each_entry_safe(raid_volume, raid_volume_next,
+				 &adapter->dev_topo.raid_volume_list, list) {
+		if (!raid_volume->resp)
+			list_move_tail(&raid_volume->list, &head);
+		else
+			raid_volume->resp = 0;
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags);
+
+	list_for_each_entry_safe(raid_volume, raid_volume_next, &head, list) {
+		leapraid_sas_volume_delete_by_ptr(adapter, raid_volume);
+	}
+
+	dev_warn(&adapter->pdev->dev,
+		 "Unresponsive RAID volumes removed\n");
+}
+
+static void leapraid_remove_unresp_sas_exp(struct leapraid_adapter *adapter)
+{
+	struct leapraid_topo_node *topo_node_exp, *topo_node_exp_next;
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+	list_for_each_entry_safe(topo_node_exp, topo_node_exp_next,
+				 &adapter->dev_topo.exp_list, list) {
+		if (!topo_node_exp->resp)
+			list_move_tail(&topo_node_exp->list, &head);
+		else
+			topo_node_exp->resp = 0;
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags);
+
+	list_for_each_entry_safe(topo_node_exp, topo_node_exp_next,
+				 &head, list)
+		leapraid_exp_node_rm(adapter, topo_node_exp);
+
+	dev_warn(&adapter->pdev->dev,
+		 "Unresponsive SAS expanders removed\n");
+}
+
+static void leapraid_remove_unresp_dev(struct leapraid_adapter *adapter)
+{
+	leapraid_remove_unresp_sas_end_dev(adapter);
+	if (adapter->adapter_attr.raid_support)
+		leapraid_remove_unresp_raid_volumes(adapter);
+	leapraid_remove_unresp_sas_exp(adapter);
+	leapraid_ublk_io_all_dev(adapter);
+}
+
+static void leapraid_del_dirty_vphy(struct leapraid_adapter *adapter)
+{
+	struct leapraid_card_port *card_port, *card_port_next;
+	struct leapraid_vphy *vphy, *vphy_next;
+
+	list_for_each_entry_safe(card_port, card_port_next,
+				 &adapter->dev_topo.card_port_list, list) {
+		if (!card_port->vphys_mask)
+			continue;
+
+		list_for_each_entry_safe(vphy, vphy_next,
+					 &card_port->vphys_list, list) {
+			if (!(vphy->flg & LEAPRAID_VPHY_FLG_DIRTY))
+				continue;
+
+			card_port->vphys_mask &= ~vphy->phy_mask;
+			list_del(&vphy->list);
+			kfree(vphy);
+		}
+
+		if (!card_port->vphys_mask && !card_port->sas_address)
+			card_port->flg |= LEAPRAID_CARD_PORT_FLG_DIRTY;
+	}
+}
+
+static void leapraid_del_dirty_card_port(struct leapraid_adapter *adapter)
+{
+	struct leapraid_card_port *card_port, *card_port_next;
+
+	list_for_each_entry_safe(card_port, card_port_next,
+				 &adapter->dev_topo.card_port_list, list) {
+		if (!(card_port->flg & LEAPRAID_CARD_PORT_FLG_DIRTY) ||
+		    card_port->flg & LEAPRAID_CARD_PORT_FLG_NEW)
+			continue;
+
+		list_del(&card_port->list);
+		kfree(card_port);
+	}
+}
+
+static void leapraid_update_dev_qdepth(struct leapraid_adapter *adapter)
+{
+	struct leapraid_sdev_priv *sdev_priv;
+	struct leapraid_sas_dev *sas_dev;
+	struct leapraid_adapter_attr *attr;
+	struct scsi_device *sdev;
+	u16 qdepth;
+
+	attr = &adapter->adapter_attr;
+	shost_for_each_device(sdev, adapter->shost) {
+		sdev_priv = sdev->hostdata;
+		if (!sdev_priv || !sdev_priv->starget_priv)
+			continue;
+		sas_dev = sdev_priv->starget_priv->sas_dev;
+		if (sas_dev && sas_dev->dev_info & LEAPRAID_DEVTYP_SSP_TGT)
+			qdepth = (sas_dev->port_connection > 1) ?
+				attr->wideport_max_queue_depth :
+				attr->narrowport_max_queue_depth;
+		else if (sas_dev && sas_dev->dev_info &
+			 LEAPRAID_DEVTYP_SATA_DEV)
+			qdepth = attr->sata_max_queue_depth;
+		else
+			continue;
+
+		leapraid_change_queue_depth(sdev, qdepth);
+	}
+}
+
+static void leapraid_update_exp_links(struct leapraid_adapter *adapter,
+				      struct leapraid_topo_node *topo_node_exp,
+				      u16 hdl)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_exp_p1 exp_p1;
+	int i;
+
+	cfgp2.handle = hdl;
+	for (i = 0; i < topo_node_exp->phys_num; i++) {
+		cfgp1.phy_number = i;
+		if (leapraid_op_config_page(adapter, &exp_p1, cfgp1, cfgp2,
+					    GET_SAS_EXPANDER_PG1))
+			return;
+
+		leapraid_transport_update_links(
+			adapter,
+			topo_node_exp->sas_address,
+			le16_to_cpu(exp_p1.attached_dev_hdl),
+			i,
+			exp_p1.neg_link_rate >>
+				LEAPRAID_SAS_NEG_LINK_RATE_SHIFT,
+			topo_node_exp->card_port);
+	}
+}
+
+static void leapraid_scan_exp_after_reset(struct leapraid_adapter *adapter)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_topo_node *topo_node_exp;
+	struct leapraid_exp_p0 exp_p0;
+	unsigned long flags;
+	u16 hdl;
+	u8 port_id;
+
+	cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP;
+	for (hdl = 0xFFFF, cfgp2.handle = hdl;
+	     !leapraid_op_config_page(adapter, &exp_p0, cfgp1, cfgp2,
+				      GET_SAS_EXPANDER_PG0);
+	     cfgp2.handle = hdl) {
+		hdl = le16_to_cpu(exp_p0.dev_hdl);
+		port_id = exp_p0.physical_port;
+		spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+		topo_node_exp =
+			leapraid_exp_find_by_sas_address(
+				adapter,
+				le64_to_cpu(exp_p0.sas_address),
+				leapraid_get_port_by_id(adapter,
+							port_id,
+							false));
+		spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock,
+				       flags);
+
+		if (topo_node_exp) {
+			leapraid_update_exp_links(adapter, topo_node_exp, hdl);
+		} else {
+			leapraid_exp_add(adapter, hdl);
+
+			dev_info(&adapter->pdev->dev,
+				 "add exp: hdl=0x%04x, SAS addr=0x%016llx\n",
+				 hdl,
+				 (unsigned long long)le64_to_cpu(
+					exp_p0.sas_address));
+		}
+	}
+}
+
+static void leapraid_scan_phy_disks_after_reset(
+		struct leapraid_adapter *adapter)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	union cfg_param_1 cfgp1_extra = {0};
+	union cfg_param_2 cfgp2_extra = {0};
+	struct leapraid_sas_dev_p0 sas_dev_p0;
+	struct leapraid_raidpd_p0 raidpd_p0;
+	struct leapraid_sas_dev *sas_dev;
+	u8 phys_disk_num, port_id;
+	u16 hdl, parent_hdl;
+	u64 sas_addr;
+
+	cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP;
+	for (phys_disk_num = 0xFF, cfgp2.form_specific = phys_disk_num;
+	     !leapraid_op_config_page(adapter, &raidpd_p0,
+				      cfgp1, cfgp2, GET_PHY_DISK_PG0);
+	     cfgp2.form_specific = phys_disk_num) {
+		phys_disk_num = raidpd_p0.phys_disk_num;
+		hdl = le16_to_cpu(raidpd_p0.dev_hdl);
+		sas_dev = leapraid_get_sas_dev_by_hdl(adapter, hdl);
+		if (sas_dev) {
+			leapraid_sdev_put(sas_dev);
+			continue;
+		}
+
+		cfgp1_extra.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL;
+		cfgp2_extra.handle = hdl;
+		if (leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1_extra,
+					    cfgp2_extra, GET_SAS_DEVICE_PG0) !=
+					    0)
+			continue;
+
+		parent_hdl = le16_to_cpu(sas_dev_p0.parent_dev_hdl);
+		if (!leapraid_get_sas_address(adapter,
+					      parent_hdl,
+					      &sas_addr)) {
+			port_id = sas_dev_p0.physical_port;
+			leapraid_transport_update_links(
+				adapter, sas_addr, hdl,
+				sas_dev_p0.phy_num,
+				LEAPRAID_SAS_NEG_LINK_RATE_1_5,
+				leapraid_get_port_by_id(
+					adapter, port_id, false));
+			if (!hdl || hdl >
+			    adapter->adapter_attr.features.max_dev_handle) {
+				dev_warn(&adapter->pdev->dev,
+					 "%s: Invalid device handle\n",
+					 __func__);
+			} else {
+				set_bit(hdl, adapter->dev_topo.pd_hdls);
+				leapraid_add_dev(adapter, hdl);
+
+				dev_info(&adapter->pdev->dev,
+					 "add pd hdl=0x%04x saddr=0x%016llx\n",
+					 hdl,
+					 (unsigned long long)le64_to_cpu(
+						sas_dev_p0.sas_address));
+			}
+		}
+	}
+}
+
+static void leapraid_scan_vol_after_reset(struct leapraid_adapter *adapter)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	union cfg_param_1 cfgp1_extra = {0};
+	union cfg_param_2 cfgp2_extra = {0};
+	struct leapraid_evt_data_ir_change evt_data;
+	struct leapraid_raid_volume *raid_volume;
+	struct leapraid_raidvol_p1 *vol_p1;
+	struct leapraid_raidvol_p0 *vol_p0;
+	u16 hdl;
+
+	vol_p0 = kzalloc_obj(*vol_p0);
+	if (!vol_p0)
+		return;
+
+	vol_p1 = kzalloc_obj(*vol_p1);
+	if (!vol_p1) {
+		kfree(vol_p0);
+		return;
+	}
+
+	cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP;
+	for (hdl = 0xFFFF, cfgp2.handle = hdl;
+	     !leapraid_op_config_page(adapter, vol_p1, cfgp1,
+				      cfgp2, GET_RAID_VOLUME_PG1);
+	     cfgp2.handle = hdl) {
+		hdl = le16_to_cpu(vol_p1->dev_hdl);
+		raid_volume = leapraid_raid_volume_find_by_wwid(
+				adapter,
+				le64_to_cpu(vol_p1->wwid));
+		if (raid_volume) {
+			leapraid_raid_volume_put(raid_volume);
+			continue;
+		}
+
+		cfgp1_extra.size = sizeof(struct leapraid_raidvol_p0);
+		cfgp2_extra.handle = hdl;
+		if (leapraid_op_config_page(adapter, vol_p0, cfgp1_extra,
+					    cfgp2_extra, GET_RAID_VOLUME_PG0))
+			continue;
+
+		if (vol_p0->volume_state == LEAPRAID_VOL_STATE_OPTIMAL ||
+		    vol_p0->volume_state == LEAPRAID_VOL_STATE_ONLINE ||
+		    vol_p0->volume_state == LEAPRAID_VOL_STATE_DEGRADED) {
+			memset(&evt_data, 0,
+			       sizeof(struct leapraid_evt_data_ir_change));
+			evt_data.reason_code = LEAPRAID_EVT_IR_RC_VOLUME_ADD;
+			evt_data.vol_dev_hdl = vol_p1->dev_hdl;
+			leapraid_sas_volume_add(adapter, &evt_data);
+			dev_info(&adapter->pdev->dev,
+				 "add volume: hdl=0x%04x\n",
+				 vol_p1->dev_hdl);
+		}
+	}
+
+	kfree(vol_p0);
+	kfree(vol_p1);
+}
+
+static void leapraid_scan_sas_dev_after_reset(struct leapraid_adapter *adapter)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_sas_dev_p0 sas_dev_p0;
+	struct leapraid_sas_dev *sas_dev;
+	u16 hdl, parent_hdl;
+	u64 sas_address;
+	u8 port_id;
+
+	cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP;
+	for (hdl = 0xFFFF, cfgp2.handle = hdl;
+	     !leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1, cfgp2,
+				      GET_SAS_DEVICE_PG0);
+	     cfgp2.handle = hdl) {
+		hdl = le16_to_cpu(sas_dev_p0.dev_hdl);
+		if (!hdl ||
+		    hdl > adapter->adapter_attr.features.max_dev_handle) {
+			dev_warn(&adapter->pdev->dev,
+				 "%s: Invalid device handle\n", __func__);
+			continue;
+		}
+
+		if (!(leapraid_is_end_dev(le32_to_cpu(sas_dev_p0.dev_info))))
+			continue;
+
+		port_id = sas_dev_p0.physical_port;
+		sas_dev = leapraid_get_sas_dev_by_addr(
+				adapter,
+				le64_to_cpu(sas_dev_p0.sas_address),
+				leapraid_get_port_by_id(
+					adapter,
+					port_id,
+					false));
+		if (sas_dev) {
+			leapraid_sdev_put(sas_dev);
+			continue;
+		}
+
+		parent_hdl = le16_to_cpu(sas_dev_p0.parent_dev_hdl);
+		if (!leapraid_get_sas_address(adapter, parent_hdl,
+					      &sas_address)) {
+			leapraid_transport_update_links(
+				adapter,
+				sas_address,
+				hdl,
+				sas_dev_p0.phy_num,
+				LEAPRAID_SAS_NEG_LINK_RATE_1_5,
+				leapraid_get_port_by_id(adapter,
+							port_id,
+							false));
+			leapraid_add_dev(adapter, hdl);
+			dev_info(&adapter->pdev->dev,
+				 "Add SAS dev: hdl=0x%04x, saddr=0x%016llx\n",
+				 hdl,
+				 (unsigned long long)le64_to_cpu(
+					sas_dev_p0.sas_address));
+		}
+	}
+}
+
+static void leapraid_scan_all_dev_after_reset(struct leapraid_adapter *adapter)
+{
+	leapraid_sas_host_add(adapter, adapter->dev_topo.card.phys_num > 0);
+	leapraid_scan_exp_after_reset(adapter);
+	if (adapter->adapter_attr.raid_support) {
+		leapraid_scan_phy_disks_after_reset(adapter);
+		leapraid_scan_vol_after_reset(adapter);
+	}
+	leapraid_scan_sas_dev_after_reset(adapter);
+}
+
+static void leapraid_hardreset_async_logic(struct leapraid_adapter *adapter)
+{
+	unsigned long flags;
+
+	leapraid_remove_unresp_dev(adapter);
+	leapraid_del_dirty_vphy(adapter);
+	leapraid_del_dirty_card_port(adapter);
+	leapraid_update_dev_qdepth(adapter);
+	leapraid_scan_all_dev_after_reset(adapter);
+
+	if (adapter->scan_dev_desc.driver_loading)
+		leapraid_scan_dev_done(adapter);
+	spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags);
+	adapter->access_ctrl.shost_recover_async = 0;
+	spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags);
+	wake_up(&adapter->access_ctrl.shost_recover_wq);
+}
+
+static int leapraid_send_enc_cmd(struct leapraid_adapter *adapter,
+				 struct leapraid_sep_rep *sep_rep,
+				 struct leapraid_sep_req *sep_req)
+{
+	void *req;
+	bool reset_flg = false;
+	int rc;
+	u16 smid;
+
+	mutex_lock(&adapter->driver_cmds.enc_cmd.mutex);
+	rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT,
+					  __func__);
+	if (rc)
+		goto unlock;
+
+	adapter->driver_cmds.enc_cmd.status = LEAPRAID_CMD_PENDING;
+	smid = adapter->driver_cmds.enc_cmd.inter_taskid;
+	req = leapraid_get_task_desc(adapter, smid);
+	memset(req, 0, LEAPRAID_REQUEST_SIZE);
+	memcpy(req, sep_req, sizeof(struct leapraid_sep_req));
+	init_completion(&adapter->driver_cmds.enc_cmd.done);
+	leapraid_fire_task(adapter, smid);
+	wait_for_completion_timeout(&adapter->driver_cmds.enc_cmd.done,
+				    LEAPRAID_ENC_CMD_TIMEOUT * HZ);
+	if (!(adapter->driver_cmds.enc_cmd.status & LEAPRAID_CMD_DONE)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: SEP command timeout, status=0x%x\n",
+			__func__, adapter->driver_cmds.enc_cmd.status);
+		leapraid_log_req_context(adapter, smid, sep_req);
+		reset_flg =
+			leapraid_check_reset(
+				adapter->driver_cmds.enc_cmd.status);
+		rc = -EFAULT;
+		goto do_hard_reset;
+	}
+
+	if (adapter->driver_cmds.enc_cmd.status & LEAPRAID_CMD_REPLY_VALID)
+		memcpy(sep_rep, &adapter->driver_cmds.enc_cmd.reply,
+		       sizeof(struct leapraid_sep_rep));
+do_hard_reset:
+	if (reset_flg) {
+		dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n",
+			 __func__, __LINE__);
+		leapraid_hard_reset_handler(adapter, FULL_RESET);
+	}
+
+	adapter->driver_cmds.enc_cmd.status = LEAPRAID_CMD_NOT_USED;
+unlock:
+	mutex_unlock(&adapter->driver_cmds.enc_cmd.mutex);
+	return rc;
+}
+
+static void leapraid_set_led(struct leapraid_adapter *adapter,
+			     struct leapraid_sas_dev *sas_dev, bool on)
+{
+	struct leapraid_sep_rep sep_rep;
+	struct leapraid_sep_req sep_req;
+
+	if (!sas_dev)
+		return;
+
+	memset(&sep_req, 0, sizeof(struct leapraid_sep_req));
+	memset(&sep_rep, 0, sizeof(struct leapraid_sep_rep));
+	sep_req.func = LEAPRAID_FUNC_SCSI_ENC_PROCESSOR;
+	sep_req.act = LEAPRAID_SEP_REQ_ACT_WRITE_STATUS;
+	if (on) {
+		u32 status = LEAPRAID_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT;
+
+		sep_req.slot_status = cpu_to_le32(status);
+		sep_req.dev_hdl = cpu_to_le16(sas_dev->hdl);
+		sep_req.flg = LEAPRAID_SEP_REQ_FLG_DEVHDL_ADDRESS;
+		if (leapraid_send_enc_cmd(adapter, &sep_rep, &sep_req)) {
+			leapraid_sdev_put(sas_dev);
+			return;
+		}
+
+		sas_dev->led_on = 1;
+		leapraid_sdev_put(sas_dev);
+	} else {
+		sep_req.slot_status = 0;
+		sep_req.slot = cpu_to_le16(sas_dev->slot);
+		sep_req.dev_hdl = 0;
+		sep_req.enc_hdl = cpu_to_le16(sas_dev->enc_hdl);
+		sep_req.flg = LEAPRAID_SEP_REQ_FLG_ENCLOSURE_SLOT_ADDRESS;
+		leapraid_send_enc_cmd(adapter, &sep_rep, &sep_req);
+	}
+}
+
+static int leapraid_wait_adapter_recovery(struct leapraid_adapter *adapter)
+{
+	unsigned long flags;
+
+	while (leapraid_shost_in_recovery(adapter->shost) ||
+	       READ_ONCE(adapter->access_ctrl.shost_recovering)) {
+		if (READ_ONCE(adapter->access_ctrl.host_removing) ||
+		    READ_ONCE(adapter->fw_evt_s.fw_evt_cleanup)) {
+			spin_lock_irqsave(
+				&adapter->reset_desc.adapter_reset_lock,
+				flags);
+			adapter->access_ctrl.shost_recover_async = 0;
+			spin_unlock_irqrestore(
+				&adapter->reset_desc.adapter_reset_lock,
+				flags);
+
+			wake_up(&adapter->access_ctrl.shost_recover_wq);
+
+			dev_warn(&adapter->pdev->dev,
+				 "%s: Failed, shost %d, host %d\n",
+				 __func__,
+				 adapter->access_ctrl.shost_recovering,
+				 adapter->access_ctrl.host_removing);
+
+			return -EFAULT;
+		}
+
+		wait_event_timeout(
+			adapter->access_ctrl.recovery_waitq,
+			(!leapraid_shost_in_recovery(adapter->shost) &&
+			 !READ_ONCE(adapter->access_ctrl.shost_recovering)),
+			msecs_to_jiffies(1000));
+	}
+
+	return 0;
+}
+
+static void leapraid_fw_work(struct leapraid_adapter *adapter,
+			     struct leapraid_fw_evt_work *fw_evt)
+{
+	struct leapraid_sas_dev *sas_dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags);
+	adapter->fw_evt_s.cur_evt = fw_evt;
+	adapter->fw_evt_s.cur_evt_task = current;
+	spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags);
+	leapraid_del_fw_evt_from_list(adapter, fw_evt);
+	if (adapter->access_ctrl.host_removing ||
+	    adapter->access_ctrl.pcie_recovering) {
+		spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags);
+		leapraid_fw_evt_put(fw_evt);
+		adapter->fw_evt_s.cur_evt = NULL;
+		adapter->fw_evt_s.cur_evt_task = NULL;
+		spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags);
+		return;
+	}
+	switch (fw_evt->evt_type) {
+	case LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST:
+		leapraid_sas_topo_chg_evt(adapter, fw_evt);
+		break;
+	case LEAPRAID_EVT_IR_CHANGE:
+		leapraid_sas_ir_chg_evt(adapter, fw_evt);
+		break;
+	case LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE:
+		leapraid_sas_enc_dev_stat_chg_evt(adapter, fw_evt);
+		break;
+	case LEAPRAID_EVT_REMOVE_DEAD_DEV:
+		if (leapraid_wait_adapter_recovery(adapter))
+			goto out_cleanup;
+
+		leapraid_hardreset_async_logic(adapter);
+		break;
+	case LEAPRAID_EVT_TURN_ON_PFA_LED:
+		sas_dev = leapraid_get_sas_dev_by_hdl(adapter,
+						      fw_evt->dev_handle);
+		leapraid_set_led(adapter, sas_dev, true);
+		break;
+	case LEAPRAID_EVT_SCAN_DEV_DONE:
+		adapter->scan_dev_desc.scan_start = 0;
+		break;
+	default:
+		break;
+	}
+out_cleanup:
+	spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags);
+	leapraid_fw_evt_put(fw_evt);
+	adapter->fw_evt_s.cur_evt = NULL;
+	adapter->fw_evt_s.cur_evt_task = NULL;
+	spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags);
+}
+
+static void leapraid_sas_dev_stat_chg_evt(
+		struct leapraid_adapter *adapter,
+		struct leapraid_evt_data_sas_dev_status_change *event_data)
+{
+	struct leapraid_starget_priv *starget_priv;
+	struct leapraid_sas_dev *sas_dev;
+	u64 sas_address;
+	unsigned long flags;
+
+	switch (event_data->reason_code) {
+	case LEAPRAID_EVT_SAS_DEV_STAT_RC_INTERNAL_DEV_RESET:
+	case LEAPRAID_EVT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET:
+		break;
+	default:
+		return;
+	}
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+
+	sas_address = le64_to_cpu(event_data->sas_address);
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_addr(
+			adapter,
+			sas_address,
+			leapraid_get_port_by_id(adapter,
+						event_data->physical_port,
+						false));
+	if (!sas_dev || !sas_dev->starget)
+		goto out_unlock;
+
+	starget_priv = sas_dev->starget->hostdata;
+	if (starget_priv) {
+		switch (event_data->reason_code) {
+		case LEAPRAID_EVT_SAS_DEV_STAT_RC_INTERNAL_DEV_RESET:
+			starget_priv->tm_busy = 1;
+			break;
+		case LEAPRAID_EVT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET:
+			starget_priv->tm_busy = 0;
+			break;
+		}
+	}
+
+out_unlock:
+	if (sas_dev)
+		leapraid_sdev_put(sas_dev);
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+}
+
+static void leapraid_set_volume_delete_flag(struct leapraid_adapter *adapter,
+					    u16 handle)
+{
+	struct leapraid_raid_volume *raid_volume;
+	struct leapraid_starget_priv *sas_target_priv_data;
+	unsigned long flags;
+
+	raid_volume = leapraid_raid_volume_find_by_hdl(adapter, handle);
+	if (raid_volume) {
+		spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+		if (raid_volume->starget &&
+		    raid_volume->starget->hostdata) {
+			sas_target_priv_data = raid_volume->starget->hostdata;
+			sas_target_priv_data->deleted = 1;
+		}
+		spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock,
+				       flags);
+		leapraid_raid_volume_put(raid_volume);
+	}
+}
+
+static void leapraid_check_ir_change_evt(
+		struct leapraid_adapter *adapter,
+		struct leapraid_evt_data_ir_change *evt_data)
+{
+	u16 phys_disk_dev_hdl;
+
+	switch (evt_data->reason_code) {
+	case LEAPRAID_EVT_IR_RC_VOLUME_DELETE:
+		leapraid_set_volume_delete_flag(
+			adapter,
+			le16_to_cpu(evt_data->vol_dev_hdl));
+		break;
+	case LEAPRAID_EVT_IR_RC_PD_UNHIDDEN_TO_DELETE:
+		phys_disk_dev_hdl =
+			le16_to_cpu(evt_data->phys_disk_dev_hdl);
+		if (!phys_disk_dev_hdl ||
+		    phys_disk_dev_hdl >
+		    adapter->adapter_attr.features.max_dev_handle) {
+			dev_warn(&adapter->pdev->dev,
+				 "%s: Invalid device handle\n", __func__);
+		} else {
+			clear_bit(phys_disk_dev_hdl,
+				  adapter->dev_topo.pd_hdls);
+			leapraid_tgt_rst_send(adapter, phys_disk_dev_hdl);
+		}
+		break;
+	}
+}
+
+static void leapraid_topo_del_evts_process_exp_status(
+		struct leapraid_adapter *adapter,
+		struct leapraid_evt_data_sas_topo_change_list *evt_data)
+{
+	struct leapraid_fw_evt_work *fw_evt = NULL;
+	struct leapraid_evt_data_sas_topo_change_list *loc_evt_data;
+	unsigned long flags;
+	u16 exp_hdl;
+
+	exp_hdl = le16_to_cpu(evt_data->exp_dev_hdl);
+
+	switch (evt_data->exp_status) {
+	case LEAPRAID_EVT_SAS_TOPO_ES_NOT_RESPONDING:
+		spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags);
+		list_for_each_entry(fw_evt,
+				    &adapter->fw_evt_s.fw_evt_list, list) {
+			if (fw_evt->evt_type !=
+			    LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST ||
+			    fw_evt->ignore)
+				continue;
+
+			loc_evt_data = fw_evt->evt_data;
+			if ((loc_evt_data->exp_status ==
+			     LEAPRAID_EVT_SAS_TOPO_ES_ADDED ||
+			     loc_evt_data->exp_status ==
+			     LEAPRAID_EVT_SAS_TOPO_ES_RESPONDING) &&
+			    le16_to_cpu(loc_evt_data->exp_dev_hdl) == exp_hdl)
+				fw_evt->ignore = 1;
+		}
+		spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags);
+		break;
+	default:
+		break;
+	}
+}
+
+static void leapraid_check_topo_del_evts(
+		struct leapraid_adapter *adapter,
+		struct leapraid_evt_data_sas_topo_change_list *evt_data)
+{
+	int reason_code;
+	u16 hdl;
+	int i;
+
+	for (i = 0; i < evt_data->entry_num; i++) {
+		hdl = le16_to_cpu(evt_data->phy[i].attached_dev_hdl);
+		if (!hdl)
+			continue;
+
+		reason_code = evt_data->phy[i].phy_status &
+				LEAPRAID_EVT_SAS_TOPO_RC_MASK;
+		if (reason_code ==
+		    LEAPRAID_EVT_SAS_TOPO_RC_TARG_NOT_RESPONDING)
+			leapraid_tgt_not_responding(adapter, hdl);
+	}
+	leapraid_topo_del_evts_process_exp_status(adapter, evt_data);
+}
+
+static bool leapraid_async_evt_validate(
+		struct leapraid_adapter *adapter,
+		struct leapraid_evt_notify_rep *event_notify_rep)
+{
+	size_t msg_len;
+	size_t evt_sz;
+	size_t evt_avail;
+	u16 evt;
+
+	msg_len = event_notify_rep->msg_len * sizeof(u32);
+	if (msg_len > LEAPRAID_REPLY_SIZE ||
+	    msg_len < offsetof(struct leapraid_evt_notify_rep, evt_data)) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Invalid async event msg_len=%zu\n",
+			 __func__, msg_len);
+		return false;
+	}
+
+	evt_sz = le16_to_cpu(event_notify_rep->evt_data_len) * sizeof(u32);
+	evt_avail = msg_len -
+		    offsetof(struct leapraid_evt_notify_rep, evt_data);
+	if (evt_sz > evt_avail) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Invalid async event evt_data_len=%zu\n",
+			 __func__, evt_sz);
+		return false;
+	}
+
+	evt = le16_to_cpu(event_notify_rep->evt);
+	switch (evt) {
+	case LEAPRAID_EVT_SAS_DEV_STATUS_CHANGE:
+		if (evt_sz <
+		    sizeof(struct leapraid_evt_data_sas_dev_status_change))
+			goto invalid_evt_sz;
+		break;
+	case LEAPRAID_EVT_IR_CHANGE:
+		if (evt_sz < sizeof(struct leapraid_evt_data_ir_change))
+			goto invalid_evt_sz;
+		break;
+	case LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST:
+	{
+		struct leapraid_evt_data_sas_topo_change_list *evt_data =
+			(void *)event_notify_rep->evt_data;
+		size_t hdr_sz;
+
+		hdr_sz =
+			offsetof(struct leapraid_evt_data_sas_topo_change_list,
+				 phy);
+		if (evt_sz < hdr_sz)
+			goto invalid_evt_sz;
+
+		if (evt_data->entry_num >
+		    (evt_sz - hdr_sz) / sizeof(evt_data->phy[0]))
+			goto invalid_evt_sz;
+		break;
+	}
+	case LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE:
+		if (evt_sz <
+		    sizeof(struct leapraid_evt_data_sas_enc_dev_status_change))
+			goto invalid_evt_sz;
+		break;
+	default:
+		break;
+	}
+
+	return true;
+
+invalid_evt_sz:
+	dev_warn(&adapter->pdev->dev,
+		 "%s: Invalid async event size=%zu for evt=0x%x\n",
+		 __func__, evt_sz, evt);
+	return false;
+}
+
+static bool leapraid_async_process_evt(
+		struct leapraid_adapter *adapter,
+		struct leapraid_evt_notify_rep *event_notify_rep)
+{
+	u16 evt = le16_to_cpu(event_notify_rep->evt);
+	bool exit_flag = false;
+
+	if (adapter->access_ctrl.host_removing ||
+	    adapter->access_ctrl.pcie_recovering)
+		return true;
+
+	switch (evt) {
+	case LEAPRAID_EVT_SAS_DEV_STATUS_CHANGE:
+		leapraid_sas_dev_stat_chg_evt(
+			adapter,
+			(struct leapraid_evt_data_sas_dev_status_change
+				*)event_notify_rep->evt_data);
+		break;
+	case LEAPRAID_EVT_IR_CHANGE:
+		leapraid_check_ir_change_evt(
+			adapter,
+			(struct leapraid_evt_data_ir_change
+				*)event_notify_rep->evt_data);
+		break;
+	case LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST:
+		leapraid_check_topo_del_evts(
+			adapter,
+			(struct leapraid_evt_data_sas_topo_change_list
+				*)event_notify_rep->evt_data);
+		if (adapter->access_ctrl.shost_recovering) {
+			exit_flag = true;
+			return exit_flag;
+		}
+		break;
+	case LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE:
+		break;
+	default:
+		exit_flag = true;
+		return exit_flag;
+	}
+
+	return exit_flag;
+}
+
+static void leapraid_async_evt_cb_enqueue(
+		struct leapraid_adapter *adapter,
+		struct leapraid_evt_notify_rep *evt_notify_rep)
+{
+	struct leapraid_fw_evt_work *fw_evt;
+	u16 evt_sz;
+
+	fw_evt = leapraid_alloc_fw_evt_work();
+	if (!fw_evt)
+		return;
+
+	evt_sz = le16_to_cpu(evt_notify_rep->evt_data_len) * sizeof(u32);
+	fw_evt->evt_data = kmemdup(evt_notify_rep->evt_data,
+				   evt_sz, GFP_ATOMIC);
+	if (!fw_evt->evt_data) {
+		leapraid_fw_evt_put(fw_evt);
+		return;
+	}
+	fw_evt->adapter = adapter;
+	fw_evt->evt_type = le16_to_cpu(evt_notify_rep->evt);
+	leapraid_fw_evt_add(adapter, fw_evt);
+	leapraid_fw_evt_put(fw_evt);
+}
+
+static void leapraid_async_evt_cb(struct leapraid_adapter *adapter,
+				  u8 msix_index, u32 rep_paddr)
+{
+	struct leapraid_evt_notify_rep *evt_notify_rep;
+
+	if (adapter->access_ctrl.host_removing ||
+	    adapter->access_ctrl.pcie_recovering)
+		return;
+
+	evt_notify_rep = leapraid_get_reply_vaddr(adapter, rep_paddr);
+	if (unlikely(!evt_notify_rep))
+		return;
+
+	if (!leapraid_async_evt_validate(adapter, evt_notify_rep))
+		return;
+
+	if (leapraid_async_process_evt(adapter, evt_notify_rep))
+		return;
+
+	leapraid_async_evt_cb_enqueue(adapter, evt_notify_rep);
+}
+
+static void leapraid_handle_async_event(struct leapraid_adapter *adapter,
+					u8 msix_index, u32 reply)
+{
+	struct leapraid_evt_notify_rep *leap_mpi_rep =
+		leapraid_get_reply_vaddr(adapter, reply);
+
+	if (!leap_mpi_rep)
+		return;
+
+	if (leap_mpi_rep->func != LEAPRAID_FUNC_EVENT_NOTIFY)
+		return;
+
+	leapraid_async_evt_cb(adapter, msix_index, reply);
+}
+
+void leapraid_async_turn_on_led(struct leapraid_adapter *adapter, u16 handle)
+{
+	struct leapraid_fw_evt_work *fw_event;
+
+	fw_event = leapraid_alloc_fw_evt_work();
+	if (!fw_event)
+		return;
+
+	fw_event->dev_handle = handle;
+	fw_event->adapter = adapter;
+	fw_event->evt_type = LEAPRAID_EVT_TURN_ON_PFA_LED;
+	leapraid_fw_evt_add(adapter, fw_event);
+	leapraid_fw_evt_put(fw_event);
+}
+
+static void leapraid_hardreset_barrier(struct leapraid_adapter *adapter)
+{
+	struct leapraid_fw_evt_work *fw_event;
+
+	fw_event = leapraid_alloc_fw_evt_work();
+	if (!fw_event)
+		return;
+
+	fw_event->adapter = adapter;
+	fw_event->evt_type = LEAPRAID_EVT_REMOVE_DEAD_DEV;
+	leapraid_fw_evt_add(adapter, fw_event);
+	leapraid_fw_evt_put(fw_event);
+}
+
+static void leapraid_scan_dev_complete(struct leapraid_adapter *adapter)
+{
+	struct leapraid_fw_evt_work *fw_evt;
+
+	fw_evt = leapraid_alloc_fw_evt_work();
+	if (!fw_evt)
+		return;
+
+	fw_evt->evt_type = LEAPRAID_EVT_SCAN_DEV_DONE;
+	fw_evt->adapter = adapter;
+	leapraid_fw_evt_add(adapter, fw_evt);
+	leapraid_fw_evt_put(fw_evt);
+}
+
+static void leapraid_handle_scan_cb(struct leapraid_adapter *adapter,
+				    struct leapraid_driver_cmd *cmd,
+				    struct leapraid_rep *rep)
+{
+	u16 status;
+
+	cmd->status &= ~LEAPRAID_CMD_PENDING;
+
+	status = le16_to_cpu(rep->adapter_status) &
+		 LEAPRAID_ADAPTER_STATUS_MASK;
+	if (status != LEAPRAID_ADAPTER_STATUS_SUCCESS)
+		adapter->scan_dev_desc.scan_dev_failed = 1;
+
+	if (!cmd->async_scan_dev) {
+		complete(&cmd->done);
+		return;
+	}
+
+	if (status == LEAPRAID_ADAPTER_STATUS_SUCCESS)
+		leapraid_scan_dev_complete(adapter);
+	else
+		adapter->scan_dev_desc.scan_start_failed = status;
+}
+
+static void leapraid_handle_ctl_cb(struct leapraid_adapter *adapter,
+				   struct leapraid_rep *rep,
+				   u16 taskid)
+{
+	struct leapraid_scsiio_rep *scsiio_reply;
+
+	if (rep->function != LEAPRAID_FUNC_SCSIIO &&
+	    rep->function != LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH)
+		return;
+
+	scsiio_reply = (struct leapraid_scsiio_rep *)rep;
+	if (!(scsiio_reply->scsi_state &
+	      LEAPRAID_SCSI_STATE_AUTOSENSE_VALID))
+		return;
+
+	memcpy(&adapter->driver_cmds.ctl_cmd.sense,
+	       leapraid_get_sense_buffer(adapter, taskid),
+	       min_t(u32, SCSI_SENSE_BUFFERSIZE,
+		     le32_to_cpu(scsiio_reply->sense_count)));
+}
+
+static bool leapraid_driver_cmds_done(struct leapraid_adapter *adapter,
+				      u16 taskid, u8 msix_index,
+				      u32 rep_paddr, u8 cb_idx)
+{
+	struct leapraid_rep *leap_mpi_rep =
+		leapraid_get_reply_vaddr(adapter, rep_paddr);
+	struct leapraid_driver_cmd *sp_cmd, *_sp_cmd = NULL;
+	u8 reply_len;
+
+	list_for_each_entry(sp_cmd, &adapter->driver_cmds.special_cmd_list,
+			    list)
+		if (cb_idx == sp_cmd->cb_idx) {
+			_sp_cmd = sp_cmd;
+			break;
+		}
+
+	if (WARN_ON(!_sp_cmd))
+		return true;
+	if (WARN_ON(_sp_cmd->status == LEAPRAID_CMD_NOT_USED))
+		return true;
+	if (WARN_ON(taskid != _sp_cmd->hp_taskid &&
+		    taskid != _sp_cmd->taskid &&
+		    taskid != _sp_cmd->inter_taskid))
+		return true;
+	_sp_cmd->status |= LEAPRAID_CMD_DONE;
+	if (leap_mpi_rep) {
+		reply_len = leap_mpi_rep->msg_len * sizeof(u32);
+		if (reply_len > LEAPRAID_REPLY_SIZE)
+			reply_len = LEAPRAID_REPLY_SIZE;
+		memcpy(&_sp_cmd->reply, leap_mpi_rep, reply_len);
+		_sp_cmd->status |= LEAPRAID_CMD_REPLY_VALID;
+
+		if (_sp_cmd->cb_idx == LEAPRAID_SCAN_DEV_CB_IDX) {
+			leapraid_handle_scan_cb(adapter, _sp_cmd,
+						leap_mpi_rep);
+			return true;
+		}
+
+		if (_sp_cmd->cb_idx == LEAPRAID_CTL_CB_IDX)
+			leapraid_handle_ctl_cb(adapter, leap_mpi_rep, taskid);
+	}
+
+	_sp_cmd->status &= ~LEAPRAID_CMD_PENDING;
+	complete(&_sp_cmd->done);
+
+	return true;
+}
+
+static void leapraid_complete_task(struct leapraid_adapter *adapter,
+				   u16 taskid, u8 msix_idx, u32 rep)
+{
+	bool scsiio_task = taskid <= adapter->shost->can_queue;
+
+	if (scsiio_task) {
+		if (leapraid_scsiio_done(adapter, taskid, msix_idx, rep))
+			leapraid_free_taskid(adapter, taskid);
+		return;
+	}
+
+	if (leapraid_driver_cmds_done(adapter, taskid, msix_idx, rep,
+				      leapraid_get_cb_idx(adapter, taskid)))
+		leapraid_free_taskid(adapter, taskid);
+}
+
+static void leapraid_request_descript_handler(
+		struct leapraid_adapter *adapter,
+		union leapraid_rep_desc_union *rpf,
+		u8 req_desc_type, u8 msix_idx)
+{
+	u32 rep;
+	u16 taskid;
+
+	rep = 0;
+	taskid = le16_to_cpu(rpf->dflt_rep.taskid);
+	switch (req_desc_type) {
+	case LEAPRAID_RPY_DESC_FLG_FP_SCSI_IO_SUCCESS:
+	case LEAPRAID_RPY_DESC_FLG_SCSI_IO_SUCCESS:
+		leapraid_complete_task(adapter, taskid, msix_idx, 0);
+		break;
+	case LEAPRAID_RPY_DESC_FLG_ADDRESS_REPLY:
+		rep = le32_to_cpu(rpf->addr_rep.rep_frame_addr);
+		if (rep > (u32)adapter->mem_desc.rep_msg_dma +
+			   adapter->adapter_attr.rep_msg_qd *
+			   LEAPRAID_REPLY_SIZE ||
+		    rep < (u32)adapter->mem_desc.rep_msg_dma)
+			rep = 0;
+
+		if (taskid)
+			leapraid_complete_task(adapter, taskid, msix_idx, rep);
+		else
+			leapraid_handle_async_event(adapter, msix_idx, rep);
+
+		if (!rep)
+			return;
+
+		adapter->rep_msg_host_idx =
+			(adapter->rep_msg_host_idx ==
+				(adapter->adapter_attr.rep_msg_qd - 1)) ?
+					0 : adapter->rep_msg_host_idx + 1;
+		adapter->mem_desc.rep_msg_addr[adapter->rep_msg_host_idx] =
+			cpu_to_le32(rep);
+		wmb(); /* Make sure that all write ops are in order */
+		writel(adapter->rep_msg_host_idx,
+		       &adapter->iomem_base->rep_msg_host_idx);
+
+		break;
+	default:
+		break;
+	}
+}
+
+int leapraid_rep_queue_handler(struct leapraid_rq *rq)
+{
+	struct leapraid_adapter *adapter = rq->adapter;
+	union leapraid_rep_desc_union *rep_desc;
+	u8 req_desc_type;
+	u64 finish_cmds;
+	u8 msix_idx;
+
+	msix_idx = rq->msix_idx;
+	finish_cmds = 0;
+	if (!atomic_add_unless(&rq->busy, LEAPRAID_BUSY_LIMIT,
+			       LEAPRAID_BUSY_LIMIT))
+		return finish_cmds;
+
+	rep_desc = &rq->rep_desc[rq->rep_post_host_idx];
+	req_desc_type = rep_desc->dflt_rep.rep_flg &
+				LEAPRAID_RPY_DESC_FLG_TYPE_MASK;
+	if (req_desc_type == LEAPRAID_RPY_DESC_FLG_UNUSED) {
+		atomic_dec(&rq->busy);
+		return finish_cmds;
+	}
+
+	for (;;) {
+		if (rep_desc->u.low == UINT_MAX ||
+		    rep_desc->u.high == UINT_MAX)
+			break;
+
+		leapraid_request_descript_handler(adapter, rep_desc,
+						  req_desc_type, msix_idx);
+		dev_dbg(&adapter->pdev->dev,
+			"LEAPRAID_SCSIIO: Handled Desc taskid %d, msix %d\n",
+			rep_desc->dflt_rep.taskid, msix_idx);
+		rep_desc->words = cpu_to_le64(ULLONG_MAX);
+		rq->rep_post_host_idx =
+			(rq->rep_post_host_idx ==
+				(adapter->adapter_attr.rep_desc_qd -
+					LEAPRAID_BUSY_LIMIT)) ?
+					0 : rq->rep_post_host_idx + 1;
+		req_desc_type =
+			rq->rep_desc[rq->rep_post_host_idx].dflt_rep.rep_flg &
+					LEAPRAID_RPY_DESC_FLG_TYPE_MASK;
+		finish_cmds++;
+		if (req_desc_type == LEAPRAID_RPY_DESC_FLG_UNUSED)
+			break;
+		rep_desc = rq->rep_desc + rq->rep_post_host_idx;
+	}
+
+	if (!finish_cmds) {
+		atomic_dec(&rq->busy);
+		return finish_cmds;
+	}
+
+	wmb(); /* Make sure that all write ops are in order */
+	writel(rq->rep_post_host_idx | ((msix_idx & LEAPRAID_MSIX_GROUP_MASK) <<
+					LEAPRAID_RPHI_MSIX_IDX_SHIFT),
+	       &adapter->iomem_base->rep_post_reg_idx[
+		       msix_idx / LEAPRAID_MSIX_GROUP_SIZE].idx);
+	atomic_dec(&rq->busy);
+	return finish_cmds;
+}
+
+static irqreturn_t leapraid_irq_handler(int irq, void *bus_id)
+{
+	struct leapraid_rq *rq = bus_id;
+	struct leapraid_adapter *adapter = rq->adapter;
+
+	dev_dbg(&adapter->pdev->dev,
+		"LEAPRAID_SCSIIO: Receive a interrupt, irq %d msix %d\n",
+		irq, rq->msix_idx);
+
+	if (adapter->mask_int)
+		return IRQ_NONE;
+
+	return (leapraid_rep_queue_handler(rq) > 0 ?
+		 IRQ_HANDLED : IRQ_NONE);
+}
+
+void leapraid_sync_irqs(struct leapraid_adapter *adapter, bool poll)
+{
+	struct leapraid_int_rq *int_rq;
+	struct leapraid_blk_mq_poll_rq *blk_mq_poll_rq;
+	unsigned int i;
+
+	if (!adapter->notification_desc.msix_enable)
+		return;
+
+	if (adapter->access_ctrl.shost_recovering ||
+	    adapter->access_ctrl.host_removing ||
+	    adapter->access_ctrl.pcie_recovering)
+		return;
+
+	for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) {
+		int_rq = &adapter->notification_desc.int_rqs[i];
+		if (adapter->access_ctrl.shost_recovering ||
+		    adapter->access_ctrl.host_removing ||
+		    adapter->access_ctrl.pcie_recovering)
+			return;
+
+		if (int_rq->rq.msix_idx == 0)
+			continue;
+
+		synchronize_irq(pci_irq_vector(adapter->pdev,
+					       int_rq->rq.msix_idx));
+		if (poll)
+			leapraid_rep_queue_handler(&int_rq->rq);
+	}
+
+	for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) {
+		blk_mq_poll_rq =
+			&adapter->notification_desc.blk_mq_poll_rqs[i];
+		if (adapter->access_ctrl.shost_recovering ||
+		    adapter->access_ctrl.host_removing ||
+		    adapter->access_ctrl.pcie_recovering)
+			return;
+
+		if (blk_mq_poll_rq->rq.msix_idx == 0)
+			continue;
+
+		leapraid_rep_queue_handler(&blk_mq_poll_rq->rq);
+	}
+}
+
+static void leapraid_sync_irqs_for_cleanup(struct leapraid_adapter *adapter)
+{
+	struct leapraid_notification_desc *desc = &adapter->notification_desc;
+	struct leapraid_int_rq *int_rq;
+	unsigned int i;
+
+	if (!adapter->mask_int && leapraid_pci_active(adapter))
+		leapraid_mask_int(adapter);
+
+	for (i = 0; i < desc->int_rqs_allocated; i++) {
+		int_rq = &desc->int_rqs[i];
+		synchronize_irq(pci_irq_vector(adapter->pdev,
+					       int_rq->rq.msix_idx));
+	}
+}
+
+void leapraid_mq_polling_pause(struct leapraid_adapter *adapter)
+{
+	struct leapraid_notification_desc *desc;
+	int iopoll_q_count;
+	int qid;
+
+	desc = &adapter->notification_desc;
+	iopoll_q_count = adapter->adapter_attr.rq_cnt - desc->iopoll_qdex;
+
+	for (qid = 0; qid < iopoll_q_count; qid++)
+		atomic_set(&desc->blk_mq_poll_rqs[qid].pause, 1);
+
+	for (qid = 0; qid < iopoll_q_count; qid++) {
+		while (atomic_read(&desc->blk_mq_poll_rqs[qid].busy)) {
+			cpu_relax();
+			udelay(LEAPRAID_IO_POLL_DELAY_US);
+		}
+	}
+}
+
+void leapraid_mq_polling_resume(struct leapraid_adapter *adapter)
+{
+	struct leapraid_notification_desc *desc;
+	int iopoll_q_count;
+	int qid;
+
+	desc = &adapter->notification_desc;
+	iopoll_q_count = adapter->adapter_attr.rq_cnt - desc->iopoll_qdex;
+
+	for (qid = 0; qid < iopoll_q_count; qid++)
+		atomic_set(&desc->blk_mq_poll_rqs[qid].pause, 0);
+}
+
+static int leapraid_unlock_host_diag(struct leapraid_adapter *adapter,
+				     u32 *host_diag)
+{
+	const u32 unlock_seq[] = {
+		LEAPRAID_WRSEQ_FLUSH_KEY_VALUE,
+		LEAPRAID_WRSEQ_1ST_KEY_VALUE,
+		LEAPRAID_WRSEQ_2ND_KEY_VALUE,
+		LEAPRAID_WRSEQ_3RD_KEY_VALUE,
+		LEAPRAID_WRSEQ_4TH_KEY_VALUE,
+		LEAPRAID_WRSEQ_5TH_KEY_VALUE,
+		LEAPRAID_WRSEQ_6TH_KEY_VALUE
+	};
+
+	const int max_retries = LEAPRAID_UNLOCK_RETRY_LIMIT;
+	int retry = 0;
+	unsigned int i;
+
+	*host_diag = 0;
+	while (retry++ <= max_retries) {
+		for (i = 0; i < ARRAY_SIZE(unlock_seq); i++)
+			writel(unlock_seq[i], &adapter->iomem_base->ws);
+
+		msleep(LEAPRAID_UNLOCK_SLEEP_MS);
+
+		*host_diag = leapraid_readl(&adapter->iomem_base->host_diag);
+		if (*host_diag & LEAPRAID_DIAG_WRITE_ENABLE)
+			return 0;
+	}
+
+	dev_err(&adapter->pdev->dev, "Try host reset timeout!\n");
+	return -EFAULT;
+}
+
+static int leapraid_host_diag_reset(struct leapraid_adapter *adapter)
+{
+	u32 host_diag;
+
+	pci_cfg_access_lock(adapter->pdev);
+
+	mutex_lock(&adapter->reset_desc.host_diag_mutex);
+	if (leapraid_unlock_host_diag(adapter, &host_diag))
+		goto out_cleanup;
+
+	writel(host_diag | LEAPRAID_DIAG_RESET,
+	       &adapter->iomem_base->host_diag);
+
+	msleep(LEAPRAID_MSLEEP_EXTRA_LONG_MS);
+	msleep(LEAPRAID_MSLEEP_NORMAL_MS);
+
+	host_diag = leapraid_readl(&adapter->iomem_base->host_diag);
+	if (host_diag == LEAPRAID_INVALID_HOST_DIAG_VAL ||
+	    host_diag & LEAPRAID_DIAG_RESET)
+		goto out_cleanup;
+
+	writel(0x0, &adapter->iomem_base->ws);
+	mutex_unlock(&adapter->reset_desc.host_diag_mutex);
+	if (!leapraid_wait_adapter_ready(adapter))
+		goto out_failed;
+
+	pci_cfg_access_unlock(adapter->pdev);
+	return 0;
+
+out_cleanup:
+	mutex_unlock(&adapter->reset_desc.host_diag_mutex);
+out_failed:
+	pci_cfg_access_unlock(adapter->pdev);
+	dev_err(&adapter->pdev->dev, "Host diag failed\n");
+	return -EFAULT;
+}
+
+static int leapraid_find_matching_port(
+		struct leapraid_card_port *card_port_table,
+		u8 count, u8 port_id, u64 sas_addr)
+{
+	int i;
+
+	for (i = 0; i < count; i++)
+		if (card_port_table[i].port_id == port_id &&
+		    card_port_table[i].sas_address == sas_addr)
+			return i;
+
+	return LEAPRAID_INVALID_INDEX;
+}
+
+static u8 leapraid_fill_card_port_table(
+		struct leapraid_adapter *adapter,
+		struct leapraid_sas_io_unit_p0 *sas_iounit_p0,
+		struct leapraid_card_port *new_card_port_table)
+{
+	u8 port_entry_num = 0, port_id;
+	u16 attached_hdl;
+	u64 attached_sas_addr;
+	int i, idx;
+
+	for (i = 0; i < adapter->dev_topo.card.phys_num; i++) {
+		if (sas_iounit_p0->phy_info[i].neg_link_rate >>
+		    LEAPRAID_SAS_NEG_LINK_RATE_SHIFT <
+		    LEAPRAID_SAS_NEG_LINK_RATE_1_5)
+			continue;
+
+		attached_hdl = le16_to_cpu(sas_iounit_p0->phy_info[i]
+					   .attached_dev_hdl);
+		if (leapraid_get_sas_address(adapter,
+					     attached_hdl,
+					     &attached_sas_addr) != 0)
+			continue;
+
+		port_id = sas_iounit_p0->phy_info[i].port;
+
+		idx = leapraid_find_matching_port(new_card_port_table,
+						  port_entry_num,
+						  port_id,
+						  attached_sas_addr);
+		if (idx >= 0) {
+			new_card_port_table[idx].phy_mask |= BIT(i);
+		} else {
+			new_card_port_table[port_entry_num].port_id = port_id;
+			new_card_port_table[port_entry_num].phy_mask = BIT(i);
+			new_card_port_table[port_entry_num].sas_address =
+				attached_sas_addr;
+			port_entry_num++;
+		}
+	}
+
+	return port_entry_num;
+}
+
+static u8 leapraid_set_new_card_port_table_after_reset(
+		struct leapraid_adapter *adapter,
+		struct leapraid_card_port *new_card_port_table)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_sas_io_unit_p0 *sas_iounit_p0;
+	u8 port_entry_num = 0;
+	u16 sz;
+
+	sz = offsetof(struct leapraid_sas_io_unit_p0, phy_info) +
+		(adapter->dev_topo.card.phys_num *
+		 sizeof(struct leapraid_sas_io_unit0_phy_info));
+	sas_iounit_p0 = kzalloc(sz, GFP_KERNEL);
+	if (!sas_iounit_p0)
+		return port_entry_num;
+
+	cfgp1.size = sz;
+	if (leapraid_op_config_page(adapter, sas_iounit_p0, cfgp1, cfgp2,
+				    GET_SAS_IOUNIT_PG0) != 0)
+		goto out_free;
+
+	port_entry_num = leapraid_fill_card_port_table(adapter,
+						       sas_iounit_p0,
+						       new_card_port_table);
+out_free:
+	kfree(sas_iounit_p0);
+	return port_entry_num;
+}
+
+static void leapraid_update_existing_port(struct leapraid_adapter *adapter,
+					  struct leapraid_card_port *new_table,
+					  int entry_idx, int port_entry_num)
+{
+	struct leapraid_card_port *matched_card_port;
+	int matched_code;
+	int count, lcount = 0;
+	u64 sas_addr;
+	int i;
+
+	matched_code = leapraid_check_card_port(adapter,
+						&new_table[entry_idx],
+						&matched_card_port,
+						&count);
+
+	if (!matched_card_port)
+		return;
+
+	if (matched_code == SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS ||
+	    matched_code == SAME_ADDR_WITH_PARTIALLY_CHANGED_PHYS) {
+		leapraid_add_or_del_phys_from_existing_port(adapter,
+							    matched_card_port,
+							    new_table,
+							    entry_idx,
+							    port_entry_num);
+	} else if (matched_code == SAME_ADDR_ONLY) {
+		sas_addr = new_table[entry_idx].sas_address;
+		for (i = 0; i < port_entry_num; i++)
+			if (new_table[i].sas_address == sas_addr)
+				lcount++;
+
+		if (count > 1 || lcount > 1)
+			return;
+
+		leapraid_add_or_del_phys_from_existing_port(adapter,
+							    matched_card_port,
+							    new_table,
+							    entry_idx,
+							    port_entry_num);
+	}
+
+	if (matched_card_port->port_id != new_table[entry_idx].port_id)
+		matched_card_port->port_id = new_table[entry_idx].port_id;
+
+	matched_card_port->flg &= ~LEAPRAID_CARD_PORT_FLG_DIRTY;
+	matched_card_port->phy_mask = new_table[entry_idx].phy_mask;
+}
+
+static void leapraid_update_card_port_after_reset(
+		struct leapraid_adapter *adapter)
+{
+	struct leapraid_card_port *new_card_port_table;
+	struct leapraid_card_port *matched_card_port;
+	u8 port_entry_num;
+	u8 nr_phys;
+	int i;
+
+	if (leapraid_get_adapter_phys(adapter, &nr_phys) || !nr_phys)
+		return;
+
+	if (!adapter->dev_topo.card.card_phy) {
+		adapter->dev_topo.card.card_phy =
+			kcalloc(nr_phys, sizeof(struct leapraid_card_phy),
+				GFP_KERNEL);
+		if (!adapter->dev_topo.card.card_phy)
+			return;
+	}
+
+	adapter->dev_topo.card.phys_num = nr_phys;
+
+	new_card_port_table = kcalloc(adapter->dev_topo.card.phys_num,
+				      sizeof(struct leapraid_card_port),
+				      GFP_KERNEL);
+	if (!new_card_port_table)
+		return;
+
+	port_entry_num =
+		leapraid_set_new_card_port_table_after_reset(
+			adapter,
+			new_card_port_table);
+	if (!port_entry_num)
+		goto out_free_port_table;
+
+	list_for_each_entry(matched_card_port,
+			    &adapter->dev_topo.card_port_list, list)
+		matched_card_port->flg |= LEAPRAID_CARD_PORT_FLG_DIRTY;
+
+	matched_card_port = NULL;
+	for (i = 0; i < port_entry_num; i++)
+		leapraid_update_existing_port(adapter,
+					      new_card_port_table,
+					      i, port_entry_num);
+out_free_port_table:
+	kfree(new_card_port_table);
+}
+
+static bool leapraid_is_valid_vphy(
+		struct leapraid_adapter *adapter,
+		struct leapraid_sas_io_unit_p0 *sas_io_unit_p0,
+		int phy_index)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_sas_phy_p0 phy_p0;
+	u32 dev_info;
+
+	if (sas_io_unit_p0->phy_info[phy_index].neg_link_rate >>
+	    LEAPRAID_SAS_NEG_LINK_RATE_SHIFT <
+	    LEAPRAID_SAS_NEG_LINK_RATE_1_5)
+		return false;
+
+	dev_info = le32_to_cpu(sas_io_unit_p0->phy_info[phy_index]
+			       .controller_phy_dev_info);
+	if (!(dev_info & LEAPRAID_DEVTYP_SEP))
+		return false;
+
+	cfgp1.phy_number = phy_index;
+	if (leapraid_op_config_page(adapter, &phy_p0, cfgp1, cfgp2,
+				    GET_PHY_PG0))
+		return false;
+
+	if (!(le32_to_cpu(phy_p0.phy_info) & LEAPRAID_SAS_PHYINFO_VPHY))
+		return false;
+
+	return true;
+}
+
+static void leapraid_update_vphy_binding(struct leapraid_adapter *adapter,
+					 struct leapraid_card_port *card_port,
+					 struct leapraid_vphy *vphy,
+					 int phy_index, u8 may_new_port_id,
+					 u64 attached_sas_addr)
+{
+	struct leapraid_card_port *may_new_card_port;
+	struct leapraid_sas_dev *sas_dev;
+
+	may_new_card_port = leapraid_get_port_by_id(adapter,
+						    may_new_port_id,
+						    true);
+	if (!may_new_card_port) {
+		may_new_card_port = kzalloc_obj(*may_new_card_port);
+		if (!may_new_card_port)
+			return;
+		may_new_card_port->port_id = may_new_port_id;
+		dev_err(&adapter->pdev->dev,
+			"%s: New card port %p added, port=%d\n",
+			__func__, may_new_card_port, may_new_port_id);
+		list_add_tail(&may_new_card_port->list,
+			      &adapter->dev_topo.card_port_list);
+	}
+
+	if (card_port != may_new_card_port) {
+		if (!may_new_card_port->vphys_mask)
+			INIT_LIST_HEAD(&may_new_card_port->vphys_list);
+		may_new_card_port->vphys_mask |= BIT(phy_index);
+		card_port->vphys_mask &= ~BIT(phy_index);
+		list_move(&vphy->list, &may_new_card_port->vphys_list);
+
+		sas_dev = leapraid_get_sas_dev_by_addr(adapter,
+						       attached_sas_addr,
+						       card_port);
+		if (sas_dev) {
+			sas_dev->card_port = may_new_card_port;
+			leapraid_sdev_put(sas_dev);
+		}
+	}
+
+	if (may_new_card_port->flg & LEAPRAID_CARD_PORT_FLG_DIRTY) {
+		may_new_card_port->sas_address = 0;
+		may_new_card_port->phy_mask = 0;
+		may_new_card_port->flg &= ~LEAPRAID_CARD_PORT_FLG_DIRTY;
+	}
+	vphy->flg &= ~LEAPRAID_VPHY_FLG_DIRTY;
+}
+
+static void leapraid_update_vphys_after_reset(struct leapraid_adapter *adapter)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_sas_io_unit_p0 *sas_iounit_p0;
+	struct leapraid_card_port *card_port, *card_port_next;
+	struct leapraid_vphy *vphy, *vphy_next;
+	u64 attached_sas_addr;
+	u16 sz;
+	u16 attached_hdl;
+	bool found = false;
+	u8 port_id;
+	int i;
+
+	list_for_each_entry_safe(card_port, card_port_next,
+				 &adapter->dev_topo.card_port_list, list) {
+		if (!card_port->vphys_mask)
+			continue;
+
+		list_for_each_entry_safe(vphy, vphy_next,
+					 &card_port->vphys_list, list)
+			vphy->flg |= LEAPRAID_VPHY_FLG_DIRTY;
+	}
+
+	sz = offsetof(struct leapraid_sas_io_unit_p0, phy_info) +
+		(adapter->dev_topo.card.phys_num *
+		 sizeof(struct leapraid_sas_io_unit0_phy_info));
+	sas_iounit_p0 = kzalloc(sz, GFP_KERNEL);
+	if (!sas_iounit_p0)
+		return;
+
+	cfgp1.size = sz;
+	if (leapraid_op_config_page(adapter, sas_iounit_p0, cfgp1, cfgp2,
+				    GET_SAS_IOUNIT_PG0) != 0)
+		goto out_free;
+
+	for (i = 0; i < adapter->dev_topo.card.phys_num; i++) {
+		if (!leapraid_is_valid_vphy(adapter, sas_iounit_p0, i))
+			continue;
+
+		attached_hdl =
+			le16_to_cpu(sas_iounit_p0->phy_info[i]
+					.attached_dev_hdl);
+		if (leapraid_get_sas_address(adapter, attached_hdl,
+					     &attached_sas_addr) != 0)
+			continue;
+
+		found = false;
+		card_port = NULL;
+		card_port_next = NULL;
+		list_for_each_entry_safe(card_port, card_port_next,
+					 &adapter->dev_topo.card_port_list,
+					 list) {
+			if (!card_port->vphys_mask)
+				continue;
+
+			list_for_each_entry_safe(vphy, vphy_next,
+						 &card_port->vphys_list,
+						 list) {
+				if (!(vphy->flg & LEAPRAID_VPHY_FLG_DIRTY))
+					continue;
+
+				if (vphy->sas_address != attached_sas_addr)
+					continue;
+
+				if (!(vphy->phy_mask & BIT(i)))
+					vphy->phy_mask = BIT(i);
+
+				port_id = sas_iounit_p0->phy_info[i].port;
+
+				leapraid_update_vphy_binding(adapter,
+							     card_port,
+							     vphy,
+							     i,
+							     port_id,
+							     attached_sas_addr);
+
+				found = true;
+				break;
+			}
+			if (found)
+				break;
+		}
+	}
+out_free:
+	kfree(sas_iounit_p0);
+}
+
+static void leapraid_mark_all_dev_deleted(struct leapraid_adapter *adapter)
+{
+	struct leapraid_sdev_priv *sdev_priv;
+	struct scsi_device *sdev;
+
+	shost_for_each_device(sdev, adapter->shost) {
+		sdev_priv = sdev->hostdata;
+		if (sdev_priv && sdev_priv->starget_priv)
+			sdev_priv->starget_priv->deleted = 1;
+	}
+}
+
+static void leapraid_free_enc_list(struct leapraid_adapter *adapter)
+{
+	struct leapraid_enc_node *enc_dev, *enc_dev_next;
+	LIST_HEAD(free_list);
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags);
+	list_splice_init(&adapter->dev_topo.enc_list, &free_list);
+	spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags);
+
+	list_for_each_entry_safe(enc_dev, enc_dev_next, &free_list, list) {
+		list_del(&enc_dev->list);
+		kfree(enc_dev);
+	}
+}
+
+static void leapraid_rebuild_enc_list_after_reset(
+		struct leapraid_adapter *adapter)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_enc_node *enc_node;
+	u16 enc_hdl;
+	unsigned long flags;
+	int rc;
+
+	leapraid_free_enc_list(adapter);
+
+	cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP;
+	for (enc_hdl = 0xFFFF; ; enc_hdl = le16_to_cpu(enc_node->pg0.enc_hdl)) {
+		enc_node = kzalloc_obj(*enc_node);
+		if (!enc_node)
+			return;
+
+		cfgp2.handle = enc_hdl;
+		rc = leapraid_op_config_page(adapter, &enc_node->pg0, cfgp1,
+					     cfgp2, GET_SAS_ENCLOSURE_PG0);
+		if (rc) {
+			kfree(enc_node);
+			return;
+		}
+
+		spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags);
+		list_add_tail(&enc_node->list, &adapter->dev_topo.enc_list);
+		spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags);
+	}
+}
+
+static void leapraid_mark_resp_sas_dev(struct leapraid_adapter *adapter,
+				       struct leapraid_sas_dev_p0 *sas_dev_p0)
+{
+	struct leapraid_starget_priv *starget_priv;
+	struct leapraid_enc_node *enc_node = NULL;
+	struct leapraid_card_port *card_port;
+	struct leapraid_sas_dev *sas_dev;
+	struct scsi_target *starget;
+	unsigned long flags;
+	unsigned long enc_flags = 0;
+	u16 enc_hdl;
+	u64 enc_lid = 0;
+
+	card_port = leapraid_get_port_by_id(adapter, sas_dev_p0->physical_port,
+					    false);
+	enc_hdl = le16_to_cpu(sas_dev_p0->enc_hdl);
+	if (enc_hdl) {
+		spin_lock_irqsave(&adapter->dev_topo.enc_lock, enc_flags);
+		enc_node = leapraid_enc_find_by_hdl(adapter, enc_hdl);
+		if (enc_node)
+			enc_lid = le64_to_cpu(enc_node->pg0.enc_lid);
+		spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, enc_flags);
+		if (!enc_node)
+			dev_info(&adapter->pdev->dev,
+				 "enc hdl 0x%04x has no matched enc dev\n",
+				 enc_hdl);
+	}
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_list, list) {
+		if (sas_dev->sas_addr != le64_to_cpu(sas_dev_p0->sas_address) ||
+		    sas_dev->slot != le16_to_cpu(sas_dev_p0->slot) ||
+		    sas_dev->card_port != card_port)
+			continue;
+
+		sas_dev->resp = 1;
+		starget = sas_dev->starget;
+		if (starget && starget->hostdata) {
+			starget_priv = starget->hostdata;
+			starget_priv->tm_busy = 0;
+			starget_priv->deleted = 0;
+		} else {
+			starget_priv = NULL;
+		}
+
+		if (starget) {
+			starget_printk(
+				KERN_INFO, starget,
+				"dev: hdl=0x%04x saddr=0x%016llx port_id=%d\n",
+				sas_dev->hdl,
+				(unsigned long long)sas_dev->sas_addr,
+				sas_dev->card_port->port_id);
+			if (sas_dev->enc_hdl != 0)
+				starget_printk(
+					KERN_INFO,
+					starget,
+					"enc info: enc_lid=0x%016llx slot=%d\n",
+					(unsigned long long)sas_dev->enc_lid,
+					sas_dev->slot);
+		}
+
+		if (le16_to_cpu(sas_dev_p0->flg) &
+		    LEAPRAID_SAS_DEV_P0_FLG_ENC_LEVEL_VALID) {
+			sas_dev->enc_level = sas_dev_p0->enc_level;
+			memcpy(sas_dev->connector_name,
+			       sas_dev_p0->connector_name,
+			       LEAPRAID_SAS_DEV_P0_CON_NAME_LEN);
+			sas_dev->connector_name[
+				LEAPRAID_SAS_DEV_P0_CON_NAME_LEN] = '\0';
+		} else {
+			sas_dev->enc_level = 0;
+			sas_dev->connector_name[0] = '\0';
+		}
+
+		sas_dev->enc_hdl = enc_hdl;
+		sas_dev->enc_lid = enc_lid;
+
+		if (sas_dev->hdl == le16_to_cpu(sas_dev_p0->dev_hdl))
+			goto unlock;
+
+		dev_info(&adapter->pdev->dev,
+			 "hdl changed: 0x%04x -> 0x%04x\n",
+			 sas_dev->hdl, sas_dev_p0->dev_hdl);
+		sas_dev->hdl = le16_to_cpu(sas_dev_p0->dev_hdl);
+		if (starget_priv)
+			starget_priv->hdl = le16_to_cpu(sas_dev_p0->dev_hdl);
+
+		goto unlock;
+	}
+unlock:
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+}
+
+static void leapraid_search_resp_sas_dev(struct leapraid_adapter *adapter)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_sas_dev_p0 sas_dev_p0;
+	u32 device_info;
+
+	if (list_empty(&adapter->dev_topo.sas_dev_list))
+		return;
+
+	cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP;
+	for (cfgp2.handle = 0xFFFF;
+	     !leapraid_op_config_page(adapter, &sas_dev_p0,
+				      cfgp1, cfgp2, GET_SAS_DEVICE_PG0);
+	     cfgp2.handle = le16_to_cpu(sas_dev_p0.dev_hdl)) {
+		device_info = le32_to_cpu(sas_dev_p0.dev_info);
+		if (!(leapraid_is_end_dev(device_info)))
+			continue;
+
+		leapraid_mark_resp_sas_dev(adapter, &sas_dev_p0);
+	}
+}
+
+static void leapraid_mark_resp_raid_volume(struct leapraid_adapter *adapter,
+					   u64 wwid, u16 hdl)
+{
+	struct leapraid_raid_volume *raid_volume;
+	struct leapraid_starget_priv *starget_priv = NULL;
+	struct scsi_target *starget;
+	unsigned long flags;
+	u64 volume_wwid;
+	u16 old_hdl;
+
+	raid_volume = leapraid_raid_volume_find_by_wwid(adapter, wwid);
+	if (!raid_volume)
+		return;
+
+	spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+	if (!raid_volume->starget) {
+		spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock,
+				       flags);
+		leapraid_raid_volume_put(raid_volume);
+		return;
+	}
+
+	starget = raid_volume->starget;
+	if (starget->hostdata) {
+		starget_priv = starget->hostdata;
+		starget_priv->deleted = 0;
+	}
+
+	raid_volume->resp = 1;
+	volume_wwid = raid_volume->wwid;
+	old_hdl = raid_volume->hdl;
+	if (old_hdl != hdl) {
+		raid_volume->hdl = hdl;
+		if (starget_priv)
+			starget_priv->hdl = hdl;
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags);
+
+	starget_printk(KERN_INFO, starget,
+		       "raid volume: hdl=0x%04x, wwid=0x%016llx\n",
+		       hdl, (unsigned long long)volume_wwid);
+	if (old_hdl != hdl)
+		dev_info(&adapter->pdev->dev,
+			 "hdl changed: 0x%04x -> 0x%04x\n",
+			 old_hdl, hdl);
+
+	leapraid_raid_volume_put(raid_volume);
+}
+
+static void leapraid_search_resp_raid_volume(struct leapraid_adapter *adapter)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_1 cfgp1_extra = {0};
+	union cfg_param_2 cfgp2 = {0};
+	union cfg_param_2 cfgp2_extra = {0};
+	struct leapraid_raidvol_p1 raidvol_p1;
+	struct leapraid_raidvol_p0 raidvol_p0;
+	struct leapraid_raidpd_p0 raidpd_p0;
+	unsigned long flags;
+	u16 hdl;
+	u8 phys_disk_num;
+	bool is_empty;
+
+	if (!adapter->adapter_attr.raid_support)
+		return;
+
+	spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+	is_empty = list_empty(&adapter->dev_topo.raid_volume_list);
+	spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags);
+	if (is_empty)
+		return;
+
+	cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP;
+	for (hdl = 0xFFFF, cfgp2.handle = hdl;
+	     !leapraid_op_config_page(adapter, &raidvol_p1, cfgp1, cfgp2,
+				      GET_RAID_VOLUME_PG1);
+	     cfgp2.handle = hdl) {
+		hdl = le16_to_cpu(raidvol_p1.dev_hdl);
+		cfgp1_extra.size = sizeof(struct leapraid_raidvol_p0);
+		cfgp2_extra.handle = hdl;
+		if (leapraid_op_config_page(adapter, &raidvol_p0, cfgp1_extra,
+					    cfgp2_extra, GET_RAID_VOLUME_PG0))
+			continue;
+
+		if (raidvol_p0.volume_state == LEAPRAID_VOL_STATE_OPTIMAL ||
+		    raidvol_p0.volume_state == LEAPRAID_VOL_STATE_ONLINE ||
+		    raidvol_p0.volume_state == LEAPRAID_VOL_STATE_DEGRADED)
+			leapraid_mark_resp_raid_volume(
+				adapter,
+				le64_to_cpu(raidvol_p1.wwid),
+				hdl);
+	}
+
+	memset(adapter->dev_topo.pd_hdls, 0, adapter->dev_topo.pd_hdls_sz);
+	cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP;
+	for (phys_disk_num = 0xFF, cfgp2.form_specific = phys_disk_num;
+	     !leapraid_op_config_page(adapter, &raidpd_p0, cfgp1, cfgp2,
+				      GET_PHY_DISK_PG0);
+	     cfgp2.form_specific = phys_disk_num) {
+		phys_disk_num = raidpd_p0.phys_disk_num;
+		hdl = le16_to_cpu(raidpd_p0.dev_hdl);
+		if (!hdl ||
+		    hdl > adapter->adapter_attr.features.max_dev_handle)
+			dev_warn(&adapter->pdev->dev,
+				 "%s: Invalid device handle\n", __func__);
+		else
+			set_bit(hdl, adapter->dev_topo.pd_hdls);
+	}
+}
+
+static void leapraid_mark_resp_exp(struct leapraid_adapter *adapter,
+				   struct leapraid_exp_p0 *exp_pg0)
+{
+	struct leapraid_enc_node *enc_node = NULL;
+	struct leapraid_topo_node *topo_node_exp;
+	u16 enc_hdl = le16_to_cpu(exp_pg0->enc_hdl);
+	u64 sas_address = le64_to_cpu(exp_pg0->sas_address);
+	u16 hdl = le16_to_cpu(exp_pg0->dev_hdl);
+	u8 port_id = exp_pg0->physical_port;
+	struct leapraid_card_port *card_port = leapraid_get_port_by_id(adapter,
+								       port_id,
+								       false);
+	unsigned long flags;
+	unsigned long enc_flags = 0;
+	u64 enc_lid = 0;
+	int i;
+
+	if (enc_hdl) {
+		spin_lock_irqsave(&adapter->dev_topo.enc_lock, enc_flags);
+		enc_node = leapraid_enc_find_by_hdl(adapter, enc_hdl);
+		if (enc_node)
+			enc_lid = le64_to_cpu(enc_node->pg0.enc_lid);
+		spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, enc_flags);
+	}
+
+	spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+	list_for_each_entry(topo_node_exp, &adapter->dev_topo.exp_list, list) {
+		if (topo_node_exp->sas_address != sas_address ||
+		    topo_node_exp->card_port != card_port)
+			continue;
+
+		topo_node_exp->resp = 1;
+		topo_node_exp->enc_hdl = enc_hdl;
+		topo_node_exp->enc_lid = enc_lid;
+		if (topo_node_exp->hdl == hdl)
+			goto unlock;
+
+		dev_info(&adapter->pdev->dev,
+			 "hdl changed: 0x%04x -> 0x%04x\n",
+			 topo_node_exp->hdl, hdl);
+		topo_node_exp->hdl = hdl;
+		for (i = 0; i < topo_node_exp->phys_num; i++)
+			topo_node_exp->card_phy[i].hdl = hdl;
+		goto unlock;
+	}
+unlock:
+	spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags);
+}
+
+static void leapraid_search_resp_exp(struct leapraid_adapter *adapter)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_exp_p0 exp_p0;
+	u64 sas_address;
+	u16 hdl;
+	u8 port;
+
+	if (list_empty(&adapter->dev_topo.exp_list))
+		return;
+
+	cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP;
+	for (hdl = 0xFFFF, cfgp2.handle = hdl;
+	     !leapraid_op_config_page(adapter, &exp_p0, cfgp1, cfgp2,
+				      GET_SAS_EXPANDER_PG0);
+	     cfgp2.handle = hdl) {
+		hdl = le16_to_cpu(exp_p0.dev_hdl);
+		sas_address = le64_to_cpu(exp_p0.sas_address);
+		port = exp_p0.physical_port;
+
+		dev_dbg(&adapter->pdev->dev,
+			"exp detected: hdl=0x%04x, sas=0x%016llx, port=%u",
+			hdl, (unsigned long long)sas_address,
+			adapter->adapter_attr.enable_mp ? port :
+				LEAPRAID_DISABLE_MP_PORT_ID);
+		leapraid_mark_resp_exp(adapter, &exp_p0);
+	}
+}
+
+void leapraid_wait_cmds_done(struct leapraid_adapter *adapter)
+{
+	struct leapraid_io_req_tracker *io_req_tracker;
+	unsigned long flags;
+	u16 i;
+
+	adapter->reset_desc.pending_io_cnt = 0;
+	if (!leapraid_pci_active(adapter)) {
+		dev_err(&adapter->pdev->dev,
+			"%s %s: PCI error, device reset or unplugged!\n",
+			adapter->adapter_attr.name, __func__);
+		return;
+	}
+
+	if (leapraid_get_adapter_state(adapter) != LEAPRAID_DB_OPERATIONAL)
+		return;
+
+	spin_lock_irqsave(&adapter->dynamic_task_desc.task_lock, flags);
+	for (i = 1; i <= adapter->shost->can_queue; i++) {
+		io_req_tracker = leapraid_get_io_tracker_from_taskid(adapter,
+								     i);
+		if (io_req_tracker && io_req_tracker->taskid != 0 &&
+		    io_req_tracker->scmd)
+			adapter->reset_desc.pending_io_cnt++;
+	}
+	spin_unlock_irqrestore(&adapter->dynamic_task_desc.task_lock, flags);
+
+	if (!adapter->reset_desc.pending_io_cnt)
+		return;
+
+	wait_event_timeout(adapter->reset_desc.reset_wait_queue,
+			   adapter->reset_desc.pending_io_cnt == 0,
+			   LEAPRAID_IO_CMD_TIMEOUT * HZ);
+}
+
+int leapraid_hard_reset_handler(struct leapraid_adapter *adapter,
+				enum reset_type type)
+{
+	unsigned long flags;
+	bool wake = false;
+	int rc;
+
+	mutex_lock(&adapter->reset_desc.adapter_reset_mutex);
+
+	if (adapter->access_ctrl.shost_recover_async) {
+		rc = adapter->reset_desc.adapter_reset_results;
+		dev_info(&adapter->pdev->dev,
+			 "Skip nested hard reset, async evt running, rc=%d\n",
+			 rc);
+		mutex_unlock(&adapter->reset_desc.adapter_reset_mutex);
+		return rc;
+	}
+
+	if (!leapraid_pci_active(adapter)) {
+		if (leapraid_pci_removed(adapter)) {
+			dev_info(&adapter->pdev->dev,
+				 "pci_dev removed, pause poll, clean cmds\n");
+			leapraid_mq_polling_pause(adapter);
+			leapraid_clean_active_scsi_cmds(adapter);
+			leapraid_mq_polling_resume(adapter);
+		}
+		dev_err(&adapter->pdev->dev, "PCIe unavailable!\n");
+		mutex_unlock(&adapter->reset_desc.adapter_reset_mutex);
+		return -ENXIO;
+	}
+
+	dev_info(&adapter->pdev->dev, "Starting hard reset\n");
+
+	spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags);
+	adapter->access_ctrl.shost_recovering = 1;
+	adapter->access_ctrl.shost_recover_async = 1;
+	spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags);
+
+	leapraid_wait_cmds_done(adapter);
+	leapraid_mask_int(adapter);
+	leapraid_mq_polling_pause(adapter);
+	rc = leapraid_make_adapter_ready(adapter, type);
+	if (rc) {
+		dev_err(&adapter->pdev->dev,
+			"Failed to make adapter ready, rc=%d\n", rc);
+		goto out_cleanup;
+	}
+
+	rc = leapraid_fw_log_init(adapter);
+	if (rc) {
+		dev_err(&adapter->pdev->dev, "Firmware log init failed\n");
+		goto out_cleanup;
+	}
+
+	leapraid_clean_active_cmds(adapter);
+	if (adapter->scan_dev_desc.driver_loading &&
+	    adapter->scan_dev_desc.scan_dev_failed) {
+		dev_err(&adapter->pdev->dev,
+			"Previous device scan failed or driver loading\n");
+		adapter->access_ctrl.host_removing = 1;
+		rc = -EFAULT;
+		goto out_cleanup;
+	}
+
+	rc = leapraid_make_adapter_available(adapter);
+	if (!rc) {
+		dev_info(&adapter->pdev->dev,
+			 "Adapter is now available, rebuilding topology\n");
+		if (adapter->adapter_attr.enable_mp) {
+			leapraid_update_card_port_after_reset(adapter);
+			leapraid_update_vphys_after_reset(adapter);
+		}
+		leapraid_mark_all_dev_deleted(adapter);
+		leapraid_rebuild_enc_list_after_reset(adapter);
+		leapraid_search_resp_sas_dev(adapter);
+		leapraid_search_resp_raid_volume(adapter);
+		leapraid_search_resp_exp(adapter);
+		leapraid_hardreset_barrier(adapter);
+	}
+out_cleanup:
+	if (rc)
+		dev_err(&adapter->pdev->dev, "Hard reset failed\n");
+
+	spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags);
+	adapter->reset_desc.adapter_reset_results = rc;
+	adapter->access_ctrl.shost_recovering = 0;
+	wake_up(&adapter->access_ctrl.recovery_waitq);
+	if (rc) {
+		adapter->access_ctrl.shost_recover_async = 0;
+		wake = true;
+	}
+	spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags);
+	if (wake)
+		wake_up(&adapter->access_ctrl.shost_recover_wq);
+
+	adapter->reset_desc.reset_cnt++;
+	mutex_unlock(&adapter->reset_desc.adapter_reset_mutex);
+
+	if (rc)
+		leapraid_clean_active_scsi_cmds(adapter);
+	leapraid_mq_polling_resume(adapter);
+
+	return rc;
+}
+
+static int leapraid_get_adapter_features(struct leapraid_adapter *adapter)
+{
+	struct leapraid_adapter_features_req leap_mpi_req;
+	struct leapraid_adapter_features_rep leap_mpi_rep;
+	u8 fw_major, fw_minor, fw_build, fw_release;
+	u32 req_sz;
+	u32 rep_sz;
+	u32 db;
+	int r;
+
+	db = leapraid_readl(&adapter->iomem_base->db);
+	if (db & LEAPRAID_DB_USED ||
+	    (db & LEAPRAID_DB_MASK) == LEAPRAID_DB_FAULT) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Doorbell used or fault\n", __func__);
+		return -EFAULT;
+	}
+
+	if ((db & LEAPRAID_DB_MASK) != LEAPRAID_DB_READY &&
+	    (db & LEAPRAID_DB_MASK) != LEAPRAID_DB_OPERATIONAL &&
+	    !leapraid_wait_adapter_ready(adapter)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: adapter not ready\n", __func__);
+		return -EFAULT;
+	}
+
+	req_sz = sizeof(struct leapraid_adapter_features_req);
+	rep_sz = sizeof(struct leapraid_adapter_features_rep);
+	memset(&leap_mpi_req, 0, req_sz);
+	memset(&leap_mpi_rep, 0, rep_sz);
+	leap_mpi_req.func = LEAPRAID_FUNC_GET_ADAPTER_FEATURES;
+	r = leapraid_handshake_func(adapter,
+				    req_sz,
+				    (u32 *)&leap_mpi_req,
+				    rep_sz,
+				    (u16 *)&leap_mpi_rep);
+	if (r) {
+		dev_err(&adapter->pdev->dev,
+			"%s %s: Handshake failed, r=%d\n",
+			adapter->adapter_attr.name, __func__, r);
+		return r;
+	}
+
+	memset(&adapter->adapter_attr.features, 0,
+	       sizeof(struct leapraid_adapter_features));
+	adapter->adapter_attr.features.req_slot =
+		le16_to_cpu(leap_mpi_rep.req_slot);
+	adapter->adapter_attr.features.hp_slot =
+		le16_to_cpu(leap_mpi_rep.hp_slot);
+	adapter->adapter_attr.features.adapter_caps =
+		le32_to_cpu(leap_mpi_rep.adapter_caps);
+	adapter->adapter_attr.features.max_msix_vectors =
+		leap_mpi_rep.max_msix_vectors;
+	adapter->adapter_attr.features.max_volumes =
+		leap_mpi_rep.max_volumes;
+	if (!adapter->adapter_attr.features.max_volumes)
+		adapter->adapter_attr.features.max_volumes =
+			LEAPRAID_MAX_VOLUMES_DEFAULT;
+	adapter->adapter_attr.features.max_dev_handle =
+		le16_to_cpu(leap_mpi_rep.max_dev_hdl);
+	if (!adapter->adapter_attr.features.max_dev_handle)
+		adapter->adapter_attr.features.max_dev_handle =
+			LEAPRAID_MAX_DEV_HANDLE_DEFAULT;
+	adapter->adapter_attr.features.min_dev_handle =
+		le16_to_cpu(leap_mpi_rep.min_dev_hdl);
+	if (adapter->adapter_attr.features.adapter_caps &
+	    LEAPRAID_ADAPTER_FEATURES_CAP_INTEGRATED_RAID)
+		adapter->adapter_attr.raid_support = 1;
+	adapter->adapter_attr.wideport_max_queue_depth =
+		le16_to_cpu(leap_mpi_rep.sas_wide_max_qdepth) ?
+		le16_to_cpu(leap_mpi_rep.sas_wide_max_qdepth) :
+		LEAPRAID_SAS_QUEUE_DEPTH;
+	adapter->adapter_attr.narrowport_max_queue_depth =
+		le16_to_cpu(leap_mpi_rep.sas_narrow_max_qdepth) ?
+		le16_to_cpu(leap_mpi_rep.sas_narrow_max_qdepth) :
+		LEAPRAID_SAS_QUEUE_DEPTH;
+	adapter->adapter_attr.sata_max_queue_depth =
+		leap_mpi_rep.sata_max_qdepth ?
+		leap_mpi_rep.sata_max_qdepth :
+		LEAPRAID_SATA_QUEUE_DEPTH;
+	adapter->adapter_attr.raid_volume_max_queue_depth =
+		LEAPRAID_RAID_QUEUE_DEPTH;
+	dev_info(&adapter->pdev->dev,
+		 "max: wp qd=%d, np qd=%d, SATA qd=%d, raid qd=%d\n",
+		 adapter->adapter_attr.wideport_max_queue_depth,
+		 adapter->adapter_attr.narrowport_max_queue_depth,
+		 adapter->adapter_attr.sata_max_queue_depth,
+		 adapter->adapter_attr.raid_volume_max_queue_depth);
+	if (WARN_ON(!(adapter->adapter_attr.features.adapter_caps &
+		      LEAPRAID_ADAPTER_FEATURES_CAP_ATOMIC_REQ)))
+		return -EFAULT;
+	adapter->adapter_attr.features.fw_version =
+		le32_to_cpu(leap_mpi_rep.fw_version);
+
+	fw_major = (adapter->adapter_attr.features.fw_version >>
+		    LEAPRAID_VER_MAJOR_SHIFT) & LEAPRAID_VER_MASK;
+	fw_minor = (adapter->adapter_attr.features.fw_version >>
+		    LEAPRAID_VER_MINOR_SHIFT) & LEAPRAID_VER_MASK;
+	fw_build = (adapter->adapter_attr.features.fw_version >>
+		    LEAPRAID_VER_BUILD_SHIFT)  & LEAPRAID_VER_MASK;
+	fw_release =
+		adapter->adapter_attr.features.fw_version & LEAPRAID_VER_MASK;
+
+	dev_info(&adapter->pdev->dev,
+		 "Firmware version: %u.%u.%u.%u (0x%08x)\n",
+		 fw_major, fw_minor, fw_build, fw_release,
+		 adapter->adapter_attr.features.fw_version);
+
+	adapter->adapter_attr.features.msg_ver =
+		le16_to_cpu(leap_mpi_rep.msg_ver);
+	adapter->adapter_attr.features.product_id =
+		le16_to_cpu(leap_mpi_rep.product_id);
+	dev_info(&adapter->pdev->dev,
+		 "message version: 0x%x, product id 0x%x\n",
+		 adapter->adapter_attr.features.msg_ver,
+		 adapter->adapter_attr.features.product_id);
+
+	if (adapter->adapter_attr.features.msg_ver < 0x1000) {
+		dev_err(&adapter->pdev->dev, "Device not supported\n");
+		return -EFAULT;
+	}
+	adapter->shost->max_id = LEAPRAID_INVALID_INITIAL_VALUE;
+
+	return 0;
+}
+
+static inline void leapraid_disable_pcie(struct leapraid_adapter *adapter)
+{
+	mutex_lock(&adapter->access_ctrl.pci_access_lock);
+	if (adapter->iomem_base) {
+		iounmap(adapter->iomem_base);
+		adapter->iomem_base = NULL;
+	}
+	if (pci_is_enabled(adapter->pdev)) {
+		pci_release_regions(adapter->pdev);
+		pci_disable_device(adapter->pdev);
+	}
+	mutex_unlock(&adapter->access_ctrl.pci_access_lock);
+}
+
+static int leapraid_enable_pcie(struct leapraid_adapter *adapter)
+{
+	u64 dma_mask;
+	int rc;
+
+	rc = pci_enable_device(adapter->pdev);
+	if (rc) {
+		dev_err(&adapter->pdev->dev, "Failed to enable PCI device\n");
+		return rc;
+	}
+
+	rc = pci_request_regions(adapter->pdev, LEAPRAID_DRIVER_NAME);
+	if (rc) {
+		dev_err(&adapter->pdev->dev,
+			"Failed to obtain PCI resources\n");
+		return rc;
+	}
+
+	if (sizeof(dma_addr_t) > 4) {
+		dma_mask = DMA_BIT_MASK(DMA_64_BITS);
+		adapter->adapter_attr.use_32_dma_mask = 0;
+	} else {
+		dma_mask = DMA_BIT_MASK(DMA_32_BITS);
+		adapter->adapter_attr.use_32_dma_mask = 1;
+	}
+
+	rc = dma_set_mask_and_coherent(&adapter->pdev->dev, dma_mask);
+	if (rc) {
+		dev_err(&adapter->pdev->dev,
+			"Failed to set %lld DMA mask\n", dma_mask);
+		return rc;
+	}
+	adapter->iomem_base = ioremap(pci_resource_start(adapter->pdev, 0),
+				      sizeof(struct leapraid_reg_base));
+	if (!adapter->iomem_base) {
+		dev_err(&adapter->pdev->dev,
+			"Failed to map memory for controller registers\n");
+		return -ENOMEM;
+	}
+
+	pci_set_master(adapter->pdev);
+
+	return 0;
+}
+
+static void leapraid_cpus_on_irq(struct leapraid_adapter *adapter)
+{
+	struct leapraid_int_rq *int_rq;
+	unsigned int i, base_group, this_group;
+	unsigned int cpu, nr_cpus, total_msix, index;
+
+	total_msix = adapter->notification_desc.iopoll_qdex;
+	nr_cpus = num_online_cpus();
+
+	if (!nr_cpus || !total_msix)
+		return;
+	base_group = nr_cpus / total_msix;
+
+	cpu = cpumask_first(cpu_online_mask);
+	for (index = 0; index < adapter->notification_desc.iopoll_qdex;
+	     index++) {
+		int_rq = &adapter->notification_desc.int_rqs[index];
+
+		if (cpu >= adapter->notification_desc.msix_cpu_map_sz)
+			break;
+
+		this_group = base_group +
+				(index < (nr_cpus % total_msix) ? 1 : 0);
+
+		for (i = 0 ; i < this_group ; i++) {
+			if (cpu >= adapter->notification_desc.msix_cpu_map_sz)
+				break;
+
+			adapter->notification_desc.msix_cpu_map[cpu] =
+				int_rq->rq.msix_idx;
+			cpu = cpumask_next(cpu, cpu_online_mask);
+		}
+	}
+}
+
+static void leapraid_map_msix_to_cpu(struct leapraid_adapter *adapter)
+{
+	struct leapraid_int_rq *int_rq;
+	const cpumask_t *affinity_mask;
+	int cpu;
+	u32 i;
+
+	if (!adapter->adapter_attr.rq_cnt)
+		return;
+
+	for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) {
+		int_rq = &adapter->notification_desc.int_rqs[i];
+		affinity_mask = pci_irq_get_affinity(adapter->pdev,
+						     int_rq->rq.msix_idx);
+		if (!affinity_mask) {
+			dev_warn(&adapter->pdev->dev,
+				 "%s: IRQ affinity NULL, msix_idx=%d\n",
+				 __func__, int_rq->rq.msix_idx);
+			goto out_apply_irq_affinity;
+		}
+
+		for_each_cpu_and(cpu, affinity_mask, cpu_online_mask) {
+			if (cpu >= adapter->notification_desc.msix_cpu_map_sz)
+				continue;
+
+			adapter->notification_desc.msix_cpu_map[cpu] =
+				int_rq->rq.msix_idx;
+		}
+	}
+	return;
+out_apply_irq_affinity:
+	leapraid_cpus_on_irq(adapter);
+}
+
+static int leapraid_alloc_msix_cpu_map(struct leapraid_adapter *adapter)
+{
+	adapter->notification_desc.msix_cpu_map_sz = nr_cpu_ids;
+	adapter->notification_desc.msix_cpu_map =
+		kzalloc(adapter->notification_desc.msix_cpu_map_sz,
+			GFP_KERNEL);
+	if (!adapter->notification_desc.msix_cpu_map)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void leapraid_configure_reply_queue_affinity(
+		struct leapraid_adapter *adapter)
+{
+	if (!adapter || !adapter->notification_desc.msix_enable)
+		return;
+
+	leapraid_map_msix_to_cpu(adapter);
+}
+
+static void leapraid_free_irq(struct leapraid_adapter *adapter)
+{
+	struct leapraid_int_rq *int_rq;
+	unsigned int i;
+	int irq;
+
+	for (i = 0; adapter->notification_desc.int_rqs &&
+	     i < adapter->notification_desc.int_rqs_allocated; i++) {
+		int_rq = &adapter->notification_desc.int_rqs[i];
+		if (!int_rq)
+			continue;
+
+		irq = pci_irq_vector(adapter->pdev, int_rq->rq.msix_idx);
+		irq_set_affinity_hint(irq, NULL);
+		free_irq(irq, &int_rq->rq);
+	}
+	adapter->notification_desc.int_rqs_allocated = 0;
+
+	if (adapter->notification_desc.irq_vectors_allocated) {
+		pci_free_irq_vectors(adapter->pdev);
+		adapter->notification_desc.irq_vectors_allocated = 0;
+	}
+
+	adapter->notification_desc.msix_enable = 0;
+
+	kfree(adapter->notification_desc.blk_mq_poll_rqs);
+	adapter->notification_desc.blk_mq_poll_rqs = NULL;
+
+	kfree(adapter->notification_desc.int_rqs);
+	adapter->notification_desc.int_rqs = NULL;
+
+	kfree(adapter->notification_desc.msix_cpu_map);
+	adapter->notification_desc.msix_cpu_map = NULL;
+	adapter->notification_desc.msix_cpu_map_sz = 0;
+	adapter->notification_desc.iopoll_qdex = 0;
+	adapter->notification_desc.iopoll_qcnt = 0;
+}
+
+static int leapraid_setup_irqs(struct leapraid_adapter *adapter)
+{
+	int irq_mode = adapter->notification_desc.irq_mode;
+	unsigned int i;
+	int rc = 0;
+
+	if (irq_mode == LEAPRAID_INTERRUPT_MODE_MSIX) {
+		rc = pci_alloc_irq_vectors_affinity(
+			adapter->pdev,
+			adapter->notification_desc.iopoll_qdex,
+			adapter->notification_desc.iopoll_qdex,
+			PCI_IRQ_MSIX | PCI_IRQ_AFFINITY, NULL);
+		if (rc < 0) {
+			dev_err(&adapter->pdev->dev,
+				"%d MSI/MSIX vectors allocated failed!\n",
+				adapter->notification_desc.iopoll_qdex);
+			return rc;
+		}
+
+		adapter->notification_desc.irq_vectors_allocated = 1;
+	}
+
+	for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) {
+		adapter->notification_desc.int_rqs[i].rq.adapter = adapter;
+		adapter->notification_desc.int_rqs[i].rq.msix_idx = i;
+		atomic_set(&adapter->notification_desc.int_rqs[i].rq.busy, 0);
+		if (irq_mode == LEAPRAID_INTERRUPT_MODE_MSIX)
+			snprintf(adapter->notification_desc.int_rqs[i].rq.name,
+				 LEAPRAID_NAME_LENGTH, "%s%u-MSIx%u",
+				 LEAPRAID_DRIVER_NAME,
+				 adapter->adapter_attr.id, i);
+		else if (irq_mode == LEAPRAID_INTERRUPT_MODE_MSI)
+			snprintf(adapter->notification_desc.int_rqs[i].rq.name,
+				 LEAPRAID_NAME_LENGTH, "%s%u-MSI%u",
+				 LEAPRAID_DRIVER_NAME,
+				 adapter->adapter_attr.id, i);
+
+		rc = request_irq(pci_irq_vector(adapter->pdev, i),
+				 leapraid_irq_handler,
+				 IRQF_SHARED,
+				 adapter->notification_desc.int_rqs[i].rq.name,
+				 &adapter->notification_desc.int_rqs[i].rq);
+		if (rc) {
+			dev_err(&adapter->pdev->dev,
+				"MSI/MSIx: request_irq %s failed!\n",
+				adapter->notification_desc.int_rqs[i].rq.name);
+			return rc;
+		}
+		adapter->notification_desc.int_rqs_allocated++;
+	}
+
+	return 0;
+}
+
+static int leapraid_setup_legacy_int(struct leapraid_adapter *adapter)
+{
+	int rc;
+
+	adapter->notification_desc.int_rqs[0].rq.adapter = adapter;
+	adapter->notification_desc.int_rqs[0].rq.msix_idx = 0;
+	atomic_set(&adapter->notification_desc.int_rqs[0].rq.busy, 0);
+	snprintf(adapter->notification_desc.int_rqs[0].rq.name,
+		 LEAPRAID_NAME_LENGTH, "%s%d-LegacyInt",
+		 LEAPRAID_DRIVER_NAME, adapter->adapter_attr.id);
+
+	rc = pci_alloc_irq_vectors_affinity(
+		adapter->pdev,
+		adapter->notification_desc.iopoll_qdex,
+		adapter->notification_desc.iopoll_qdex,
+		PCI_IRQ_INTX | PCI_IRQ_AFFINITY,
+		NULL);
+	if (rc < 0) {
+		dev_err(&adapter->pdev->dev,
+			"Legacy irq allocated failed!\n");
+		return rc;
+	}
+
+	adapter->notification_desc.irq_vectors_allocated = 1;
+	adapter->notification_desc.irq_mode = LEAPRAID_INTERRUPT_MODE_LEGACY;
+	rc = request_irq(pci_irq_vector(adapter->pdev, 0),
+			 leapraid_irq_handler,
+			 IRQF_SHARED,
+			 adapter->notification_desc.int_rqs[0].rq.name,
+			 &adapter->notification_desc.int_rqs[0].rq);
+	if (rc) {
+		irq_set_affinity_hint(pci_irq_vector(adapter->pdev, 0), NULL);
+		pci_free_irq_vectors(adapter->pdev);
+		adapter->notification_desc.irq_vectors_allocated = 0;
+		dev_err(&adapter->pdev->dev,
+			"Legacy Int: request_irq %s failed!\n",
+			adapter->notification_desc.int_rqs[0].rq.name);
+		return -EBUSY;
+	}
+	adapter->notification_desc.int_rqs_allocated = 1;
+	return rc;
+}
+
+static int leapraid_set_legacy_int(struct leapraid_adapter *adapter)
+{
+	int rc;
+
+	rc = leapraid_alloc_msix_cpu_map(adapter);
+	if (rc)
+		return rc;
+
+	adapter->adapter_attr.rq_cnt = 1;
+	adapter->notification_desc.iopoll_qdex =
+		adapter->adapter_attr.rq_cnt;
+	adapter->notification_desc.iopoll_qcnt = 0;
+	dev_info(&adapter->pdev->dev,
+		 "Legacy Intr: req queue cnt=%d intr=%d/poll=%d rep queues!\n",
+		 adapter->adapter_attr.rq_cnt,
+		 adapter->notification_desc.iopoll_qdex,
+		 adapter->notification_desc.iopoll_qcnt);
+	adapter->notification_desc.int_rqs =
+		kcalloc(adapter->notification_desc.iopoll_qdex,
+			sizeof(struct leapraid_int_rq), GFP_KERNEL);
+	if (!adapter->notification_desc.int_rqs)
+		return -ENOMEM;
+
+	return leapraid_setup_legacy_int(adapter);
+}
+
+static int leapraid_set_msix(struct leapraid_adapter *adapter)
+{
+	int iopoll_qcnt = 0;
+	unsigned int i;
+	int rc, msix_cnt;
+
+	msix_cnt = pci_msix_vec_count(adapter->pdev);
+	if (msix_cnt <= 0 ||
+	    adapter->adapter_attr.features.max_msix_vectors == 0) {
+		dev_info(&adapter->pdev->dev, "MSIX unsupported!\n");
+		return -EOPNOTSUPP;
+	}
+
+	msix_cnt = min_t(int, msix_cnt,
+			 adapter->adapter_attr.features.max_msix_vectors);
+
+	if (reset_devices)
+		adapter->adapter_attr.rq_cnt = 1;
+	else
+		adapter->adapter_attr.rq_cnt = min_t(int,
+						     num_online_cpus(),
+						     msix_cnt);
+
+	if (max_msix_vectors > 0)
+		adapter->adapter_attr.rq_cnt = min_t(
+			int, max_msix_vectors, adapter->adapter_attr.rq_cnt);
+
+	if (adapter->adapter_attr.rq_cnt <= 1)
+		adapter->shost->host_tagset = 0;
+	if (adapter->shost->host_tagset) {
+		iopoll_qcnt = poll_queues;
+		if (iopoll_qcnt >= adapter->adapter_attr.rq_cnt)
+			iopoll_qcnt = 0;
+	}
+	if (iopoll_qcnt) {
+		adapter->notification_desc.blk_mq_poll_rqs =
+			kcalloc(iopoll_qcnt,
+				sizeof(struct leapraid_blk_mq_poll_rq),
+				GFP_KERNEL);
+		if (!adapter->notification_desc.blk_mq_poll_rqs)
+			return -ENOMEM;
+		adapter->adapter_attr.rq_cnt =
+			min(adapter->adapter_attr.rq_cnt + iopoll_qcnt,
+			    msix_cnt);
+	}
+
+	adapter->notification_desc.iopoll_qdex =
+		adapter->adapter_attr.rq_cnt - iopoll_qcnt;
+
+	adapter->notification_desc.iopoll_qcnt = iopoll_qcnt;
+	dev_info(&adapter->pdev->dev,
+		 "MSIx: req queue cnt=%d, intr=%d/poll=%d rep queues!\n",
+		 adapter->adapter_attr.rq_cnt,
+		 adapter->notification_desc.iopoll_qdex,
+		 adapter->notification_desc.iopoll_qcnt);
+
+	adapter->notification_desc.int_rqs =
+		kcalloc(adapter->notification_desc.iopoll_qdex,
+			sizeof(struct leapraid_int_rq), GFP_KERNEL);
+	if (!adapter->notification_desc.int_rqs)
+		return -ENOMEM;
+
+	for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) {
+		adapter->notification_desc.blk_mq_poll_rqs[i].rq.adapter =
+			adapter;
+		adapter->notification_desc.blk_mq_poll_rqs[i].rq.msix_idx =
+			i + adapter->notification_desc.iopoll_qdex;
+		atomic_set(
+			&adapter->notification_desc.blk_mq_poll_rqs[i].rq.busy,
+			0);
+		snprintf(adapter->notification_desc.blk_mq_poll_rqs[i].rq.name,
+			 LEAPRAID_NAME_LENGTH,
+			 "%s%u-MQ-Poll%u", LEAPRAID_DRIVER_NAME,
+			 adapter->adapter_attr.id, i);
+		atomic_set(&adapter->notification_desc.blk_mq_poll_rqs[i].busy,
+			   0);
+		atomic_set(&adapter->notification_desc.blk_mq_poll_rqs[i].pause,
+			   0);
+	}
+
+	rc = leapraid_alloc_msix_cpu_map(adapter);
+	if (rc)
+		return rc;
+
+	adapter->notification_desc.irq_mode = LEAPRAID_INTERRUPT_MODE_MSIX;
+	adapter->notification_desc.msix_enable = 1;
+	rc = leapraid_setup_irqs(adapter);
+	if (rc) {
+		leapraid_free_irq(adapter);
+		adapter->notification_desc.msix_enable = 0;
+		return rc;
+	}
+
+	return 0;
+}
+
+static int leapraid_set_msi(struct leapraid_adapter *adapter)
+{
+	int iopoll_qcnt = 0;
+	unsigned int i;
+	int rc, msi_cnt;
+
+	msi_cnt = pci_msi_vec_count(adapter->pdev);
+	if (msi_cnt <= 0 ||
+	    adapter->adapter_attr.features.max_msix_vectors == 0) {
+		dev_info(&adapter->pdev->dev, "MSI unsupported!\n");
+		return -EOPNOTSUPP;
+	}
+
+	msi_cnt = min_t(int, msi_cnt,
+			adapter->adapter_attr.features.max_msix_vectors);
+
+	if (reset_devices)
+		adapter->adapter_attr.rq_cnt = 1;
+	else
+		adapter->adapter_attr.rq_cnt = min_t(int,
+						     num_online_cpus(),
+						     msi_cnt);
+
+	if (max_msix_vectors > 0)
+		adapter->adapter_attr.rq_cnt = min_t(
+			int, max_msix_vectors, adapter->adapter_attr.rq_cnt);
+
+	if (adapter->adapter_attr.rq_cnt <= 1)
+		adapter->shost->host_tagset = 0;
+	if (adapter->shost->host_tagset) {
+		iopoll_qcnt = poll_queues;
+		if (iopoll_qcnt >= adapter->adapter_attr.rq_cnt)
+			iopoll_qcnt = 0;
+	}
+
+	if (iopoll_qcnt) {
+		adapter->notification_desc.blk_mq_poll_rqs =
+			kcalloc(iopoll_qcnt,
+				sizeof(struct leapraid_blk_mq_poll_rq),
+				GFP_KERNEL);
+		if (!adapter->notification_desc.blk_mq_poll_rqs)
+			return -ENOMEM;
+
+		adapter->adapter_attr.rq_cnt =
+			 min(adapter->adapter_attr.rq_cnt + iopoll_qcnt,
+			     msi_cnt);
+	}
+
+	adapter->notification_desc.iopoll_qdex =
+		adapter->adapter_attr.rq_cnt - iopoll_qcnt;
+	rc = pci_alloc_irq_vectors_affinity(
+		adapter->pdev,
+		1,
+		adapter->notification_desc.iopoll_qdex,
+		PCI_IRQ_MSI | PCI_IRQ_AFFINITY, NULL);
+	if (rc < 0) {
+		dev_err(&adapter->pdev->dev,
+			"%d MSI vectors allocated failed!\n",
+			adapter->notification_desc.iopoll_qdex);
+		leapraid_free_irq(adapter);
+		return rc;
+	}
+	adapter->notification_desc.irq_vectors_allocated = 1;
+	if (rc != adapter->notification_desc.iopoll_qdex) {
+		adapter->notification_desc.iopoll_qdex = rc;
+		adapter->adapter_attr.rq_cnt =
+			adapter->notification_desc.iopoll_qdex + iopoll_qcnt;
+	}
+	adapter->notification_desc.iopoll_qcnt = iopoll_qcnt;
+	dev_info(&adapter->pdev->dev,
+		 "MSI: req queue cnt=%d, intr=%d/poll=%d rep queues!\n",
+		 adapter->adapter_attr.rq_cnt,
+		 adapter->notification_desc.iopoll_qdex,
+		 adapter->notification_desc.iopoll_qcnt);
+
+	adapter->notification_desc.int_rqs =
+		kcalloc(adapter->notification_desc.iopoll_qdex,
+			sizeof(struct leapraid_int_rq),
+			GFP_KERNEL);
+	if (!adapter->notification_desc.int_rqs)
+		return -ENOMEM;
+
+	for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) {
+		adapter->notification_desc.blk_mq_poll_rqs[i].rq.adapter =
+			adapter;
+		adapter->notification_desc.blk_mq_poll_rqs[i].rq.msix_idx =
+			i + adapter->notification_desc.iopoll_qdex;
+		atomic_set(
+			&adapter->notification_desc.blk_mq_poll_rqs[i].rq.busy,
+			0);
+		snprintf(adapter->notification_desc.blk_mq_poll_rqs[i].rq.name,
+			 LEAPRAID_NAME_LENGTH,
+			 "%s%u-MQ-Poll%u", LEAPRAID_DRIVER_NAME,
+			 adapter->adapter_attr.id, i);
+		atomic_set(
+			&adapter->notification_desc.blk_mq_poll_rqs[i].busy,
+			0);
+		atomic_set(
+			&adapter->notification_desc.blk_mq_poll_rqs[i].pause,
+			0);
+	}
+
+	rc = leapraid_alloc_msix_cpu_map(adapter);
+	if (rc)
+		return rc;
+
+	adapter->notification_desc.irq_mode = LEAPRAID_INTERRUPT_MODE_MSI;
+	adapter->notification_desc.msix_enable = 1;
+	rc = leapraid_setup_irqs(adapter);
+	if (rc) {
+		leapraid_free_irq(adapter);
+		adapter->notification_desc.msix_enable = 0;
+		return rc;
+	}
+
+	return 0;
+}
+
+static int leapraid_set_notification_auto(struct leapraid_adapter *adapter)
+{
+	int rc;
+
+	rc = leapraid_set_msix(adapter);
+	if (!rc)
+		return 0;
+
+	leapraid_free_irq(adapter);
+	dev_info(&adapter->pdev->dev,
+		 "MSI-X setup failed (%d), trying MSI\n", rc);
+
+	rc = leapraid_set_msi(adapter);
+	if (!rc)
+		return 0;
+
+	leapraid_free_irq(adapter);
+	dev_info(&adapter->pdev->dev,
+		 "MSI setup failed (%d), back to legacy INTx\n", rc);
+
+	rc = leapraid_set_legacy_int(adapter);
+	if (rc)
+		dev_err(&adapter->pdev->dev,
+			"%s: Enable legacy irq failed!\n", __func__);
+
+	return rc;
+}
+
+int leapraid_set_pcie_and_notification(struct leapraid_adapter *adapter)
+{
+	int rc;
+
+	rc = leapraid_enable_pcie(adapter);
+	if (rc)
+		goto out_fail;
+
+	leapraid_mask_int(adapter);
+
+	rc = leapraid_make_adapter_ready(adapter, PART_RESET);
+	if (rc) {
+		dev_err(&adapter->pdev->dev, "Make adapter ready failure\n");
+		goto out_fail;
+	}
+
+	rc = leapraid_get_adapter_features(adapter);
+	if (rc) {
+		dev_err(&adapter->pdev->dev, "Get adapter feature failure\n");
+		goto out_fail;
+	}
+
+	rc = leapraid_set_notification_auto(adapter);
+	if (rc)
+		goto out_fail;
+
+	pci_save_state(adapter->pdev);
+
+	return 0;
+
+out_fail:
+	leapraid_free_irq(adapter);
+	leapraid_disable_pcie(adapter);
+	return rc;
+}
+
+void leapraid_disable_controller(struct leapraid_adapter *adapter)
+{
+	if (!adapter->iomem_base)
+		return;
+
+	leapraid_mask_int(adapter);
+
+	adapter->access_ctrl.shost_recovering = 1;
+	leapraid_make_adapter_ready(adapter, PART_RESET);
+	adapter->access_ctrl.shost_recovering = 0;
+	wake_up(&adapter->access_ctrl.recovery_waitq);
+
+	leapraid_free_irq(adapter);
+	leapraid_disable_pcie(adapter);
+}
+
+static int leapraid_adapter_unit_reset(struct leapraid_adapter *adapter)
+{
+	int rc = 0;
+
+	writel(LEAPRAID_FUNC_ADAPTER_UNIT_RESET << LEAPRAID_DB_FUNC_SHIFT,
+	       &adapter->iomem_base->db);
+	if (leapraid_db_wait_ack_and_clear_int(adapter))
+		rc = -EFAULT;
+
+	if (!leapraid_wait_adapter_ready(adapter)) {
+		dev_err(&adapter->pdev->dev, "unit reset failed\n");
+		return -EFAULT;
+	}
+
+	return rc;
+}
+
+static int leapraid_make_adapter_ready(struct leapraid_adapter *adapter,
+				       enum reset_type type)
+{
+	u32 db;
+	int count;
+
+	if (!leapraid_pci_active(adapter))
+		return 0;
+
+	count = 0;
+	db = leapraid_readl(&adapter->iomem_base->db);
+	if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_RESET) {
+		while ((db & LEAPRAID_DB_MASK) != LEAPRAID_DB_READY) {
+			if (count++ == LEAPRAID_DB_RETRY_COUNT_MAX) {
+				dev_err(&adapter->pdev->dev,
+					"Wait adapter ready timeout\n");
+				return -EFAULT;
+			}
+			ssleep(1);
+			db = leapraid_readl(&adapter->iomem_base->db);
+			dev_info(&adapter->pdev->dev,
+				 "Wait adapter ready, count=%d, db=0x%x\n",
+				 count, db);
+		}
+	}
+	if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_READY)
+		return 0;
+
+	if (db & LEAPRAID_DB_USED)
+		goto full_reset;
+
+	if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_FAULT)
+		goto full_reset;
+
+	if (type == FULL_RESET)
+		goto full_reset;
+
+	if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_OPERATIONAL &&
+	    !leapraid_adapter_unit_reset(adapter))
+		return 0;
+
+full_reset:
+	return leapraid_host_diag_reset(adapter);
+}
+
+static void leapraid_fw_log_exit(struct leapraid_adapter *adapter)
+{
+	if (!adapter->fw_log_desc.open_pcie_trace)
+		return;
+
+	if (adapter->fw_log_desc.fw_log_buffer) {
+		wait_event(adapter->fw_log_desc.mmap_waitq,
+			   !atomic_read(&adapter->fw_log_desc.mmap_refcnt));
+		dma_free_coherent(&adapter->pdev->dev,
+				  (LEAPRAID_SYS_LOG_BUF_SIZE +
+				   LEAPRAID_SYS_LOG_BUF_RESERVE),
+				  adapter->fw_log_desc.fw_log_buffer,
+				  adapter->fw_log_desc.fw_log_buffer_dma);
+		adapter->fw_log_desc.fw_log_buffer = NULL;
+	}
+}
+
+static int leapraid_fw_log_init(struct leapraid_adapter *adapter)
+{
+	struct leapraid_adapter_log_req adapter_log_req;
+	struct leapraid_adapter_log_rep adapter_log_rep;
+	u16 adapter_status;
+	u64 buf_addr;
+	u32 rc;
+
+	if (!adapter->fw_log_desc.open_pcie_trace)
+		return 0;
+
+	if (!adapter->fw_log_desc.fw_log_buffer) {
+		adapter->fw_log_desc.fw_log_buffer =
+			dma_alloc_coherent(
+				&adapter->pdev->dev,
+				(LEAPRAID_SYS_LOG_BUF_SIZE +
+				 LEAPRAID_SYS_LOG_BUF_RESERVE),
+				&adapter->fw_log_desc.fw_log_buffer_dma,
+				GFP_KERNEL);
+		if (!adapter->fw_log_desc.fw_log_buffer)
+			return -ENOMEM;
+	}
+
+	memset(&adapter_log_req, 0, sizeof(struct leapraid_adapter_log_req));
+	adapter_log_req.func = LEAPRAID_FUNC_LOGBUF_INIT;
+	buf_addr = adapter->fw_log_desc.fw_log_buffer_dma;
+
+	adapter_log_req.mbox.w[0] =
+		cpu_to_le32((u32)(buf_addr & 0xFFFFFFFF));
+	adapter_log_req.mbox.w[1] =
+		cpu_to_le32((u32)((buf_addr >> 32) & 0xFFFFFFFF));
+	adapter_log_req.mbox.w[2] =
+		cpu_to_le32(LEAPRAID_SYS_LOG_BUF_SIZE);
+	rc = leapraid_handshake_func(adapter,
+				     sizeof(struct leapraid_adapter_log_req),
+				     (u32 *)&adapter_log_req,
+				     sizeof(struct leapraid_adapter_log_rep),
+				     (u16 *)&adapter_log_rep);
+	if (rc != 0) {
+		dev_err(&adapter->pdev->dev, "%s: Handshake failed, rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	adapter_status = le16_to_cpu(adapter_log_rep.adapter_status) &
+			LEAPRAID_ADAPTER_STATUS_MASK;
+	if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) {
+		dev_err(&adapter->pdev->dev, "%s: failed!\n", __func__);
+		rc = -EIO;
+	}
+
+	return rc;
+}
+
+static void leapraid_free_host_memory(struct leapraid_adapter *adapter)
+{
+	unsigned int i;
+
+	if (adapter->mem_desc.task_desc) {
+		dma_free_coherent(&adapter->pdev->dev,
+				  adapter->adapter_attr.task_desc_dma_size,
+				  adapter->mem_desc.task_desc,
+				  adapter->mem_desc.task_desc_dma);
+		adapter->mem_desc.task_desc = NULL;
+	}
+
+	if (adapter->mem_desc.sense_data) {
+		dma_free_coherent(
+			&adapter->pdev->dev,
+			adapter->adapter_attr.io_qd * SCSI_SENSE_BUFFERSIZE,
+			adapter->mem_desc.sense_data,
+			adapter->mem_desc.sense_data_dma);
+		adapter->mem_desc.sense_data = NULL;
+	}
+
+	if (adapter->mem_desc.rep_msg) {
+		dma_free_coherent(
+			&adapter->pdev->dev,
+			adapter->adapter_attr.rep_msg_qd * LEAPRAID_REPLY_SIZE,
+			adapter->mem_desc.rep_msg,
+			adapter->mem_desc.rep_msg_dma);
+		adapter->mem_desc.rep_msg = NULL;
+	}
+
+	if (adapter->mem_desc.rep_msg_addr) {
+		dma_free_coherent(&adapter->pdev->dev,
+				  adapter->adapter_attr.rep_msg_qd *
+				  LEAPRAID_REP_MSG_ADDR_SIZE,
+				  adapter->mem_desc.rep_msg_addr,
+				  adapter->mem_desc.rep_msg_addr_dma);
+		adapter->mem_desc.rep_msg_addr = NULL;
+	}
+
+	if (adapter->mem_desc.rep_desc_seg_maint) {
+		for (i = 0; i < adapter->adapter_attr.rep_desc_q_seg_cnt;
+		     i++) {
+			if (adapter->mem_desc.rep_desc_seg_maint[i]
+			    .rep_desc_seg) {
+				dma_free_coherent(
+					&adapter->pdev->dev,
+					(adapter->adapter_attr.rep_desc_qd *
+					 LEAPRAID_REP_DESC_ENTRY_SIZE) *
+					LEAPRAID_REP_DESC_CHUNK_SIZE,
+					adapter->mem_desc.rep_desc_seg_maint[i]
+						.rep_desc_seg,
+					adapter->mem_desc.rep_desc_seg_maint[i]
+						.rep_desc_seg_dma);
+				adapter->mem_desc.rep_desc_seg_maint[i]
+					.rep_desc_seg = NULL;
+			}
+		}
+
+		if (adapter->mem_desc.rep_desc_q_arr) {
+			dma_free_coherent(
+				&adapter->pdev->dev,
+				adapter->adapter_attr.rq_cnt *
+				LEAPRAID_REP_RQ_CNT_SIZE,
+				adapter->mem_desc.rep_desc_q_arr,
+				adapter->mem_desc.rep_desc_q_arr_dma);
+			adapter->mem_desc.rep_desc_q_arr = NULL;
+		}
+
+		for (i = 0; i < adapter->adapter_attr.rep_desc_q_seg_cnt; i++) {
+			struct leapraid_mem_desc *mem_desc =
+				 &adapter->mem_desc;
+			kfree(adapter->mem_desc.rep_desc_seg_maint[i]
+				.rep_desc_maint);
+			mem_desc->rep_desc_seg_maint[i].rep_desc_maint = NULL;
+		}
+		kfree(adapter->mem_desc.rep_desc_seg_maint);
+		adapter->mem_desc.rep_desc_seg_maint = NULL;
+	}
+
+	kfree(adapter->mem_desc.taskid_to_uniq_tag);
+	adapter->mem_desc.taskid_to_uniq_tag = NULL;
+
+	dma_pool_destroy(adapter->mem_desc.sg_chain_pool);
+	adapter->mem_desc.sg_chain_pool = NULL;
+}
+
+static inline bool leapraid_is_in_same_4g_seg(dma_addr_t start, u32 size)
+{
+	return upper_32_bits(start) == upper_32_bits(start + size - 1);
+}
+
+int leapraid_internal_init_cmd_priv(struct leapraid_adapter *adapter,
+				    struct leapraid_io_req_tracker *io_tracker)
+{
+	io_tracker->chain =
+		dma_pool_alloc(adapter->mem_desc.sg_chain_pool,
+			       GFP_KERNEL,
+			       &io_tracker->chain_dma);
+
+	if (!io_tracker->chain)
+		return -ENOMEM;
+
+	return 0;
+}
+
+void leapraid_internal_exit_cmd_priv(struct leapraid_adapter *adapter,
+				     struct leapraid_io_req_tracker *io_tracker)
+{
+	if (io_tracker && io_tracker->chain)
+		dma_pool_free(adapter->mem_desc.sg_chain_pool,
+			      io_tracker->chain,
+			      io_tracker->chain_dma);
+}
+
+static int leapraid_request_host_memory(struct leapraid_adapter *adapter)
+{
+	struct leapraid_adapter_features *facts =
+			&adapter->adapter_attr.features;
+	u16 rep_desc_q_cnt_allocated;
+	unsigned int i, j;
+	int rc;
+
+	/* Scatter-gather table size. */
+	adapter->shost->sg_tablesize = LEAPRAID_SG_DEPTH;
+	if (reset_devices)
+		adapter->shost->sg_tablesize =
+				LEAPRAID_KDUMP_MIN_PHYS_SEGMENTS;
+	/* High-priority commands queue depth. */
+	adapter->dynamic_task_desc.hp_cmd_qd = LEAPRAID_FIXED_HP_CMDS;
+	/* Internal commands queue depth. */
+	adapter->dynamic_task_desc.inter_cmd_qd = LEAPRAID_FIXED_INTER_CMDS;
+	/* Adapter commands total queue depth. */
+	if (reset_devices)
+		adapter->adapter_attr.adapter_total_qd =
+			LEAPRAID_DEFAULT_CMD_QD_OFFSET +
+			adapter->dynamic_task_desc.inter_cmd_qd +
+			adapter->dynamic_task_desc.hp_cmd_qd;
+	else
+		adapter->adapter_attr.adapter_total_qd = facts->req_slot +
+				adapter->dynamic_task_desc.hp_cmd_qd;
+	/* Reply message queue depth. */
+	adapter->adapter_attr.rep_msg_qd =
+		adapter->adapter_attr.adapter_total_qd +
+		LEAPRAID_DEFAULT_CMD_QD_OFFSET;
+	/* Reply descriptor queue depth. */
+	adapter->adapter_attr.rep_desc_qd =
+		round_up(adapter->adapter_attr.adapter_total_qd +
+			 adapter->adapter_attr.rep_msg_qd +
+			 LEAPRAID_TASKID_OFFSET_CTRL_CMD,
+			 LEAPRAID_REPLY_QD_ALIGNMENT);
+	/* SCSI command I/O depth. */
+	adapter->adapter_attr.io_qd =
+		adapter->adapter_attr.adapter_total_qd -
+		adapter->dynamic_task_desc.hp_cmd_qd -
+		adapter->dynamic_task_desc.inter_cmd_qd;
+	/* SCSI host can queue. */
+	adapter->shost->can_queue = adapter->adapter_attr.io_qd -
+		LEAPRAID_TASKID_OFFSET_CTRL_CMD;
+	adapter->driver_cmds.ctl_cmd.taskid = adapter->shost->can_queue +
+		LEAPRAID_TASKID_OFFSET_CTRL_CMD;
+
+	/* Allocate task descriptor. */
+try_again:
+	adapter->adapter_attr.task_desc_dma_size =
+		(adapter->adapter_attr.adapter_total_qd +
+			LEAPRAID_TASKID_OFFSET_CTRL_CMD) *
+		LEAPRAID_REQUEST_SIZE;
+	adapter->mem_desc.task_desc =
+		dma_alloc_coherent(&adapter->pdev->dev,
+				   adapter->adapter_attr.task_desc_dma_size,
+				   &adapter->mem_desc.task_desc_dma,
+				   GFP_KERNEL);
+	if (!adapter->mem_desc.task_desc)
+		return -ENOMEM;
+
+	/* Allocate chain message pool. */
+	adapter->mem_desc.sg_chain_pool_size =
+		LEAPRAID_DEFAULT_CHAINS_PER_IO * LEAPRAID_CHAIN_SEG_SIZE;
+	adapter->mem_desc.sg_chain_pool =
+		dma_pool_create("leapraid chain pool",
+				&adapter->pdev->dev,
+				adapter->mem_desc.sg_chain_pool_size,
+				LEAPRAID_DMA_ALIGN, 0);
+	if (!adapter->mem_desc.sg_chain_pool)
+		return -ENOMEM;
+
+	/* Allocate I/O tracker to SCSI I/O. */
+	adapter->mem_desc.taskid_to_uniq_tag =
+		kcalloc(adapter->shost->can_queue, sizeof(u16), GFP_KERNEL);
+	if (!adapter->mem_desc.taskid_to_uniq_tag)
+		return -ENOMEM;
+
+	adapter->dynamic_task_desc.hp_taskid =
+		adapter->adapter_attr.io_qd +
+		LEAPRAID_HP_TASKID_OFFSET_CTL_CMD;
+	/* Allocate static high-priority task ID. */
+	adapter->driver_cmds.ctl_cmd.hp_taskid =
+		adapter->dynamic_task_desc.hp_taskid;
+	adapter->driver_cmds.tm_cmd.hp_taskid =
+		adapter->dynamic_task_desc.hp_taskid +
+		LEAPRAID_HP_TASKID_OFFSET_TM_CMD;
+
+	adapter->dynamic_task_desc.inter_taskid =
+		adapter->dynamic_task_desc.hp_taskid +
+		adapter->dynamic_task_desc.hp_cmd_qd;
+	adapter->driver_cmds.scan_dev_cmd.inter_taskid =
+		adapter->dynamic_task_desc.inter_taskid;
+	adapter->driver_cmds.cfg_op_cmd.inter_taskid =
+		adapter->dynamic_task_desc.inter_taskid +
+		LEAPRAID_TASKID_OFFSET_CFG_OP_CMD;
+	adapter->driver_cmds.transport_cmd.inter_taskid =
+		adapter->dynamic_task_desc.inter_taskid +
+		LEAPRAID_TASKID_OFFSET_TRANSPORT_CMD;
+	adapter->driver_cmds.enc_cmd.inter_taskid =
+		adapter->dynamic_task_desc.inter_taskid +
+		LEAPRAID_TASKID_OFFSET_ENC_CMD;
+	adapter->driver_cmds.notify_event_cmd.inter_taskid =
+		adapter->dynamic_task_desc.inter_taskid +
+		LEAPRAID_TASKID_OFFSET_NOTIFY_EVENT_CMD;
+	dev_info(&adapter->pdev->dev, "queue depth:\n");
+	dev_info(&adapter->pdev->dev, "	host->can_queue: %d\n",
+		 adapter->shost->can_queue);
+	dev_info(&adapter->pdev->dev, "	io_qd: %d\n",
+		 adapter->adapter_attr.io_qd);
+	dev_info(&adapter->pdev->dev, "	hpr_cmd_qd: %d\n",
+		 adapter->dynamic_task_desc.hp_cmd_qd);
+	dev_info(&adapter->pdev->dev, "	inter_cmd_qd: %d\n",
+		 adapter->dynamic_task_desc.inter_cmd_qd);
+	dev_info(&adapter->pdev->dev, "	adapter_total_qd: %d\n",
+		 adapter->adapter_attr.adapter_total_qd);
+
+	dev_info(&adapter->pdev->dev, "taskid range:\n");
+	dev_info(&adapter->pdev->dev,
+		 "	adapter->dynamic_task_desc.hp_taskid: %d\n",
+		 adapter->dynamic_task_desc.hp_taskid);
+	dev_info(&adapter->pdev->dev,
+		 "	adapter->dynamic_task_desc.inter_taskid: %d\n",
+		 adapter->dynamic_task_desc.inter_taskid);
+
+	/*
+	 * Allocate sense data DMA buffer.
+	 * Constraint: Must reside within the same 4GB segment
+	 * (driver-maintained).
+	 */
+	adapter->mem_desc.sense_data =
+		dma_alloc_coherent(
+			&adapter->pdev->dev,
+			adapter->adapter_attr.io_qd * SCSI_SENSE_BUFFERSIZE,
+			&adapter->mem_desc.sense_data_dma,
+			GFP_KERNEL);
+	if (!adapter->mem_desc.sense_data)
+		return -ENOMEM;
+
+	if (!leapraid_is_in_same_4g_seg(adapter->mem_desc.sense_data_dma,
+					adapter->adapter_attr.io_qd *
+					SCSI_SENSE_BUFFERSIZE)) {
+		dev_warn(&adapter->pdev->dev,
+			 "Try 32bit DMA: Sense not in same 4G\n");
+		rc = -EAGAIN;
+		goto out_fail;
+	}
+
+	/*
+	 * Allocate reply frame buffer.
+	 * Constraint: Must reside in the same 4GB segment as the sense DMA.
+	 */
+	adapter->mem_desc.rep_msg =
+		dma_alloc_coherent(&adapter->pdev->dev,
+				   adapter->adapter_attr.rep_msg_qd *
+				   LEAPRAID_REPLY_SIZE,
+				   &adapter->mem_desc.rep_msg_dma,
+				   GFP_KERNEL);
+	if (!adapter->mem_desc.rep_msg) {
+		rc = -ENOMEM;
+		goto out_fail;
+	}
+
+	if (!leapraid_is_in_same_4g_seg(adapter->mem_desc.rep_msg_dma,
+					adapter->adapter_attr.rep_msg_qd *
+					LEAPRAID_REPLY_SIZE)) {
+		dev_warn(&adapter->pdev->dev,
+			 "Use 32 bit DMA due to rep msg is not in same 4g!\n");
+		rc = -EAGAIN;
+		goto out_fail;
+	}
+
+	/* Address of reply frame. */
+	adapter->mem_desc.rep_msg_addr =
+		dma_alloc_coherent(&adapter->pdev->dev,
+				   adapter->adapter_attr.rep_msg_qd *
+				   LEAPRAID_REP_MSG_ADDR_SIZE,
+				   &adapter->mem_desc.rep_msg_addr_dma,
+				   GFP_KERNEL);
+	if (!adapter->mem_desc.rep_msg_addr)
+		return -ENOMEM;
+	adapter->adapter_attr.rep_desc_q_seg_cnt =
+		DIV_ROUND_UP(adapter->adapter_attr.rq_cnt,
+			     LEAPRAID_REP_DESC_CHUNK_SIZE);
+	adapter->mem_desc.rep_desc_seg_maint =
+		kcalloc(adapter->adapter_attr.rep_desc_q_seg_cnt,
+			sizeof(struct leapraid_rep_desc_seg_maint),
+			GFP_KERNEL);
+	if (!adapter->mem_desc.rep_desc_seg_maint)
+		return -ENOMEM;
+
+	rep_desc_q_cnt_allocated = 0;
+	for (i = 0; i < adapter->adapter_attr.rep_desc_q_seg_cnt; i++) {
+		adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_maint =
+			kcalloc(LEAPRAID_REP_DESC_CHUNK_SIZE,
+				sizeof(struct leapraid_rep_desc_maint),
+				GFP_KERNEL);
+		if (!adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_maint)
+			return -ENOMEM;
+
+		adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_seg =
+			dma_alloc_coherent(
+				&adapter->pdev->dev,
+				(adapter->adapter_attr.rep_desc_qd *
+				LEAPRAID_REP_DESC_ENTRY_SIZE) *
+				LEAPRAID_REP_DESC_CHUNK_SIZE,
+				&adapter->mem_desc.rep_desc_seg_maint[i]
+					.rep_desc_seg_dma,
+				GFP_KERNEL);
+		if (!adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_seg)
+			return -ENOMEM;
+
+		for (j = 0; j < LEAPRAID_REP_DESC_CHUNK_SIZE; j++) {
+			if (rep_desc_q_cnt_allocated >=
+			    adapter->adapter_attr.rq_cnt)
+				break;
+			adapter->mem_desc
+				.rep_desc_seg_maint[i]
+				.rep_desc_maint[j]
+				.rep_desc =
+					(void *)((u8 *)(
+					adapter->mem_desc
+						.rep_desc_seg_maint[i]
+						.rep_desc_seg) +
+					j *
+					(adapter->adapter_attr.rep_desc_qd *
+						LEAPRAID_REP_DESC_ENTRY_SIZE));
+			adapter->mem_desc
+				.rep_desc_seg_maint[i]
+				.rep_desc_maint[j]
+				.rep_desc_dma =
+					adapter->mem_desc
+						.rep_desc_seg_maint[i]
+						.rep_desc_seg_dma +
+					j *
+					(adapter->adapter_attr.rep_desc_qd *
+						LEAPRAID_REP_DESC_ENTRY_SIZE);
+			rep_desc_q_cnt_allocated++;
+		}
+	}
+
+	if (!reset_devices) {
+		adapter->mem_desc.rep_desc_q_arr =
+			dma_alloc_coherent(
+				&adapter->pdev->dev,
+				adapter->adapter_attr.rq_cnt *
+				LEAPRAID_REP_RQ_CNT_SIZE,
+				&adapter->mem_desc.rep_desc_q_arr_dma,
+				GFP_KERNEL);
+		if (!adapter->mem_desc.rep_desc_q_arr)
+			return -ENOMEM;
+	}
+
+	return 0;
+out_fail:
+	if (rc == -EAGAIN) {
+		leapraid_free_host_memory(adapter);
+		adapter->adapter_attr.use_32_dma_mask = 1;
+		rc = dma_set_mask_and_coherent(&adapter->pdev->dev,
+					       DMA_BIT_MASK(DMA_32_BITS));
+		if (rc) {
+			dev_err(&adapter->pdev->dev,
+				"Failed to set 32 DMA mask\n");
+			return rc;
+		}
+		goto try_again;
+	}
+	return rc;
+}
+
+static int leapraid_alloc_dev_topo_bitmaps(struct leapraid_adapter *adapter)
+{
+	u16 pd_hdls_sz;
+
+	pd_hdls_sz =
+		BITS_TO_LONGS(
+			adapter->adapter_attr.features.max_dev_handle + 1) *
+		sizeof(unsigned long);
+
+	adapter->dev_topo.pd_hdls_sz = pd_hdls_sz;
+	adapter->dev_topo.pd_hdls =
+		kzalloc(adapter->dev_topo.pd_hdls_sz, GFP_KERNEL);
+	if (!adapter->dev_topo.pd_hdls)
+		return -ENOMEM;
+
+	adapter->dev_topo.blocking_hdls =
+		kzalloc(adapter->dev_topo.pd_hdls_sz, GFP_KERNEL);
+	if (!adapter->dev_topo.blocking_hdls)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void leapraid_free_dev_topo_bitmaps(struct leapraid_adapter *adapter)
+{
+	kfree(adapter->dev_topo.pd_hdls);
+	kfree(adapter->dev_topo.blocking_hdls);
+}
+
+static int leapraid_init_driver_cmds(struct leapraid_adapter *adapter)
+{
+	INIT_LIST_HEAD(&adapter->driver_cmds.special_cmd_list);
+
+	adapter->driver_cmds.scan_dev_cmd.status = LEAPRAID_CMD_NOT_USED;
+	adapter->driver_cmds.scan_dev_cmd.cb_idx = LEAPRAID_SCAN_DEV_CB_IDX;
+	list_add_tail(&adapter->driver_cmds.scan_dev_cmd.list,
+		      &adapter->driver_cmds.special_cmd_list);
+
+	adapter->driver_cmds.cfg_op_cmd.status = LEAPRAID_CMD_NOT_USED;
+	adapter->driver_cmds.cfg_op_cmd.cb_idx = LEAPRAID_CONFIG_CB_IDX;
+	mutex_init(&adapter->driver_cmds.cfg_op_cmd.mutex);
+	list_add_tail(&adapter->driver_cmds.cfg_op_cmd.list,
+		      &adapter->driver_cmds.special_cmd_list);
+
+	adapter->driver_cmds.transport_cmd.status = LEAPRAID_CMD_NOT_USED;
+	adapter->driver_cmds.transport_cmd.cb_idx = LEAPRAID_TRANSPORT_CB_IDX;
+	mutex_init(&adapter->driver_cmds.transport_cmd.mutex);
+	list_add_tail(&adapter->driver_cmds.transport_cmd.list,
+		      &adapter->driver_cmds.special_cmd_list);
+
+	adapter->driver_cmds.enc_cmd.status = LEAPRAID_CMD_NOT_USED;
+	adapter->driver_cmds.enc_cmd.cb_idx = LEAPRAID_ENC_CB_IDX;
+	mutex_init(&adapter->driver_cmds.enc_cmd.mutex);
+	list_add_tail(&adapter->driver_cmds.enc_cmd.list,
+		      &adapter->driver_cmds.special_cmd_list);
+
+	adapter->driver_cmds.notify_event_cmd.status = LEAPRAID_CMD_NOT_USED;
+	adapter->driver_cmds.notify_event_cmd.cb_idx =
+		LEAPRAID_NOTIFY_EVENT_CB_IDX;
+	mutex_init(&adapter->driver_cmds.notify_event_cmd.mutex);
+	list_add_tail(&adapter->driver_cmds.notify_event_cmd.list,
+		      &adapter->driver_cmds.special_cmd_list);
+
+	adapter->driver_cmds.ctl_cmd.status = LEAPRAID_CMD_NOT_USED;
+	adapter->driver_cmds.ctl_cmd.cb_idx = LEAPRAID_CTL_CB_IDX;
+	mutex_init(&adapter->driver_cmds.ctl_cmd.mutex);
+	list_add_tail(&adapter->driver_cmds.ctl_cmd.list,
+		      &adapter->driver_cmds.special_cmd_list);
+
+	adapter->driver_cmds.tm_cmd.status = LEAPRAID_CMD_NOT_USED;
+	adapter->driver_cmds.tm_cmd.cb_idx = LEAPRAID_TM_CB_IDX;
+	mutex_init(&adapter->driver_cmds.tm_cmd.mutex);
+	list_add_tail(&adapter->driver_cmds.tm_cmd.list,
+		      &adapter->driver_cmds.special_cmd_list);
+
+	return 0;
+}
+
+static void leapraid_unmask_evts(struct leapraid_adapter *adapter, u16 evt)
+{
+	if (evt >= LEAPRAID_MAX_EVENT_NUM)
+		return;
+
+	clear_bit(evt, (unsigned long *)adapter->fw_evt_s.leapraid_evt_masks);
+}
+
+static void leapraid_init_event_mask(struct leapraid_adapter *adapter)
+{
+	int i;
+
+	for (i = 0; i < LEAPRAID_EVT_MASK_COUNT; i++)
+		adapter->fw_evt_s.leapraid_evt_masks[i] =
+			LEAPRAID_INVALID_INITIAL_VALUE;
+
+	leapraid_unmask_evts(adapter, LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST);
+	leapraid_unmask_evts(adapter, LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE);
+	leapraid_unmask_evts(adapter, LEAPRAID_EVT_SAS_DEV_STATUS_CHANGE);
+	leapraid_unmask_evts(adapter, LEAPRAID_EVT_IR_CHANGE);
+}
+
+static void leapraid_prepare_adapter_init_req(
+		struct leapraid_adapter *adapter,
+		struct leapraid_adapter_init_req *init_req)
+{
+	ktime_t cur_time;
+	int i, chunk;
+	u32 reply_post_free_ary_sz;
+
+	memset(init_req, 0, sizeof(struct leapraid_adapter_init_req));
+	init_req->func = LEAPRAID_FUNC_ADAPTER_INIT;
+	init_req->who_init = LEAPRAID_WHOINIT_LINUX_DRIVER;
+	init_req->msg_ver = cpu_to_le16(LEAPRAID_MSG_VERSION);
+	init_req->header_ver = cpu_to_le16(LEAPRAID_HEADER_VERSION);
+	init_req->driver_ver =
+		cpu_to_le32((LEAPRAID_MAJOR_VERSION <<
+				LEAPRAID_VER_MAJOR_SHIFT) |
+			(LEAPRAID_MINOR_VERSION << LEAPRAID_VER_MINOR_SHIFT) |
+			(LEAPRAID_BUILD_VERSION << LEAPRAID_VER_BUILD_SHIFT) |
+			LEAPRAID_RELEASE_VERSION);
+	if (adapter->notification_desc.msix_enable)
+		init_req->host_msix_vectors = adapter->adapter_attr.rq_cnt;
+
+	init_req->req_frame_size =
+		cpu_to_le16(LEAPRAID_REQUEST_SIZE / LEAPRAID_DWORDS_BYTE_SIZE);
+	init_req->rep_desc_qd =
+		cpu_to_le16(adapter->adapter_attr.rep_desc_qd);
+	init_req->rep_msg_qd =
+		cpu_to_le16(adapter->adapter_attr.rep_msg_qd);
+	init_req->sense_buffer_add_high =
+		cpu_to_le32((u64)adapter->mem_desc.sense_data_dma >> 32);
+	init_req->rep_msg_dma_high =
+		cpu_to_le32((u64)adapter->mem_desc.rep_msg_dma >> 32);
+	init_req->task_desc_base_addr =
+		cpu_to_le64((u64)adapter->mem_desc.task_desc_dma);
+	init_req->rep_msg_addr_dma =
+		cpu_to_le64((u64)adapter->mem_desc.rep_msg_addr_dma);
+	if (!reset_devices) {
+		reply_post_free_ary_sz =
+			adapter->adapter_attr.rq_cnt * LEAPRAID_REP_RQ_CNT_SIZE;
+		memset(adapter->mem_desc.rep_desc_q_arr, 0,
+		       reply_post_free_ary_sz);
+
+		chunk = LEAPRAID_REP_DESC_CHUNK_SIZE;
+		for (i = 0; i < adapter->adapter_attr.rq_cnt; i++)
+			adapter->mem_desc.rep_desc_q_arr[i].rep_desc_base_addr =
+				cpu_to_le64 ((u64)adapter->mem_desc
+					     .rep_desc_seg_maint[i / chunk]
+					     .rep_desc_maint[i % chunk]
+					     .rep_desc_dma);
+
+		init_req->msg_flg =
+			LEAPRAID_ADAPTER_INIT_MSGFLG_RDPQ_ARRAY_MODE;
+		init_req->rep_desc_q_arr_addr =
+			cpu_to_le64((u64)adapter->mem_desc.rep_desc_q_arr_dma);
+	} else {
+		init_req->rep_desc_q_arr_addr =
+			cpu_to_le64((u64)adapter->mem_desc
+					.rep_desc_seg_maint[0]
+					.rep_desc_maint[0]
+					.rep_desc_dma);
+	}
+	cur_time = ktime_get_real();
+	init_req->time_stamp = cpu_to_le64(ktime_to_ms(cur_time));
+}
+
+static int leapraid_send_adapter_init(struct leapraid_adapter *adapter)
+{
+	struct leapraid_adapter_init_req init_req;
+	struct leapraid_adapter_init_rep init_rep;
+	u16 adapter_status;
+	int rc;
+
+	leapraid_prepare_adapter_init_req(adapter, &init_req);
+
+	rc = leapraid_handshake_func(adapter,
+				     sizeof(struct leapraid_adapter_init_req),
+				     (u32 *)&init_req,
+				     sizeof(struct leapraid_adapter_init_rep),
+				     (u16 *)&init_rep);
+	if (rc != 0) {
+		dev_err(&adapter->pdev->dev, "%s: Handshake failed, rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	adapter_status =
+		le16_to_cpu(init_rep.adapter_status) &
+		LEAPRAID_ADAPTER_STATUS_MASK;
+	if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) {
+		dev_err(&adapter->pdev->dev, "%s: failed\n", __func__);
+		rc = -EIO;
+	}
+
+	return rc;
+}
+
+static int leapraid_cfg_pages(struct leapraid_adapter *adapter)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	union {
+		struct leapraid_manufacturing_p0 manufacturing_page0;
+		struct leapraid_bios_page3 bios_page3;
+		struct leapraid_bios_page2 bios_page2;
+	} cfg_page;
+	int rc;
+
+	rc = leapraid_op_config_page(adapter, &cfg_page.bios_page3, cfgp1,
+				     cfgp2, GET_BIOS_PG3);
+	if (rc)
+		return rc;
+
+	adapter->adapter_attr.bios_version =
+		le32_to_cpu(cfg_page.bios_page3.bios_version);
+
+	rc = leapraid_op_config_page(adapter, &cfg_page.bios_page2, cfgp1,
+				     cfgp2, GET_BIOS_PG2);
+	if (rc)
+		return rc;
+
+	adapter->boot_devs.requested_boot_dev.form =
+		cfg_page.bios_page2.requested_boot_dev_form;
+	memcpy(adapter->boot_devs.requested_boot_dev.pg_dev,
+	       &cfg_page.bios_page2.requested_boot_dev,
+	       LEAPRAID_BOOT_DEV_SIZE);
+	adapter->boot_devs.requested_alt_boot_dev.form =
+		cfg_page.bios_page2.requested_alt_boot_dev_form;
+	memcpy(adapter->boot_devs.requested_alt_boot_dev.pg_dev,
+	       &cfg_page.bios_page2.requested_alt_boot_dev,
+	       LEAPRAID_BOOT_DEV_SIZE);
+	adapter->boot_devs.current_boot_dev.form =
+		cfg_page.bios_page2.current_boot_dev_form;
+	memcpy(adapter->boot_devs.current_boot_dev.pg_dev,
+	       &cfg_page.bios_page2.current_boot_dev,
+	       LEAPRAID_BOOT_DEV_SIZE);
+
+	rc = leapraid_op_config_page(adapter, &cfg_page.manufacturing_page0,
+				     cfgp1, cfgp2, GET_MANUFACTURING_PG0);
+	if (rc)
+		return rc;
+
+	snprintf(adapter->adapter_attr.board_name,
+		 sizeof(adapter->adapter_attr.board_name),
+		 "%.*s",
+		 (int)sizeof(cfg_page.manufacturing_page0.board_name),
+		 cfg_page.manufacturing_page0.board_name);
+	return rc;
+}
+
+static int leapraid_evt_notify(struct leapraid_adapter *adapter)
+{
+	struct leapraid_evt_notify_req *evt_notify_req;
+	struct leapraid_evt_notify_rep *evt_notify_rep;
+	u16 adapter_status;
+	int rc = 0;
+	int i;
+
+	mutex_lock(&adapter->driver_cmds.notify_event_cmd.mutex);
+	adapter->driver_cmds.notify_event_cmd.status = LEAPRAID_CMD_PENDING;
+	evt_notify_req =
+		leapraid_get_task_desc(
+			adapter,
+			adapter->driver_cmds.notify_event_cmd.inter_taskid);
+	memset(evt_notify_req, 0, sizeof(struct leapraid_evt_notify_req));
+	evt_notify_req->func = LEAPRAID_FUNC_EVENT_NOTIFY;
+	for (i = 0; i < LEAPRAID_EVT_MASK_COUNT; i++)
+		evt_notify_req->evt_masks[i] =
+			cpu_to_le32(adapter->fw_evt_s.leapraid_evt_masks[i]);
+	init_completion(&adapter->driver_cmds.notify_event_cmd.done);
+	leapraid_fire_task(adapter,
+			   adapter->driver_cmds.notify_event_cmd.inter_taskid);
+	wait_for_completion_timeout(
+		&adapter->driver_cmds.notify_event_cmd.done,
+		LEAPRAID_NOTIFY_EVENT_CMD_TIMEOUT * HZ);
+
+	if (!(adapter->driver_cmds.notify_event_cmd.status &
+	      LEAPRAID_CMD_DONE)) {
+		rc = -EFAULT;
+		goto out_cleanup;
+	}
+
+	if (!(adapter->driver_cmds.notify_event_cmd.status &
+	      LEAPRAID_CMD_REPLY_VALID)) {
+		rc = -EFAULT;
+		goto out_cleanup;
+	}
+
+	evt_notify_rep = (void *)&adapter->driver_cmds.notify_event_cmd.reply;
+	adapter_status = le16_to_cpu(evt_notify_rep->adapter_status) &
+			 LEAPRAID_ADAPTER_STATUS_MASK;
+	if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS)
+		rc = -EFAULT;
+
+out_cleanup:
+	adapter->driver_cmds.notify_event_cmd.status = LEAPRAID_CMD_NOT_USED;
+	mutex_unlock(&adapter->driver_cmds.notify_event_cmd.mutex);
+
+	return rc;
+}
+
+int leapraid_scan_dev(struct leapraid_adapter *adapter, bool async_scan_dev)
+{
+	struct leapraid_scan_dev_req *scan_dev_req;
+	struct leapraid_scan_dev_rep *scan_dev_rep;
+	u16 adapter_status;
+	int rc = 0;
+
+	dev_info(&adapter->pdev->dev,
+		 "Send device scan, async_scan_dev=%d!\n", async_scan_dev);
+
+	adapter->driver_cmds.scan_dev_cmd.status = LEAPRAID_CMD_PENDING;
+	adapter->driver_cmds.scan_dev_cmd.async_scan_dev = async_scan_dev;
+	scan_dev_req =
+		leapraid_get_task_desc(
+			adapter,
+			adapter->driver_cmds.scan_dev_cmd.inter_taskid);
+	memset(scan_dev_req, 0, sizeof(struct leapraid_scan_dev_req));
+	scan_dev_req->func = LEAPRAID_FUNC_SCAN_DEV;
+
+	if (async_scan_dev) {
+		adapter->scan_dev_desc.first_scan_dev_fired = 1;
+		leapraid_fire_task(
+			adapter,
+			adapter->driver_cmds.scan_dev_cmd.inter_taskid);
+		return 0;
+	}
+
+	init_completion(&adapter->driver_cmds.scan_dev_cmd.done);
+	leapraid_fire_task(adapter,
+			   adapter->driver_cmds.scan_dev_cmd.inter_taskid);
+	wait_for_completion_timeout(&adapter->driver_cmds.scan_dev_cmd.done,
+				    LEAPRAID_SCAN_DEV_CMD_TIMEOUT * HZ);
+	if (!(adapter->driver_cmds.scan_dev_cmd.status & LEAPRAID_CMD_DONE)) {
+		dev_err(&adapter->pdev->dev, "Device scan timeout!\n");
+		if (adapter->driver_cmds.scan_dev_cmd.status &
+		    LEAPRAID_CMD_RESET)
+			rc = -EFAULT;
+		else
+			rc = -ETIME;
+		goto out_cleanup;
+	}
+
+	scan_dev_rep = (void *)&adapter->driver_cmds.scan_dev_cmd.reply;
+	adapter_status =
+		le16_to_cpu(scan_dev_rep->adapter_status) &
+		LEAPRAID_ADAPTER_STATUS_MASK;
+	if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) {
+		dev_err(&adapter->pdev->dev, "Device scan failure!\n");
+		rc = -EFAULT;
+		goto out_cleanup;
+	}
+
+out_cleanup:
+	adapter->driver_cmds.scan_dev_cmd.status = LEAPRAID_CMD_NOT_USED;
+	return rc;
+}
+
+static void leapraid_init_task_tracker(struct leapraid_adapter *adapter)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->dynamic_task_desc.task_lock, flags);
+
+	spin_unlock_irqrestore(&adapter->dynamic_task_desc.task_lock, flags);
+}
+
+static void leapraid_init_rep_msg_addr(struct leapraid_adapter *adapter)
+{
+	u32 reply_address;
+	unsigned int i;
+
+	for (i = 0, reply_address = (u32)adapter->mem_desc.rep_msg_dma;
+	     i < adapter->adapter_attr.rep_msg_qd;
+	     i++, reply_address += LEAPRAID_REPLY_SIZE)
+		adapter->mem_desc.rep_msg_addr[i] = cpu_to_le32(reply_address);
+}
+
+static void init_rep_desc(
+		struct leapraid_rq *rq,
+		int index,
+		union leapraid_rep_desc_union *reply_post_free_contig)
+{
+	struct leapraid_adapter *adapter = rq->adapter;
+	unsigned int i;
+
+	if (!reset_devices)
+		rq->rep_desc =
+			adapter->mem_desc
+				.rep_desc_seg_maint[index /
+					LEAPRAID_REP_DESC_CHUNK_SIZE]
+				.rep_desc_maint[index %
+					LEAPRAID_REP_DESC_CHUNK_SIZE]
+				.rep_desc;
+	else
+		rq->rep_desc = reply_post_free_contig;
+
+	rq->rep_post_host_idx = 0;
+	for (i = 0; i < adapter->adapter_attr.rep_desc_qd; i++)
+		rq->rep_desc[i].words = cpu_to_le64(ULLONG_MAX);
+}
+
+static void leapraid_init_rep_desc(struct leapraid_adapter *adapter)
+{
+	union leapraid_rep_desc_union *reply_post_free_contig;
+	struct leapraid_int_rq *int_rq;
+	struct leapraid_blk_mq_poll_rq *blk_mq_poll_rq;
+	unsigned int i;
+	int index;
+
+	index = 0;
+	reply_post_free_contig = adapter->mem_desc
+					.rep_desc_seg_maint[0]
+					.rep_desc_maint[0]
+					.rep_desc;
+
+	for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) {
+		int_rq = &adapter->notification_desc.int_rqs[i];
+		init_rep_desc(&int_rq->rq, index, reply_post_free_contig);
+		if (!reset_devices)
+			index++;
+		else
+			reply_post_free_contig +=
+				adapter->adapter_attr.rep_desc_qd;
+	}
+
+	for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) {
+		blk_mq_poll_rq = &adapter->notification_desc.blk_mq_poll_rqs[i];
+		init_rep_desc(&blk_mq_poll_rq->rq,
+			      index, reply_post_free_contig);
+		if (!reset_devices)
+			index++;
+		else
+			reply_post_free_contig +=
+				adapter->adapter_attr.rep_desc_qd;
+	}
+}
+
+static void leapraid_init_bar_idx_regs(struct leapraid_adapter *adapter)
+{
+	struct leapraid_int_rq *int_rq;
+	struct leapraid_blk_mq_poll_rq *blk_mq_poll_rq;
+	unsigned int i, j;
+
+	adapter->rep_msg_host_idx = adapter->adapter_attr.rep_msg_qd - 1;
+	writel(adapter->rep_msg_host_idx,
+	       &adapter->iomem_base->rep_msg_host_idx);
+
+	for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) {
+		int_rq = &adapter->notification_desc.int_rqs[i];
+		for (j = 0; j < REP_POST_HOST_IDX_REG_CNT; j++)
+			writel((int_rq->rq.msix_idx & 7) <<
+				LEAPRAID_RPHI_MSIX_IDX_SHIFT,
+				&adapter->iomem_base->rep_post_reg_idx[j].idx);
+	}
+
+	for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) {
+		blk_mq_poll_rq =
+			&adapter->notification_desc.blk_mq_poll_rqs[i];
+		for (j = 0; j < REP_POST_HOST_IDX_REG_CNT; j++)
+			writel((blk_mq_poll_rq->rq.msix_idx & 7) <<
+				LEAPRAID_RPHI_MSIX_IDX_SHIFT,
+				&adapter->iomem_base->rep_post_reg_idx[j].idx);
+	}
+}
+
+static int leapraid_make_adapter_available(struct leapraid_adapter *adapter)
+{
+	int rc;
+
+	leapraid_init_task_tracker(adapter);
+	leapraid_init_rep_msg_addr(adapter);
+
+	if (adapter->scan_dev_desc.driver_loading)
+		leapraid_configure_reply_queue_affinity(adapter);
+
+	leapraid_init_rep_desc(adapter);
+	rc = leapraid_send_adapter_init(adapter);
+	if (rc)
+		return rc;
+
+	leapraid_init_bar_idx_regs(adapter);
+	leapraid_unmask_int(adapter);
+	rc = leapraid_cfg_pages(adapter);
+	if (rc)
+		return rc;
+
+	rc = leapraid_evt_notify(adapter);
+	if (rc)
+		return rc;
+
+	if (!adapter->access_ctrl.shost_recovering) {
+		adapter->scan_dev_desc.wait_scan_dev_done = 1;
+		return 0;
+	}
+
+	return leapraid_scan_dev(adapter, false);
+}
+
+int leapraid_ctrl_init(struct leapraid_adapter *adapter)
+{
+	u32 cap;
+	int rc;
+
+	rc = leapraid_init_driver_cmds(adapter);
+	if (rc) {
+		dev_err(&adapter->pdev->dev, "Init driver cmds failure\n");
+		goto out_free;
+	}
+
+	rc = leapraid_set_pcie_and_notification(adapter);
+	if (rc)
+		goto out_free;
+
+	pci_set_drvdata(adapter->pdev, adapter->shost);
+
+	pcie_capability_read_dword(adapter->pdev, PCI_EXP_DEVCAP, &cap);
+
+	if (cap & PCI_EXP_DEVCAP_EXT_TAG)
+		pcie_capability_set_word(adapter->pdev, PCI_EXP_DEVCTL,
+					 PCI_EXP_DEVCTL_EXT_TAG);
+
+	rc = leapraid_fw_log_init(adapter);
+	if (rc) {
+		dev_err(&adapter->pdev->dev, "FW log init failure\n");
+		goto out_free;
+	}
+
+	rc = leapraid_request_host_memory(adapter);
+	if (rc) {
+		dev_err(&adapter->pdev->dev, "Request host memory failure\n");
+		goto out_free;
+	}
+
+	init_waitqueue_head(&adapter->reset_desc.reset_wait_queue);
+	init_waitqueue_head(&adapter->access_ctrl.shost_recover_wq);
+
+	rc = leapraid_alloc_dev_topo_bitmaps(adapter);
+	if (rc) {
+		dev_err(&adapter->pdev->dev, "Alloc topo bitmaps failure\n");
+		goto out_free;
+	}
+
+	leapraid_init_event_mask(adapter);
+
+	rc = leapraid_make_adapter_available(adapter);
+	if (rc) {
+		dev_err(&adapter->pdev->dev,
+			"Make adapter available failure\n");
+		goto out_free;
+	}
+
+	leapraid_overheat_init(adapter);
+	if (!adapter->overheat_desc.fault_overheat_wq) {
+		rc = -ENOMEM;
+		goto out_free;
+	}
+
+	return 0;
+
+out_free:
+	adapter->access_ctrl.host_removing = 1;
+	leapraid_fw_log_exit(adapter);
+	leapraid_disable_controller(adapter);
+	leapraid_free_host_memory(adapter);
+	leapraid_free_dev_topo_bitmaps(adapter);
+	pci_set_drvdata(adapter->pdev, NULL);
+	return rc;
+}
+
+void leapraid_remove_ctrl(struct leapraid_adapter *adapter)
+{
+	leapraid_overheat_cleanup(adapter);
+	leapraid_check_scheduled_fault_stop(adapter);
+	leapraid_fw_log_stop(adapter);
+	leapraid_fw_log_exit(adapter);
+	leapraid_disable_controller(adapter);
+	leapraid_free_host_memory(adapter);
+	leapraid_free_dev_topo_bitmaps(adapter);
+	leapraid_free_enc_list(adapter);
+	pci_set_drvdata(adapter->pdev, NULL);
+}
diff --git a/drivers/scsi/leapraid/leapraid_func.h b/drivers/scsi/leapraid/leapraid_func.h
new file mode 100644
index 000000000000..923596211aa1
--- /dev/null
+++ b/drivers/scsi/leapraid/leapraid_func.h
@@ -0,0 +1,1486 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 LeapIO Tech Inc.
+ *
+ * LeapRAID storage and RAID controller driver.
+ */
+#ifndef LEAPRAID_FUNC_H_INCLUDED
+#define LEAPRAID_FUNC_H_INCLUDED
+
+#include <linux/pci.h>
+#include <linux/aer.h>
+#include <linux/poll.h>
+#include <linux/atomic.h>
+#include <linux/errno.h>
+#include <linux/ktime.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsicam.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport_sas.h>
+
+#include "leapraid.h"
+
+/* Request and reply buffer size. */
+#define LEAPRAID_REQUEST_SIZE                   128
+#define LEAPRAID_REPLY_SIZE                     128
+#define LEAPRAID_CHAIN_SEG_SIZE                 128
+#define LEAPRAID_MAX_SGES_IN_CHAIN              7
+#define LEAPRAID_DEFAULT_CHAINS_PER_IO          19
+#define LEAPRAID_DEFAULT_DIX_CHAINS_PER_IO      \
+	(2 * LEAPRAID_DEFAULT_CHAINS_PER_IO) /* TODO DIX */
+#define LEAPRAID_IEEE_SGE64_ENTRY_SIZE          16
+#define LEAPRAID_REP_DESC_CHUNK_SIZE            16
+#define LEAPRAID_REP_DESC_ENTRY_SIZE            8
+#define LEAPRAID_REP_MSG_ADDR_SIZE              4
+#define LEAPRAID_REP_RQ_CNT_SIZE                16
+
+#define LEAPRAID_SYS_LOG_BUF_SIZE       0x200000
+#define LEAPRAID_SYS_LOG_BUF_RESERVE    0x1000
+
+/* Driver version and name. */
+#define LEAPRAID_DRIVER_NAME            "LeapRAID"
+#define LEAPRAID_NAME_LENGTH            48
+#define LEAPRAID_BOARD_NAME_LENGTH      17
+#define LEAPRAID_AUTHOR                 "LeapIO Inc."
+#define LEAPRAID_DESCRIPTION            "LeapRAID Driver"
+#define LEAPRAID_DRIVER_VERSION         "2.00.01.09"
+#define LEAPRAID_MAJOR_VERSION          2
+#define LEAPRAID_MINOR_VERSION          0
+#define LEAPRAID_BUILD_VERSION          1
+#define LEAPRAID_RELEASE_VERSION        9
+#define LEAPRAID_MSG_VERSION            0x1021
+#define LEAPRAID_HEADER_VERSION         0x0000
+
+/* Device ID. */
+#define LEAPRAID_VENDOR_ID      0xD405
+#define LEAPRAID_DEVID_HBA      0x8200
+#define LEAPRAID_SUBVENDOR_ID   0xD405
+#define LEAPRAID_SUBDEVID_HBA   0x8200
+
+#define LEAPRAID_PCI_VENDOR_ID_MASK     0xFFFF
+
+ /* RAID virtual channel ID. */
+#define RAID_CHANNEL    1
+
+/* Scatter-gather (SG) segment limits. */
+#define LEAPRAID_MAX_PHYS_SEGMENTS      SG_CHUNK_SIZE
+
+#define LEAPRAID_KDUMP_MIN_PHYS_SEGMENTS        32
+#define LEAPRAID_SG_DEPTH                       LEAPRAID_MAX_PHYS_SEGMENTS
+
+/* Firmware and config page operations. */
+#define LEAPRAID_CFG_REQ_RETRY_TIMES    2
+
+/* Hardware access helpers. */
+#define leapraid_readl(addr) readl(addr)
+#define leapraid_check_reset(status) \
+	(!((status) & LEAPRAID_CMD_RESET))
+
+/* Polling intervals. */
+#define LEAPRAID_PCIE_LOG_POLLING_INTERVAL      1
+#define LEAPRAID_FAULT_POLLING_INTERVAL         1000
+
+/* Init mask. */
+#define LEAPRAID_RESET_IRQ_MASK 0x40000000
+#define LEAPRAID_REPLY_INT_MASK 0x00000008
+#define LEAPRAID_TO_SYS_DB_MASK 0x00000001
+
+/* Queue depth. */
+#define LEAPRAID_SATA_QUEUE_DEPTH       32
+#define LEAPRAID_SAS_QUEUE_DEPTH        64
+#define LEAPRAID_RAID_QUEUE_DEPTH       64
+
+/* Target probe flag. */
+#define LEAPRAID_NO_ULD_ATTACH_FLAG 1
+
+/* SCSI device and queue limits. */
+#define LEAPRAID_MAX_SECTORS            2048
+#define LEAPRAID_MAX_CDB_LEN            32
+#define LEAPRAID_MAX_LUNS               16384
+#define LEAPRAID_CAN_QUEUE_MIN          1
+#define LEAPRAID_THIS_ID_NONE           -1
+#define LEAPRAID_CMD_PER_LUN            128
+#define LEAPRAID_MAX_SEGMENT_SIZE       0xffffffff
+
+/* SCSI sense and ASC/ASCQ and disk geometry configuration. */
+#define DESC_FORMAT_THRESHOLD                   0x72
+#define SENSE_KEY_MASK                          0x0F
+#define SCSI_SENSE_RESPONSE_CODE_MASK           0x7F
+#define ASC_FAILURE_PREDICTION_THRESHOLD_EXCEEDED       0x5D
+#define LEAPRAID_LARGE_DISK_THRESHOLD           0x200000UL
+#define LEAPRAID_LARGE_DISK_HEADS               255
+#define LEAPRAID_LARGE_DISK_SECTORS             63
+#define LEAPRAID_SMALL_DISK_HEADS               64
+#define LEAPRAID_SMALL_DISK_SECTORS             32
+
+/* SMP (Serial Management Protocol). */
+#define LEAPRAID_SMP_PT_FLAG_SGL_PTR            0x80
+#define LEAPRAID_SMP_FRAME_HEADER_SIZE          4
+#define LEAPRAID_SCSI_HOST_SHIFT                16
+#define LEAPRAID_SCSI_DRIVER_SHIFT              24
+
+/* SCSI ASC/ASCQ definitions. */
+#define LEAPRAID_SCSI_ASCQ_DEFAULT              0x00
+#define LEAPRAID_SCSI_ASC_POWER_ON_RESET        0x29
+#define LEAPRAID_SCSI_ASC_INVALID_CMD_CODE      0x20
+#define LEAPRAID_SCSI_ASCQ_POWER_ON_RESET       0x07
+
+/* VPD Page 0x89 (ATA Information). */
+#define LEAPRAID_VPD_PAGE_ATA_INFO      0x89
+#define LEAPRAID_VPD_PG89_MAX_LEN       255
+#define LEAPRAID_VPD_PG89_MIN_LEN       214
+
+/* Byte index for NCQ support flag in VPD page 0x89. */
+#define LEAPRAID_VPD_PG89_NCQ_BYTE_IDX  213
+#define LEAPRAID_VPD_PG89_NCQ_BIT_SHIFT 4
+#define LEAPRAID_VPD_PG89_NCQ_BIT_MASK  0x1
+
+/* Readiness polling: max retries, sleep µs between. */
+#define LEAPRAID_ADAPTER_READY_MAX_RETRY        15000
+#define LEAPRAID_ADAPTER_READY_SLEEP_MIN_US     1000
+#define LEAPRAID_ADAPTER_READY_SLEEP_MAX_US     1100
+
+/* Doorbell wait parameters. */
+#define LEAPRAID_DB_WAIT_MAX_RETRY      20000
+#define LEAPRAID_DB_WAIT_DELAY_US       500
+
+/* Basic data size definitions. */
+#define LEAPRAID_DWORDS_BYTE_SIZE       4
+#define LEAPRAID_WORD_BYTE_SIZE         2
+
+/* SGL threshold and chain offset. */
+#define LEAPRAID_SGL_INLINE_THRESHOLD   2
+#define LEAPRAID_CHAIN_OFFSET_DWORDS    7
+
+/* MSI-X group size and mask. */
+#define LEAPRAID_MSIX_GROUP_SIZE        8
+#define LEAPRAID_MSIX_GROUP_MASK        7
+
+/* Basic constants and limits.   */
+#define LEAPRAID_BUSY_LIMIT             1
+#define LEAPRAID_INVALID_HOST_DIAG_VAL  0xFFFFFFFF
+
+/* Retry/Sleep configuration. */
+#define LEAPRAID_WRSEQ_FLUSH_KEY_VALUE  0x0
+#define LEAPRAID_WRSEQ_1ST_KEY_VALUE    0xF
+#define LEAPRAID_WRSEQ_2ND_KEY_VALUE    0x4
+#define LEAPRAID_WRSEQ_3RD_KEY_VALUE    0xB
+#define LEAPRAID_WRSEQ_4TH_KEY_VALUE    0x2
+#define LEAPRAID_WRSEQ_5TH_KEY_VALUE    0x7
+#define LEAPRAID_WRSEQ_6TH_KEY_VALUE    0xD
+#define LEAPRAID_UNLOCK_RETRY_LIMIT     20
+#define LEAPRAID_UNLOCK_SLEEP_MS        100
+#define LEAPRAID_MSLEEP_NORMAL_MS       100
+#define LEAPRAID_MSLEEP_EXTRA_LONG_MS   500
+#define LEAPRAID_IO_POLL_DELAY_US       500
+
+/* Device/Volume configuration. */
+#define LEAPRAID_MAX_VOLUMES_DEFAULT    32
+#define LEAPRAID_MAX_DEV_HANDLE_DEFAULT 2048
+#define LEAPRAID_INVALID_DEV_HANDLE     0xFFFF
+
+/* Commands queue depth. */
+#define LEAPRAID_DEFAULT_CMD_QD_OFFSET  64
+#define LEAPRAID_REPLY_QD_ALIGNMENT     16
+/* Task ID offset. */
+#define LEAPRAID_TASKID_OFFSET_CTRL_CMD         1
+#define LEAPRAID_TASKID_OFFSET_CFG_OP_CMD               1
+#define LEAPRAID_TASKID_OFFSET_TRANSPORT_CMD            2
+#define LEAPRAID_TASKID_OFFSET_ENC_CMD                  3
+#define LEAPRAID_TASKID_OFFSET_NOTIFY_EVENT_CMD         4
+
+/* Task ID offset for high-priority. */
+#define LEAPRAID_HP_TASKID_OFFSET_CTL_CMD       0
+#define LEAPRAID_HP_TASKID_OFFSET_TM_CMD        1
+
+/* Event/Boot configuration. */
+#define LEAPRAID_EVT_MASK_COUNT 4
+#define LEAPRAID_BOOT_DEV_SIZE  24
+
+/* Commands timeout. */
+#define LEAPRAID_UNIFIED_TIMEOUT               30
+#define LEAPRAID_CFG_OP_TIMEOUT                LEAPRAID_UNIFIED_TIMEOUT
+#define LEAPRAID_CTL_CMD_TIMEOUT               LEAPRAID_UNIFIED_TIMEOUT
+#define LEAPRAID_SCAN_DEV_CMD_TIMEOUT          300
+#define LEAPRAID_ENC_CMD_TIMEOUT               LEAPRAID_UNIFIED_TIMEOUT
+#define LEAPRAID_IO_CMD_TIMEOUT                LEAPRAID_UNIFIED_TIMEOUT
+#define LEAPRAID_NOTIFY_EVENT_CMD_TIMEOUT      LEAPRAID_UNIFIED_TIMEOUT
+#define LEAPRAID_TM_CMD_TIMEOUT                LEAPRAID_UNIFIED_TIMEOUT
+#define LEAPRAID_TRANSPORT_CMD_TIMEOUT         LEAPRAID_UNIFIED_TIMEOUT
+
+/* Host DMA cap. */
+#define DMA_32_BITS    32
+#define DMA_64_BITS    64
+#define LEAPRAID_DMA_ALIGN    16
+
+/* Values used to represent an invalid. */
+#define LEAPRAID_INVALID_INDEX         -1
+#define LEAPRAID_INVALID_MSIX_VECTORS  -1
+#define LEAPRAID_INVALID_INITIAL_VALUE -1
+#define LEAPRAID_OPERATION_FAILED      -1
+
+/* Version shift and mask. */
+#define LEAPRAID_VER_MAJOR_SHIFT 24
+#define LEAPRAID_VER_MINOR_SHIFT 16
+#define LEAPRAID_VER_BUILD_SHIFT 8
+#define LEAPRAID_VER_MASK        0xFF
+
+/* Interrupt type. */
+#define LEAPRAID_INTERRUPT_MODE_MSIX 0
+#define LEAPRAID_INTERRUPT_MODE_MSI  1
+#define LEAPRAID_INTERRUPT_MODE_LEGACY 2
+
+/* SMP link reset. */
+#define SMP_PHY_CONTROL_LINK_RESET    0x01
+#define SMP_PHY_CONTROL_HARD_RESET    0x02
+#define SMP_PHY_CONTROL_DISABLE       0x03
+
+/**
+ * struct leapraid_adapter_features - Adapter features/capabilities.
+ *
+ * @req_slot: Number of request slots supported by the adapter.
+ * @hp_slot: Number of high-priority slots supported by the adapter.
+ * @adapter_caps: Adapter capabilities.
+ * @fw_version: Firmware version of the adapter.
+ * @max_dev_handle: Maximum device handle supported by the adapter.
+ * @min_dev_handle: Minimum device handle supported by the adapter.
+ */
+struct leapraid_adapter_features {
+	u16 req_slot;
+	u16 hp_slot;
+	u32 adapter_caps;
+	u32 fw_version;
+	u8 max_msix_vectors;
+	u8 max_volumes;
+	u16 max_dev_handle;
+	u16 min_dev_handle;
+	u16 msg_ver;
+	u16 product_id;
+};
+
+/**
+ * struct leapraid_adapter_attr - Adapter attributes and capabilities
+ *
+ * @id: Adapter identifier.
+ * @raid_support: Indicates if RAID is supported.
+ * @bios_version: Version of the adapter BIOS.
+ * @enable_mp: Indicates if multipath (MP) support is enabled.
+ * @wideport_max_queue_depth: Maximum queue depth for wide ports.
+ * @narrowport_max_queue_depth: Maximum queue depth for narrow ports.
+ * @sata_max_queue_depth: Maximum queue depth for SATA.
+ * @raid_volume_max_queue_depth: Maximum queue depth for RAID volumes.
+ * @features: Detailed features of the adapter.
+ * @adapter_total_qd: Total queue depth available on the adapter.
+ * @io_qd: Queue depth allocated for I/O operations.
+ * @rep_msg_qd: Queue depth for reply messages.
+ * @rep_desc_qd: Queue depth for reply descriptors.
+ * @rep_desc_q_seg_cnt: Number of segments in a reply descriptor queue.
+ * @rq_cnt: Number of request queues.
+ * @task_desc_dma_size: Size of task descriptor DMA memory.
+ * @use_32_dma_mask: Indicates if 32-bit DMA mask is used.
+ * @name: Adapter name string.
+ * @board_name: Board name retrieved from manufacturing page 0.
+ */
+struct leapraid_adapter_attr {
+	u8 id;
+	u8 raid_support;
+	u32 bios_version;
+	u8 enable_mp;
+	u32 wideport_max_queue_depth;
+	u32 narrowport_max_queue_depth;
+	u32 sata_max_queue_depth;
+	u32 raid_volume_max_queue_depth;
+	struct leapraid_adapter_features features;
+	u32 adapter_total_qd;
+	u32 io_qd;
+	u32 rep_msg_qd;
+	u32 rep_desc_qd;
+	u32 rep_desc_q_seg_cnt;
+	u16 rq_cnt;
+	u32 task_desc_dma_size;
+	u8 use_32_dma_mask;
+	char name[LEAPRAID_NAME_LENGTH];
+	char board_name[LEAPRAID_BOARD_NAME_LENGTH];
+};
+
+/**
+ * struct leapraid_io_req_tracker - Track a SCSI I/O request for the adapter
+ *
+ * @taskid: Unique task ID for this I/O request.
+ * @scmd: Pointer to the associated SCSI command.
+ * @chain_list: List of chain frames associated with this request.
+ * @msix_io: MSI-X vector assigned to this I/O request.
+ * @chain: Pointer to the chain memory for this request.
+ * @chain_dma: DMA address of the chain memory.
+ */
+struct leapraid_io_req_tracker {
+	u16 taskid;
+	struct scsi_cmnd *scmd;
+	struct list_head chain_list;
+	u16 msix_io;
+	void *chain;
+	dma_addr_t chain_dma;
+};
+
+/**
+ * struct leapraid_task_tracker - Tracks a task in the adapter
+ *
+ * @taskid: Unique task ID for this tracker.
+ * @cb_idx: Callback index associated with this task.
+ * @tracker_list: Linked list node to chain this tracker in lists.
+ */
+struct leapraid_task_tracker {
+	u16 taskid;
+	u8 cb_idx;
+	struct list_head tracker_list;
+};
+
+/**
+ * struct leapraid_rep_desc_maint - Maintains reply descriptor memory
+ *
+ * @rep_desc: Pointer to the reply descriptor.
+ * @rep_desc_dma: DMA address of the reply descriptor.
+ */
+struct leapraid_rep_desc_maint {
+	union leapraid_rep_desc_union *rep_desc;
+	dma_addr_t rep_desc_dma;
+};
+
+/**
+ * struct leapraid_rep_desc_seg_maint -
+ * Maintains reply descriptor segment memory
+ *
+ * @rep_desc_seg: Pointer to the reply descriptor segment.
+ * @rep_desc_seg_dma: DMA address of the reply descriptor segment.
+ * @rep_desc_maint: Pointer to the main reply descriptor structure.
+ */
+struct leapraid_rep_desc_seg_maint {
+	void *rep_desc_seg;
+	dma_addr_t rep_desc_seg_dma;
+	struct leapraid_rep_desc_maint *rep_desc_maint;
+};
+
+/**
+ * struct leapraid_mem_desc - Memory descriptor for LeapRAID adapter
+ *
+ * @task_desc: Pointer to task descriptor.
+ * @task_desc_dma: DMA address of task descriptor.
+ * @sg_chain_pool: DMA pool for SGL chain allocations.
+ * @sg_chain_pool_size: Size of the sg_chain_pool.
+ * @taskid_to_uniq_tag: Mapping from task ID to unique tag.
+ * @sense_data: Buffer for SCSI sense data.
+ * @sense_data_dma: DMA address of sense_data buffer.
+ * @rep_msg: Buffer for reply message.
+ * @rep_msg_dma: DMA address of reply message buffer.
+ * @rep_msg_addr: Pointer to reply message address.
+ * @rep_msg_addr_dma: DMA address of reply message address.
+ * @rep_desc_seg_maint: Pointer to reply descriptor segment.
+ * @rep_desc_q_arr: Pointer to reply descriptor queue array.
+ * @rep_desc_q_arr_dma: DMA address of reply descriptor queue array.
+ */
+struct leapraid_mem_desc {
+	void *task_desc;
+	dma_addr_t task_desc_dma;
+	struct dma_pool *sg_chain_pool;
+	u16 sg_chain_pool_size;
+	u16 *taskid_to_uniq_tag;
+	u8 *sense_data;
+	dma_addr_t sense_data_dma;
+	u8 *rep_msg;
+	dma_addr_t rep_msg_dma;
+	__le32 *rep_msg_addr;
+	dma_addr_t rep_msg_addr_dma;
+	struct leapraid_rep_desc_seg_maint *rep_desc_seg_maint;
+	struct leapraid_rep_desc_q_arr *rep_desc_q_arr;
+	dma_addr_t rep_desc_q_arr_dma;
+};
+
+/* internal cmd description */
+#define LEAPRAID_FIXED_INTER_CMDS       5
+#define LEAPRAID_FIXED_HP_CMDS          2
+
+#define LEAPRAID_CMD_NOT_USED           0x8000
+#define LEAPRAID_CMD_DONE               0x0001
+#define LEAPRAID_CMD_PENDING            0x0002
+#define LEAPRAID_CMD_REPLY_VALID        0x0004
+#define LEAPRAID_CMD_RESET              0x0008
+
+/**
+ * enum LEAPRAID_CB_INDEX - Callback index for LeapRAID driver
+ *
+ * @LEAPRAID_SCAN_DEV_CB_IDX: Scan device callback index.
+ * @LEAPRAID_CONFIG_CB_IDX: Configuration callback index.
+ * @LEAPRAID_TRANSPORT_CB_IDX: Transport callback index.
+ * @LEAPRAID_SAS_CTRL_CB_IDX: SAS controller callback index.
+ * @LEAPRAID_ENC_CB_IDX: Encryption callback index.
+ * @LEAPRAID_NOTIFY_EVENT_CB_IDX: Notify event callback index.
+ * @LEAPRAID_CTL_CB_IDX: Control callback index.
+ * @LEAPRAID_TM_CB_IDX: Task management callback index.
+ */
+enum LEAPRAID_CB_INDEX {
+	LEAPRAID_SCAN_DEV_CB_IDX	= 0x1,
+	LEAPRAID_CONFIG_CB_IDX		= 0x2,
+	LEAPRAID_TRANSPORT_CB_IDX	= 0x3,
+	LEAPRAID_SAS_CTRL_CB_IDX	= 0x5,
+	LEAPRAID_ENC_CB_IDX		= 0x6,
+	LEAPRAID_NOTIFY_EVENT_CB_IDX	= 0x7,
+	LEAPRAID_CTL_CB_IDX		= 0x8,
+	LEAPRAID_TM_CB_IDX		= 0x9,
+	LEAPRAID_NUM_CB_IDXS
+};
+
+/**
+ * struct leapraid_default_reply - Default reply frame buffer
+ * @pad: Raw reply data buffer returned by firmware.
+ *
+ * This structure represents a generic reply frame used by the firmware
+ * when no specific reply format is required. The buffer size is defined
+ * by LEAPRAID_REPLY_SIZE and stores the raw reply data.
+ */
+struct leapraid_default_reply {
+	u8 pad[LEAPRAID_REPLY_SIZE];
+};
+
+/**
+ * struct leapraid_sense_buffer - SCSI sense data buffer
+ * @pad: Buffer used to store sense data returned by a SCSI command.
+ *
+ * This buffer holds the sense data provided by a device.The size is
+ * defined by SCSI_SENSE_BUFFERSIZE.
+ */
+struct leapraid_sense_buffer {
+	u8 pad[SCSI_SENSE_BUFFERSIZE];
+};
+
+/**
+ * struct leapraid_driver_cmd - Driver command tracking structure
+ *
+ * @reply: Default reply structure returned by the adapter.
+ * @done: Completion object used to signal command completion.
+ * @status: Status code returned by the firmware.
+ * @taskid: Unique task identifier for this command.
+ * @hp_taskid: Task identifier for high-priority commands.
+ * @inter_taskid: Task identifier for internal commands.
+ * @cb_idx: Callback index used to identify completion context.
+ * @async_scan_dev: True if this command is for asynchronous device scan.
+ * @sense: Sense buffer holding error information from device.
+ * @mutex: Mutex to protect access to this command structure.
+ * @list: List node for linking driver commands into lists.
+ */
+struct leapraid_driver_cmd {
+	struct leapraid_default_reply reply;
+	struct completion done;
+	u16 status;
+	u16 taskid;
+	u16 hp_taskid;
+	u16 inter_taskid;
+	u8 cb_idx;
+	u8 async_scan_dev;
+	struct leapraid_sense_buffer sense;
+	struct mutex mutex; /* Mutex for driver cmd */
+	struct list_head list;
+};
+
+/**
+ * struct leapraid_driver_cmds - Collection of driver command objects
+ *
+ * @special_cmd_list: List head for tracking special driver commands.
+ * @scan_dev_cmd: Command used for asynchronous device scan operations.
+ * @cfg_op_cmd: Command for configuration operations.
+ * @transport_cmd: Command for transport-level operations.
+ * @enc_cmd: Command for enclosure management operations.
+ * @notify_event_cmd: Command for asynchronous event notification handling.
+ * @ctl_cmd: Command for generic control or maintenance operations.
+ * @tm_cmd: Task management command.
+ */
+struct leapraid_driver_cmds {
+	struct list_head special_cmd_list;
+	struct leapraid_driver_cmd scan_dev_cmd;
+	struct leapraid_driver_cmd cfg_op_cmd;
+	struct leapraid_driver_cmd transport_cmd;
+	struct leapraid_driver_cmd enc_cmd;
+	struct leapraid_driver_cmd notify_event_cmd;
+	struct leapraid_driver_cmd ctl_cmd;
+	struct leapraid_driver_cmd tm_cmd;
+};
+
+/**
+ * struct leapraid_dynamic_task_desc - Dynamic task descriptor
+ *
+ * @task_lock: Spinlock to protect concurrent access.
+ * @hp_taskid: Current high-priority task ID.
+ * @hp_cmd_qd: Fixed command queue depth for high-priority tasks.
+ * @inter_taskid: Current internal task ID.
+ * @inter_cmd_qd: Fixed command queue depth for internal tasks.
+ */
+struct leapraid_dynamic_task_desc {
+	spinlock_t task_lock; /* protects dynamic task */
+	u16 hp_taskid;
+	u16 hp_cmd_qd;
+	u16 inter_taskid;
+	u16 inter_cmd_qd;
+};
+
+/**
+ * struct leapraid_fw_evt_work - Firmware event work structure
+ *
+ * @list: Linked list node for queuing the work.
+ * @adapter: Pointer to the associated LeapRAID adapter.
+ * @work: Work structure used by the kernel workqueue.
+ * @refcnt: Reference counter for managing the lifetime of this work.
+ * @evt_data: Pointer to firmware event data.
+ * @dev_handle: Device handle associated with the event.
+ * @evt_type: Type of firmware event.
+ * @ignore: Flag indicating whether the event should be ignored.
+ */
+struct leapraid_fw_evt_work {
+	struct list_head list;
+	struct leapraid_adapter *adapter;
+	struct work_struct work;
+	struct kref refcnt;
+	void *evt_data;
+	u16 dev_handle;
+	u16 evt_type;
+	u8 ignore;
+};
+
+/**
+ * struct leapraid_fw_evt_struct - Firmware event handling structure
+ *
+ * @fw_evt_name: Name of the firmware event.
+ * @fw_evt_thread: Workqueue used for processing firmware events.
+ * @fw_evt_lock: Spinlock protecting access to the firmware event list.
+ * @fw_evt_list: Linked list of pending firmware events.
+ * @cur_evt: Pointer to the currently processing firmware event.
+ * @cur_evt_task: Task currently executing @cur_evt work.
+ * @fw_evt_cleanup: Flag indicating whether cleanup of events is in progress.
+ * @leapraid_evt_masks: Array of event masks for filtering firmware events.
+ */
+struct leapraid_fw_evt_struct {
+	u32 leapraid_evt_masks[4];
+	char fw_evt_name[48];
+	struct workqueue_struct *fw_evt_thread;
+	spinlock_t fw_evt_lock; /* protects firmware event */
+	struct list_head fw_evt_list;
+	struct leapraid_fw_evt_work *cur_evt;
+	struct task_struct *cur_evt_task;
+	u8 fw_evt_cleanup;
+};
+
+/**
+ * struct leapraid_rq - Represents a LeapRAID request queue
+ *
+ * @adapter: Pointer to the associated LeapRAID adapter.
+ * @msix_idx: MSI-X vector index used by this queue.
+ * @rep_post_host_idx: Index of the last processed reply descriptor.
+ * @rep_desc: Pointer to the reply descriptor associated with this queue.
+ * @name: Name of the request queue.
+ * @busy: Atomic counter indicating if the queue is busy.
+ */
+struct leapraid_rq {
+	struct leapraid_adapter *adapter;
+	u8 msix_idx;
+	u32 rep_post_host_idx;
+	union leapraid_rep_desc_union *rep_desc;
+	char name[LEAPRAID_NAME_LENGTH];
+	atomic_t busy;
+};
+
+/**
+ * struct leapraid_int_rq - Internal request queue for a CPU
+ *
+ * @affinity_hint: CPU affinity mask for the queue.
+ * @rq: Underlying LeapRAID request queue structure.
+ */
+struct leapraid_int_rq {
+	cpumask_var_t affinity_hint;
+	struct leapraid_rq rq;
+};
+
+/**
+ * struct leapraid_blk_mq_poll_rq - Polling request for LeapRAID blk-mq
+ *
+ * @busy: Atomic flag indicating request is being processed.
+ * @pause: Atomic flag to temporarily suspend polling.
+ * @rq: The underlying LeapRAID request structure.
+ */
+struct leapraid_blk_mq_poll_rq {
+	atomic_t busy;
+	atomic_t pause;
+	struct leapraid_rq rq;
+};
+
+/**
+ * struct leapraid_notification_desc - Notification descriptor for LeapRAID
+ *
+ * @iopoll_qdex: Index of the I/O polling queue.
+ * @iopoll_qcnt: Count of I/O polling queues.
+ * @msix_enable: Flag indicating MSI-X is enabled.
+ * @irq_vectors_allocated: Flag indicating PCI IRQ vectors are allocated.
+ * @irq_mode: Actual interrupt mode used for allocated PCI IRQ vectors.
+ * @msix_cpu_map: CPU-ID indexed MSI-X queue map.
+ * @msix_cpu_map_sz: Number of CPU-ID slots allocated in @msix_cpu_map.
+ * @int_rqs: Array of interrupt request queues.
+ * @int_rqs_allocated: Count of allocated interrupt request queues.
+ * @blk_mq_poll_rqs: Array of blk-mq polling requests.
+ */
+struct leapraid_notification_desc {
+	u32 iopoll_qdex;
+	u32 iopoll_qcnt;
+	u8 msix_enable;
+	u8 irq_vectors_allocated;
+	u8 irq_mode;
+	u8 *msix_cpu_map;
+	u32 msix_cpu_map_sz;
+	struct leapraid_int_rq *int_rqs;
+	u32 int_rqs_allocated;
+	struct leapraid_blk_mq_poll_rq *blk_mq_poll_rqs;
+};
+
+/**
+ * struct leapraid_overheat_desc - Overheat descriptor for LeapRAID
+ *
+ * @fault_overheat_wq: Workqueue for adapter thermal overheat operations.
+ * @fault_overheat_work: Work structure for thermal overheat.
+ * @thermal_alert: Flag indicating if adapter thermal alert is active.
+ * @fault_overheat_wq_name: Name of the fault overheat workqueue.
+ */
+struct leapraid_overheat_desc {
+	struct workqueue_struct *fault_overheat_wq;
+	struct work_struct fault_overheat_work;
+	atomic_t thermal_alert;
+	char fault_overheat_wq_name[48];
+};
+
+/**
+ * struct leapraid_reset_desc - Reset descriptor for LeapRAID
+ *
+ * @fault_reset_wq: Workqueue for fault reset operations.
+ * @fault_reset_work: Delayed work structure for fault reset.
+ * @fault_reset_wq_name: Name of the fault reset workqueue.
+ * @host_diag_mutex: Mutex for host diagnostic operations.
+ * @adapter_reset_lock: Spinlock for adapter reset operations.
+ * @adapter_reset_mutex: Mutex for adapter reset operations.
+ * @adapter_link_resetting: Flag indicating if adapter link is resetting.
+ * @adapter_reset_results: Results of the adapter reset operation.
+ * @pending_io_cnt: Count of pending I/O operations.
+ * @reset_wait_queue: Wait queue for reset operations.
+ * @reset_cnt: Counter for reset operations.
+ */
+struct leapraid_reset_desc {
+	struct workqueue_struct *fault_reset_wq;
+	struct delayed_work fault_reset_work;
+	char fault_reset_wq_name[48];
+	struct mutex host_diag_mutex; /* Mutex for operations. */
+	spinlock_t adapter_reset_lock; /* Protects adapter reset state. */
+	struct mutex adapter_reset_mutex; /* Serializes adapter reset. */
+	u8 adapter_link_resetting;
+	int adapter_reset_results;
+	int pending_io_cnt;
+	wait_queue_head_t reset_wait_queue;
+	u32 reset_cnt;
+};
+
+/**
+ * struct leapraid_scan_dev_desc - Scan device descriptor for LeapRAID
+ *
+ * @wait_scan_dev_done: Flag indicating if scan device operation is done.
+ * @driver_loading: Flag indicating if driver is loading.
+ * @first_scan_dev_fired: Flag indicating if first scan device operation fired.
+ * @scan_dev_failed: Flag indicating if scan device operation failed.
+ * @scan_start: Flag indicating if scan operation started.
+ * @scan_start_failed: Count of failed scan start operations.
+ */
+struct leapraid_scan_dev_desc {
+	u8 wait_scan_dev_done;
+	u8 driver_loading;
+	wait_queue_head_t wait_driver_loading;
+	u8 first_scan_dev_fired;
+	u8 scan_dev_failed;
+	u8 scan_start;
+	u16 scan_start_failed;
+};
+
+/**
+ * struct leapraid_access_ctrl - Access control structure for LeapRAID
+ *
+ * @pci_access_lock: Mutex for PCI access control.
+ * @shost_recovering: Flag indicating if host is recovering.
+ * @shost_recover_async: Flag indicating if host is recovering
+ *                       and the 0xFFFF event.
+ * @shost_recover_wq: Wait queue for threads waiting on shost_recover_async.
+ * @host_removing: Flag indicating if host is being removed.
+ * @pcie_recovering: Flag indicating if PCIe is recovering.
+ */
+struct leapraid_access_ctrl {
+	struct mutex pci_access_lock; /* serializes PCI register access. */
+	u8 shost_recovering;
+	wait_queue_head_t recovery_waitq;
+	u8 shost_recover_async;
+	wait_queue_head_t shost_recover_wq;
+	u8 host_removing;
+	u8 pcie_recovering;
+};
+
+/**
+ * struct leapraid_fw_log_desc - Firmware log descriptor for LeapRAID
+ *
+ * @fw_log_buffer: Buffer for firmware log data.
+ * @fw_log_buffer_dma: DMA address of the firmware log buffer.
+ * @fw_log_wq_name: Name of the firmware log workqueue.
+ * @fw_log_wq: Workqueue for firmware log operations.
+ * @fw_log_work: Delayed work structure for firmware log.
+ * @open_pcie_trace: Flag indicating if PCIe tracing is open.
+ * @fw_log_init_flag: Flag indicating if firmware log is initialized.
+ * @mmap_refcnt: Number of active user VMAs on fw_log_buffer.
+ * @mmap_waitq: Waitqueue for fw_log_buffer VMA teardown.
+ */
+struct leapraid_fw_log_desc {
+	u8 *fw_log_buffer;
+	dma_addr_t fw_log_buffer_dma;
+	char fw_log_wq_name[48];
+	struct workqueue_struct *fw_log_wq;
+	struct delayed_work fw_log_work;
+	int open_pcie_trace;
+	int fw_log_init_flag;
+	atomic_t mmap_refcnt;
+	wait_queue_head_t mmap_waitq;
+};
+
+#define LEAPRAID_CARD_PORT_FLG_DIRTY	0x01
+#define LEAPRAID_CARD_PORT_FLG_NEW	0x02
+#define LEAPRAID_DISABLE_MP_PORT_ID	0xFF
+/**
+ * struct leapraid_card_port - Card port structure for LeapRAID
+ *
+ * @list: List head for card port.
+ * @vphys_list: List head for virtual PHY list.
+ * @port_id: Port ID.
+ * @sas_address: SAS address.
+ * @phy_mask: Mask of PHY.
+ * @vphys_mask: Mask of virtual PHY.
+ * @flg: Flags for the port.
+ */
+struct leapraid_card_port {
+	struct list_head list;
+	struct list_head vphys_list;
+	u8 port_id;
+	u64 sas_address;
+	u32 phy_mask;
+	u32 vphys_mask;
+	u8 flg;
+};
+
+/**
+ * struct leapraid_card_phy - Card PHY structure for LeapRAID
+ *
+ * @port_siblings: List head for port siblings.
+ * @card_port: Pointer to the card port.
+ * @identify: SAS identify structure.
+ * @remote_identify: Remote SAS identify structure.
+ * @phy: SAS PHY structure.
+ * @phy_id: PHY ID.
+ * @hdl: Handle for the port.
+ * @attached_hdl: Handle for the attached port.
+ * @phy_is_assigned: Flag indicating if PHY is assigned.
+ * @vphy: Flag indicating if virtual PHY.
+ */
+struct leapraid_card_phy {
+	struct list_head port_siblings;
+	struct leapraid_card_port *card_port;
+	struct sas_identify identify;
+	struct sas_identify remote_identify;
+	struct sas_phy *phy;
+	u8 phy_id;
+	u16 hdl;
+	u16 attached_hdl;
+	u8 phy_is_assigned;
+	u8 vphy;
+};
+
+/**
+ * struct leapraid_topo_node - SAS topology node for LeapRAID
+ *
+ * @list: List head for linking nodes.
+ * @sas_port_list: List of SAS ports.
+ * @card_port: Associated card port.
+ * @card_phy: Associated card PHY.
+ * @rphy: SAS remote PHY device.
+ * @parent_dev: Parent device pointer.
+ * @sas_address: SAS address of this node.
+ * @sas_address_parent: Parent node's SAS address.
+ * @phys_num: Number of physical links.
+ * @hdl: Handle identifier.
+ * @enc_hdl: Enclosure handle.
+ * @enc_lid: Enclosure logical identifier.
+ * @resp: Response status flag.
+ */
+struct leapraid_topo_node {
+	struct list_head list;
+	struct list_head sas_port_list;
+	struct leapraid_card_port *card_port;
+	struct leapraid_card_phy *card_phy;
+	struct sas_rphy *rphy;
+	struct device *parent_dev;
+	u64 sas_address;
+	u64 sas_address_parent;
+	u8 phys_num;
+	u16 hdl;
+	u16 enc_hdl;
+	u64 enc_lid;
+	u8 resp;
+};
+
+/**
+ * struct leapraid_dev_topo - LeapRAID device topology management structure
+ *
+ * @topo_node_lock: Spinlock for protecting topology node operations.
+ * @sas_dev_lock: Spinlock for SAS device list access.
+ * @raid_volume_lock: Spinlock for RAID volume list access.
+ * @enc_lock: Spinlock for enclosure device list access.
+ * @sas_id: SAS domain identifier.
+ * @card: Main card topology node.
+ * @exp_list: List of expander devices.
+ * @enc_list: List of enclosure devices.
+ * @sas_dev_list: List of SAS devices.
+ * @sas_dev_init_list: List of SAS devices being initialized.
+ * @raid_volume_list: List of RAID volumes.
+ * @card_port_list: List of card ports.
+ * @pd_hdls: Array of physical disk handles.
+ * @blocking_hdls: Array of blocking handles.
+ */
+struct leapraid_dev_topo {
+	spinlock_t topo_node_lock; /* Protects topology node. */
+	spinlock_t sas_dev_lock; /* Protects SAS device list access. */
+	spinlock_t raid_volume_lock; /* Protects RAID volume list access. */
+	spinlock_t enc_lock; /* Protects enclosure device list access. */
+	int sas_id;
+	struct leapraid_topo_node card;
+	struct list_head exp_list;
+	struct list_head enc_list;
+	struct list_head sas_dev_list;
+	struct list_head sas_dev_init_list;
+	struct list_head raid_volume_list;
+	struct list_head card_port_list;
+	u16 pd_hdls_sz;
+	unsigned long *pd_hdls;
+	unsigned long *blocking_hdls;
+};
+
+/**
+ * struct leapraid_boot_dev - Boot device structure for LeapRAID
+ *
+ * @dev: Device pointer.
+ * @chnl: Channel number.
+ * @form: Form factor.
+ * @pg_dev: Config page device content.
+ */
+struct leapraid_boot_dev {
+	void *dev;
+	u8 chnl;
+	u8 form;
+	u8 pg_dev[24];
+};
+
+/**
+ * struct leapraid_boot_devs - Boot device management structure
+ *
+ * @lock: Spinlock protecting boot device cache access.
+ * @requested_boot_dev: Requested primary boot device.
+ * @requested_alt_boot_dev: Requested alternate boot device.
+ * @current_boot_dev: Currently active boot device.
+ */
+struct leapraid_boot_devs {
+	spinlock_t lock; /* protects boot dev */
+	struct leapraid_boot_dev requested_boot_dev;
+	struct leapraid_boot_dev requested_alt_boot_dev;
+	struct leapraid_boot_dev current_boot_dev;
+};
+
+/**
+ * struct leapraid_adapter - Main LeapRAID adapter structure
+ *
+ * @list: List head for adapter management.
+ * @shost: SCSI host structure.
+ * @pdev: PCI device structure.
+ * @iomem_base: I/O memory mapped base address.
+ * @rep_msg_host_idx: Host index for reply messages.
+ * @mask_int: Interrupt masking flag.
+ * @adapter_attr: Adapter attributes.
+ * @mem_desc: Memory descriptor.
+ * @driver_cmds: Driver commands.
+ * @dynamic_task_desc: Dynamic task descriptor.
+ * @fw_evt_s: Firmware event structure.
+ * @notification_desc: Notification descriptor.
+ * @reset_desc: Reset descriptor.
+ * @scan_dev_desc: Device scan descriptor.
+ * @access_ctrl: Access control.
+ * @fw_log_desc: Firmware log descriptor.
+ * @dev_topo: Device topology.
+ * @boot_devs: Boot devices.
+ * @overheat_desc: Overheat processing descriptor.
+ */
+struct leapraid_adapter {
+	struct list_head list;
+	struct Scsi_Host *shost;
+	struct pci_dev *pdev;
+	struct leapraid_reg_base __iomem *iomem_base;
+	u32 rep_msg_host_idx;
+	u8 mask_int;
+
+	struct leapraid_adapter_attr adapter_attr;
+	struct leapraid_mem_desc mem_desc;
+	struct leapraid_driver_cmds driver_cmds;
+	struct leapraid_dynamic_task_desc dynamic_task_desc;
+	struct leapraid_fw_evt_struct fw_evt_s;
+	struct leapraid_notification_desc notification_desc;
+	struct leapraid_reset_desc reset_desc;
+	struct leapraid_scan_dev_desc scan_dev_desc;
+	struct leapraid_access_ctrl access_ctrl;
+	struct leapraid_fw_log_desc fw_log_desc;
+	struct leapraid_dev_topo dev_topo;
+	struct leapraid_boot_devs boot_devs;
+	struct leapraid_overheat_desc overheat_desc;
+};
+
+union cfg_param_1 {
+	u32 form;
+	u32 size;
+	u32 phy_number;
+};
+
+union cfg_param_2 {
+	u32 handle;
+	u32 form_specific;
+};
+
+enum config_page_action {
+	GET_BIOS_PG2,
+	GET_BIOS_PG3,
+	GET_MANUFACTURING_PG0,
+	GET_SAS_DEVICE_PG0,
+	GET_SAS_IOUNIT_PG0,
+	GET_SAS_IOUNIT_PG1,
+	GET_SAS_EXPANDER_PG0,
+	GET_SAS_EXPANDER_PG1,
+	GET_SAS_ENCLOSURE_PG0,
+	GET_PHY_PG0,
+	GET_RAID_VOLUME_PG0,
+	GET_RAID_VOLUME_PG1,
+	GET_PHY_DISK_PG0,
+};
+
+/**
+ * struct leapraid_enc_node - Enclosure node structure
+ *
+ * @list: List head for enclosure management.
+ * @pg0: Enclosure page 0 data.
+ */
+struct leapraid_enc_node {
+	struct list_head list;
+	struct leapraid_enc_p0 pg0;
+};
+
+/**
+ * struct leapraid_raid_volume - RAID volume structure
+ *
+ * @list: List head for volume management.
+ * @refcnt: Reference count.
+ * @starget: SCSI target structure.
+ * @sdev: SCSI device structure.
+ * @id: Volume ID.
+ * @channel: SCSI channel.
+ * @wwid: World wide Identifier.
+ * @hdl: Volume handle.
+ * @vol_type: Volume type.
+ * @pd_num: Number of physical disks.
+ * @resp: Response status.
+ * @dev_info: Device information.
+ */
+struct leapraid_raid_volume {
+	struct list_head list;
+	struct kref refcnt;
+	struct scsi_target *starget;
+	struct scsi_device *sdev;
+	unsigned int id;
+	unsigned int channel;
+	u64 wwid;
+	u16 hdl;
+	u8 vol_type;
+	u8 pd_num;
+	u8 resp;
+	u32 dev_info;
+};
+
+static inline void leapraid_raid_volume_free(struct kref *ref)
+{
+	kfree(container_of(ref, struct leapraid_raid_volume, refcnt));
+}
+
+#define leapraid_raid_volume_get(volume) \
+	kref_get(&(volume)->refcnt)
+#define leapraid_raid_volume_put(volume) \
+	kref_put(&(volume)->refcnt, leapraid_raid_volume_free)
+
+#define LEAPRAID_TGT_FLG_RAID_MEMBER	0x01
+#define LEAPRAID_TGT_FLG_VOLUME		0x02
+#define LEAPRAID_NO_ULD_ATTACH		1
+/**
+ * struct leapraid_starget_priv - SCSI target private data
+ *
+ * @starget: SCSI target structure.
+ * @sas_address: SAS address.
+ * @hdl: Device handle.
+ * @num_luns: Number of LUNs.
+ * @flg: Flags.
+ * @deleted: Deletion flag.
+ * @tm_busy: Task management busy flag.
+ * @card_port: Associated card port.
+ * @sas_dev: SAS device structure.
+ */
+struct leapraid_starget_priv {
+	struct scsi_target *starget;
+	u64 sas_address;
+	u16 hdl;
+	int num_luns;
+	u32 flg;
+	u8 deleted;
+	u8 tm_busy;
+	struct leapraid_card_port *card_port;
+	struct leapraid_sas_dev *sas_dev;
+};
+
+#define LEAPRAID_DEVICE_FLG_INIT	0x01
+/**
+ * struct leapraid_sdev_priv - SCSI device private data
+ *
+ * @starget_priv: Associated target private data.
+ * @lun: Logical Unit Number.
+ * @flg: Flags.
+ * @ncq_cmd_prio_enable: Enables NCQ command priority for RT I/O.
+ * @block: Block flag.
+ * @deleted: Deletion flag.
+ * @sep: SEP flag.
+ */
+struct leapraid_sdev_priv {
+	struct leapraid_starget_priv *starget_priv;
+	unsigned int lun;
+	u32 flg;
+	u8 ncq_cmd_prio_enable;
+	u8 block;
+	u8 deleted;
+	u8 sep;
+};
+
+/**
+ * struct leapraid_sas_dev - SAS device structure
+ *
+ * @list: List head for device management.
+ * @starget: SCSI target structure.
+ * @card_port: Associated card port.
+ * @rphy: SAS remote PHY.
+ * @refcnt: Reference count.
+ * @id: Device ID.
+ * @channel: SCSI channel.
+ * @slot: Slot number.
+ * @phy: PHY identifier.
+ * @resp: Response status.
+ * @led_on: LED state.
+ * @sas_addr: SAS address.
+ * @dev_name: Device name.
+ * @hdl: Device handle.
+ * @parent_sas_addr: Parent SAS address.
+ * @enc_hdl: Enclosure handle.
+ * @enc_lid: Enclosure logical ID.
+ * @volume_hdl: Volume handle.
+ * @volume_wwid: Volume WWID.
+ * @dev_info: Device information.
+ * @pend_sas_rphy_add: Pending SAS remote PHY addition flag.
+ * @enc_level: Enclosure level.
+ * @port_connection: Port connection.
+ * @connector_name: Connector name.
+ */
+struct leapraid_sas_dev {
+	struct list_head list;
+	struct scsi_target *starget;
+	struct leapraid_card_port *card_port;
+	struct sas_rphy *rphy;
+	struct kref refcnt;
+	unsigned int id;
+	unsigned int channel;
+	u16 slot;
+	u8 phy;
+	u8 resp;
+	u8 led_on;
+	u64 sas_addr;
+	u64 dev_name;
+	u16 hdl;
+	u64 parent_sas_addr;
+	u16 enc_hdl;
+	u64 enc_lid;
+	u16 volume_hdl;
+	u64 volume_wwid;
+	u32 dev_info;
+	u8 pend_sas_rphy_add;
+	u8 enc_level;
+	u8 port_connection;
+	u8 connector_name[LEAPRAID_SAS_DEV_P0_CON_NAME_LEN + 1];
+};
+
+static inline void leapraid_sdev_free(struct kref *ref)
+{
+	kfree(container_of(ref, struct leapraid_sas_dev, refcnt));
+}
+
+#define leapraid_sdev_get(sdev)	kref_get(&(sdev)->refcnt)
+#define leapraid_sdev_put(sdev)	kref_put(&(sdev)->refcnt, leapraid_sdev_free)
+
+void leapraid_boot_dev_get(void *dev, u32 chnl);
+void leapraid_boot_dev_put(void *dev, u32 chnl);
+
+/**
+ * struct leapraid_sas_port - SAS port structure
+ *
+ * @port_list: List head for port management.
+ * @phy_list: List of PHYs in this port.
+ * @port: SAS port structure.
+ * @card_port: Associated card port.
+ * @remote_identify: Remote device identification.
+ * @rphy: SAS remote PHY.
+ * @phys_num: Number of PHYs in this port.
+ */
+struct leapraid_sas_port {
+	struct list_head port_list;
+	struct list_head phy_list;
+	struct sas_port *port;
+	struct leapraid_card_port *card_port;
+	struct sas_identify remote_identify;
+	struct sas_rphy *rphy;
+	u8 phys_num;
+};
+
+#define LEAPRAID_VPHY_FLG_DIRTY	0x01
+/**
+ * struct leapraid_vphy - Virtual PHY structure
+ *
+ * @list: List head for PHY management.
+ * @sas_address: SAS address.
+ * @phy_mask: PHY mask.
+ * @flg: Flags.
+ */
+struct leapraid_vphy {
+	struct list_head list;
+	u64 sas_address;
+	u32 phy_mask;
+	u8 flg;
+};
+
+/**
+ * struct leapraid_tgt_rst_list - Target reset tracking entry
+ * @list: List node used to link reset entries.
+ * @handle: Device handle of the target being reset.
+ * @state: Current state of the target reset operation.
+ *
+ * Each entry represents a target device that is undergoing or has
+ * undergone a target reset operation.
+ */
+struct leapraid_tgt_rst_list {
+	struct list_head list;
+	u16 handle;
+	u16 state;
+};
+
+/**
+ * struct sense_info - Parsed SCSI sense information
+ * @sense_key: Sense key extracted from sense data.
+ * @asc: Additional Sense Code (ASC).
+ * @ascq: Additional Sense Code Qualifier (ASCQ).
+ *
+ * This structure stores key fields parsed from SCSI sense data to
+ * simplify error handling and reporting.
+ */
+struct sense_info {
+	u8 sense_key;
+	u8 asc;
+	u8 ascq;
+};
+
+/**
+ * struct leapraid_fw_log_info - Firmware log position information
+ * @user_position: Current log read position used by the driver.
+ * @adapter_position: Current log write position maintained by firmware.
+ *
+ * This structure tracks firmware log buffer positions used for retrieving
+ * log entries from the adapter.
+ */
+struct leapraid_fw_log_info {
+	u32 user_position;
+	u32 adapter_position;
+};
+
+/**
+ * enum reset_type - Reset type enumeration
+ *
+ * @FULL_RESET: Full hardware reset.
+ * @PART_RESET: Partial reset.
+ */
+enum reset_type {
+	FULL_RESET,
+	PART_RESET,
+};
+
+enum leapraid_card_port_checking_flg {
+	CARD_PORT_FURTHER_CHECKING_NEEDED = 0,
+	CARD_PORT_SKIP_CHECKING,
+};
+
+enum leapraid_port_checking_state {
+	NEW_CARD_PORT = 0,
+	SAME_PORT_WITH_NOTHING_CHANGED,
+	SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS,
+	SAME_ADDR_WITH_PARTIALLY_CHANGED_PHYS,
+	SAME_ADDR_ONLY,
+};
+
+/**
+ * struct leapraid_card_port_feature - Card port feature
+ *
+ * @dirty_flg: Dirty flag indicator.
+ * @same_addr: Same address flag.
+ * @exact_phy: Exact PHY match flag.
+ * @phy_overlap: PHY overlap bitmap.
+ * @same_port: Same port flag.
+ * @cur_chking_old_port: Current checking old port.
+ * @expected_old_port: Expected old port.
+ * @same_addr_port_count: Same address port count.
+ * @checking_state: Port checking state.
+ */
+struct leapraid_card_port_feature {
+	u8 dirty_flg;
+	u8 same_addr;
+	u8 exact_phy;
+	u32 phy_overlap;
+	u8 same_port;
+	struct leapraid_card_port *cur_chking_old_port;
+	struct leapraid_card_port *expected_old_port;
+	int same_addr_port_count;
+	enum leapraid_port_checking_state checking_state;
+};
+
+#define SMP_REPORT_MANUFACTURER_INFORMATION_FRAME_TYPE	0x40
+#define SMP_REPORT_MANUFACTURER_INFORMATION_FUNC	0x01
+
+/**
+ * ref: SAS-2(INCITS 457-2010) 10.4.3.5
+ */
+struct leapraid_rep_manu_request {
+	u8 smp_frame_type;
+	u8 function;
+	u8 allocated_response_length;
+	u8 request_length;
+};
+
+/**
+ * ref: SAS-2(INCITS 457-2010) 10.4.3.5
+ */
+struct leapraid_rep_manu_reply {
+	u8 smp_frame_type;
+	u8 function;
+	u8 function_result;
+	u8 response_length;
+	u16 expander_change_count;
+	u8 r1[2];
+	u8 sas_format;
+	u8 r2[3];
+	u8 vendor_identification[SAS_EXPANDER_VENDOR_ID_LEN];
+	u8 product_identification[SAS_EXPANDER_PRODUCT_ID_LEN];
+	u8 product_revision_level[SAS_EXPANDER_PRODUCT_REV_LEN];
+	u8 comp_vendor_identification[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN];
+	u16 component_id;
+	u8 component_revision_level;
+	u8 r3;
+	u8 vendor_specific[8];
+};
+
+extern struct list_head leapraid_adapter_list;
+extern spinlock_t leapraid_adapter_lock;
+extern char driver_name[LEAPRAID_NAME_LENGTH];
+
+int leapraid_ctrl_init(struct leapraid_adapter *adapter);
+void leapraid_remove_ctrl(struct leapraid_adapter *adapter);
+void leapraid_check_scheduled_fault_start(struct leapraid_adapter *adapter);
+void leapraid_check_scheduled_fault_stop(struct leapraid_adapter *adapter);
+void leapraid_fw_log_start(struct leapraid_adapter *adapter);
+void leapraid_fw_log_stop(struct leapraid_adapter *adapter);
+int leapraid_set_pcie_and_notification(struct leapraid_adapter *adapter);
+void leapraid_disable_controller(struct leapraid_adapter *adapter);
+int leapraid_hard_reset_handler(struct leapraid_adapter *adapter,
+				enum reset_type type);
+void leapraid_mask_int(struct leapraid_adapter *adapter);
+void leapraid_unmask_int(struct leapraid_adapter *adapter);
+u32 leapraid_get_adapter_state(struct leapraid_adapter *adapter);
+bool leapraid_pci_removed(struct leapraid_adapter *adapter);
+int leapraid_check_adapter_is_op(struct leapraid_adapter *adapter, int wait,
+				 const char *caller);
+void *leapraid_get_task_desc(struct leapraid_adapter *adapter, u16 taskid);
+void *leapraid_get_sense_buffer(struct leapraid_adapter *adapter, u16 taskid);
+__le32 leapraid_get_sense_buffer_dma(struct leapraid_adapter *adapter,
+				     u16 taskid);
+void *leapraid_get_reply_vaddr(struct leapraid_adapter *adapter,
+			       u32 phys_addr);
+u16 leapraid_alloc_scsiio_taskid(struct leapraid_adapter *adapter,
+				 struct scsi_cmnd *scmd);
+void leapraid_free_taskid(struct leapraid_adapter *adapter, u16 taskid);
+struct leapraid_io_req_tracker *leapraid_get_io_tracker_from_taskid(
+		struct leapraid_adapter *adapter, u16 taskid);
+struct scsi_cmnd *leapraid_get_scmd_from_taskid(
+		struct leapraid_adapter *adapter, u16 taskid);
+int leapraid_scan_dev(struct leapraid_adapter *adapter, bool async_scan_dev);
+void leapraid_scan_dev_done(struct leapraid_adapter *adapter);
+void leapraid_cleanup_lists(struct leapraid_adapter *adapter);
+void leapraid_wait_cmds_done(struct leapraid_adapter *adapter);
+void leapraid_clean_active_scsi_cmds(struct leapraid_adapter *adapter);
+void leapraid_sync_irqs(struct leapraid_adapter *adapter, bool poll);
+int leapraid_rep_queue_handler(struct leapraid_rq *rq);
+int leapraid_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num);
+void leapraid_mq_polling_pause(struct leapraid_adapter *adapter);
+void leapraid_mq_polling_resume(struct leapraid_adapter *adapter);
+void leapraid_set_tm_flg(struct leapraid_adapter *adapter, u16 handle);
+void leapraid_clear_tm_flg(struct leapraid_adapter *adapter, u16 handle);
+void leapraid_async_turn_on_led(struct leapraid_adapter *adapter, u16 handle);
+int leapraid_issue_locked_tm(struct leapraid_adapter *adapter, u16 handle,
+			     uint channel, uint id, uint lun, u8 type,
+			     u16 taskid_task, u8 tr_method);
+int leapraid_issue_tm(struct leapraid_adapter *adapter, u16 handle,
+		      uint channel, uint id, uint lun, u8 type,
+		      u16 taskid_task, u8 tr_method);
+bool leapraid_scsiio_done(struct leapraid_adapter *adapter, u16 taskid,
+			  u8 msix_index, u32 rep);
+int leapraid_get_volume_cap(struct leapraid_adapter *adapter,
+			    struct leapraid_raid_volume *raid_volume);
+int leapraid_internal_init_cmd_priv(
+		struct leapraid_adapter *adapter,
+		struct leapraid_io_req_tracker *io_tracker);
+void leapraid_internal_exit_cmd_priv(
+		struct leapraid_adapter *adapter,
+		struct leapraid_io_req_tracker *io_tracker);
+void leapraid_clean_active_fw_evt(struct leapraid_adapter *adapter);
+bool leapraid_scmd_find_by_lun(struct leapraid_adapter *adapter,
+			       uint id, unsigned int lun, uint channel);
+bool leapraid_scmd_find_by_tgt(struct leapraid_adapter *adapter,
+			       uint id, uint channel);
+struct leapraid_vphy *leapraid_get_vphy_by_phy(struct leapraid_card_port *port,
+					       u32 phy);
+struct leapraid_raid_volume *leapraid_raid_volume_find_by_id(
+		struct leapraid_adapter *adapter, uint id, uint channel);
+struct leapraid_raid_volume *leapraid_raid_volume_find_by_hdl(
+		struct leapraid_adapter *adapter, u16 handle);
+struct leapraid_topo_node *leapraid_exp_find_by_sas_address(
+		struct leapraid_adapter *adapter, u64 sas_address,
+		struct leapraid_card_port *port);
+struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_addr_and_rphy(
+		struct leapraid_adapter *adapter,
+		u64 sas_address, struct sas_rphy *rphy);
+struct leapraid_sas_dev *leapraid_get_sas_dev_by_addr(
+		struct leapraid_adapter *adapter, u64 sas_address,
+		struct leapraid_card_port *port);
+struct leapraid_sas_dev *leapraid_get_sas_dev_by_hdl(
+		struct leapraid_adapter *adapter, u16 handle);
+struct leapraid_sas_dev *leapraid_get_sas_dev_from_tgt(
+		struct leapraid_adapter *adapter,
+		struct leapraid_starget_priv *tgt_priv);
+struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_from_tgt(
+		struct leapraid_adapter *adapter,
+		 struct leapraid_starget_priv *tgt_priv);
+struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_hdl(
+		struct leapraid_adapter *adapter, u16 handle);
+struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_addr(
+		struct leapraid_adapter *adapter, u64 sas_address,
+		struct leapraid_card_port *port);
+struct leapraid_sas_dev *leapraid_get_next_sas_dev_from_init_list(
+		struct leapraid_adapter *adapter);
+void leapraid_sas_dev_remove_by_sas_address(
+		struct leapraid_adapter *adapter,
+		u64 sas_address, struct leapraid_card_port *port);
+void leapraid_sas_dev_remove(struct leapraid_adapter *adapter,
+			     struct leapraid_sas_dev *sas_dev);
+void leapraid_raid_volume_remove(struct leapraid_adapter *adapter,
+				 struct leapraid_raid_volume *raid_volume);
+void leapraid_exp_rm(struct leapraid_adapter *adapter,
+		     u64 sas_address, struct leapraid_card_port *port);
+void leapraid_build_mpi_sg(struct leapraid_adapter *adapter,
+			   void *sge, dma_addr_t h2c_dma_addr, size_t h2c_size,
+			   dma_addr_t c2h_dma_addr, size_t c2h_size);
+void leapraid_build_ieee_nodata_sg(struct leapraid_adapter *adapter,
+				   void *sge);
+void leapraid_build_ieee_sg(struct leapraid_adapter *adapter,
+			    void *psge, dma_addr_t h2c_dma_addr,
+			    size_t h2c_size, dma_addr_t c2h_dma_addr,
+			    size_t c2h_size);
+int leapraid_build_scmd_ieee_sg(struct leapraid_adapter *adapter,
+				struct scsi_cmnd *scmd, u16 taskid);
+void leapraid_fire_scsi_io(struct leapraid_adapter *adapter,
+			   u16 taskid, u16 handle);
+void leapraid_fire_hpr_task(struct leapraid_adapter *adapter, u16 taskid,
+			    u16 msix_task);
+void leapraid_fire_task(struct leapraid_adapter *adapter, u16 taskid);
+int leapraid_cfg_get_volume_hdl(struct leapraid_adapter *adapter,
+				u16 pd_handle, u16 *volume_handle);
+int leapraid_cfg_get_volume_wwid(struct leapraid_adapter *adapter,
+				 u16 volume_handle, u64 *wwid);
+int leapraid_op_config_page(struct leapraid_adapter *adapter,
+			    void *cfgp, union cfg_param_1 cfgp1,
+			    union cfg_param_2 cfgp2,
+			    enum config_page_action cfg_op);
+void leapraid_log_req_context(struct leapraid_adapter *adapter, u16 smid,
+			      const void *req_data);
+
+int leapraid_change_queue_depth(struct scsi_device *sdev, int qdepth);
+
+int leapraid_ctl_release(struct inode *inode, struct file *filep);
+int leapraid_ctl_init(void);
+void leapraid_ctl_exit(void);
+
+extern struct sas_function_template leapraid_transport_functions;
+extern struct scsi_transport_template *leapraid_transport_template;
+struct leapraid_sas_port *leapraid_transport_port_add(
+		struct leapraid_adapter *adapter, u16 handle, u64 sas_address,
+		struct leapraid_card_port *card_port);
+void leapraid_transport_port_remove(struct leapraid_adapter *adapter,
+				    u64 sas_address, u64 sas_address_parent,
+				    struct leapraid_card_port *card_port);
+void leapraid_transport_add_card_phy(struct leapraid_adapter *adapter,
+				     struct leapraid_card_phy *card_phy,
+				     struct leapraid_sas_phy_p0 *phy_pg0,
+				     struct device *parent_dev);
+int leapraid_transport_add_exp_phy(struct leapraid_adapter *adapter,
+				   struct leapraid_card_phy *card_phy,
+				   struct leapraid_exp_p1 *exp_pg1,
+				   struct device *parent_dev);
+void leapraid_transport_update_links(struct leapraid_adapter *adapter,
+				     u64 sas_address, u16 handle,
+				     u8 phy_number, u8 link_rate,
+				     struct leapraid_card_port *card_port);
+void leapraid_transport_detach_phy_to_port(
+		struct leapraid_adapter *adapter,
+		struct leapraid_topo_node *topo_node,
+		struct leapraid_card_phy *card_phy);
+void leapraid_transport_attach_phy_to_port(
+		struct leapraid_adapter *adapter,
+		struct leapraid_topo_node *sas_node,
+		struct leapraid_card_phy *card_phy,
+		u64 sas_address,
+		struct leapraid_card_port *card_port);
+enum scsi_qc_status leapraid_queuecommand(struct Scsi_Host *shost,
+					  struct scsi_cmnd *scmd);
+void leapraid_overheat_cleanup(struct leapraid_adapter *adapter);
+void leapraid_smart_fault_detect(struct leapraid_adapter *adapter, u16 hdl);
+
+#endif /* LEAPRAID_FUNC_H_INCLUDED */
diff --git a/drivers/scsi/leapraid/leapraid_os.c b/drivers/scsi/leapraid/leapraid_os.c
new file mode 100644
index 000000000000..2f3d4ac7490c
--- /dev/null
+++ b/drivers/scsi/leapraid/leapraid_os.c
@@ -0,0 +1,2468 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 LeapIO Tech Inc.
+ *
+ * LeapRAID storage and RAID controller driver.
+ */
+#include <linux/module.h>
+
+#include "leapraid_func.h"
+#include "leapraid.h"
+
+LIST_HEAD(leapraid_adapter_list);
+DEFINE_SPINLOCK(leapraid_adapter_lock);
+
+MODULE_AUTHOR(LEAPRAID_AUTHOR);
+MODULE_DESCRIPTION(LEAPRAID_DESCRIPTION);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(LEAPRAID_DRIVER_VERSION);
+
+static atomic_t leapraid_ids = ATOMIC_INIT(0);
+
+static int open_pcie_trace = 1;
+module_param(open_pcie_trace, int, 0644);
+MODULE_PARM_DESC(open_pcie_trace, "Default=1(open)/0(close)");
+
+static int enable_mp = 1;
+module_param(enable_mp, int, 0444);
+MODULE_PARM_DESC(enable_mp,
+		 "Enable multipath on target device. default=1(enable)");
+
+static inline void leapraid_get_sense_data(char *sense,
+					   struct sense_info *data)
+{
+	bool desc_format = (sense[0] & SCSI_SENSE_RESPONSE_CODE_MASK) >=
+			    DESC_FORMAT_THRESHOLD;
+
+	if (desc_format) {
+		data->sense_key = sense[1] & SENSE_KEY_MASK;
+		data->asc = sense[2];
+		data->ascq = sense[3];
+	} else {
+		data->sense_key = sense[2] & SENSE_KEY_MASK;
+		data->asc = sense[12];
+		data->ascq = sense[13];
+	}
+}
+
+static struct leapraid_adapter *pdev_to_adapter(struct pci_dev *pdev)
+{
+	struct Scsi_Host *shost = pci_get_drvdata(pdev);
+
+	if (!shost)
+		return NULL;
+
+	return shost_priv(shost);
+}
+
+void leapraid_set_tm_flg(struct leapraid_adapter *adapter, u16 hdl)
+{
+	struct leapraid_sdev_priv *sdev_priv;
+	struct scsi_device *sdev;
+	bool skip = false;
+
+	/* Iterate over all devices. */
+	shost_for_each_device(sdev, adapter->shost) {
+		if (skip)
+			continue;
+
+		sdev_priv = sdev->hostdata;
+		if (!sdev_priv)
+			continue;
+
+		if (sdev_priv->starget_priv->hdl == hdl) {
+			sdev_priv->starget_priv->tm_busy = 1;
+			skip = true;
+		}
+	}
+}
+
+void leapraid_clear_tm_flg(struct leapraid_adapter *adapter, u16 hdl)
+{
+	struct leapraid_sdev_priv *sdev_priv;
+	struct scsi_device *sdev;
+	bool skip = false;
+
+	/* Iterate over all devices. */
+	shost_for_each_device(sdev, adapter->shost) {
+		if (skip)
+			continue;
+
+		sdev_priv = sdev->hostdata;
+		if (!sdev_priv)
+			continue;
+
+		if (sdev_priv->starget_priv->hdl == hdl) {
+			sdev_priv->starget_priv->tm_busy = 0;
+			skip = true;
+		}
+	}
+}
+
+static int leapraid_tm_cmd_map_status(struct leapraid_adapter *adapter,
+				      uint channel,
+				      uint id,
+				      uint lun,
+				      u8 type,
+				      u16 taskid_task)
+{
+	int rc = FAILED;
+
+	if (taskid_task <= adapter->shost->can_queue) {
+		switch (type) {
+		case LEAPRAID_TM_TASKTYPE_ABRT_TASK_SET:
+		case LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET:
+			if (!leapraid_scmd_find_by_lun(adapter, id, lun,
+						       channel))
+				rc = SUCCESS;
+			break;
+		case LEAPRAID_TM_TASKTYPE_TARGET_RESET:
+			if (!leapraid_scmd_find_by_tgt(adapter, id, channel))
+				rc = SUCCESS;
+			break;
+		default:
+			rc = SUCCESS;
+		}
+	}
+
+	if (taskid_task == adapter->driver_cmds.ctl_cmd.hp_taskid &&
+	    (adapter->driver_cmds.ctl_cmd.status &
+	     LEAPRAID_CMD_DONE ||
+	     adapter->driver_cmds.ctl_cmd.status &
+	     LEAPRAID_CMD_NOT_USED))
+		rc = SUCCESS;
+
+	return rc;
+}
+
+static int leapraid_tm_post_processing(struct leapraid_adapter *adapter,
+				       u16 hdl, uint channel, uint id,
+				       uint lun, u8 type, u16 taskid_task)
+{
+	int rc;
+
+	rc = leapraid_tm_cmd_map_status(adapter, channel, id, lun,
+					type, taskid_task);
+	if (rc == SUCCESS)
+		return rc;
+
+	leapraid_mask_int(adapter);
+	leapraid_sync_irqs(adapter, true);
+	leapraid_unmask_int(adapter);
+
+	return leapraid_tm_cmd_map_status(adapter, channel, id, lun, type,
+					  taskid_task);
+}
+
+static void leapraid_build_tm_req(struct leapraid_scsi_tm_req *scsi_tm_req,
+				  u16 hdl, uint lun, u8 type, u8 tr_method,
+				  u16 target_taskid)
+{
+	memset(scsi_tm_req, 0, sizeof(*scsi_tm_req));
+	scsi_tm_req->func = LEAPRAID_FUNC_SCSI_TMF;
+	scsi_tm_req->dev_hdl = cpu_to_le16(hdl);
+	scsi_tm_req->task_type = type;
+	scsi_tm_req->msg_flg = tr_method;
+	if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK ||
+	    type == LEAPRAID_TM_TASKTYPE_QUERY_TASK)
+		scsi_tm_req->task_mid = cpu_to_le16(target_taskid);
+	int_to_scsilun(lun, (struct scsi_lun *)scsi_tm_req->lun);
+}
+
+int leapraid_issue_tm(struct leapraid_adapter *adapter, u16 hdl, uint channel,
+		      uint id, uint lun, u8 type,
+		      u16 target_taskid, u8 tr_method)
+{
+	struct leapraid_scsi_tm_req *scsi_tm_req;
+	struct leapraid_scsiio_req *scsiio_req;
+	struct leapraid_io_req_tracker *io_req_tracker = NULL;
+	u16 msix_task;
+	u16 taskid;
+	bool issue_reset = false;
+	u32 db;
+	int rc;
+
+	lockdep_assert_held(&adapter->driver_cmds.tm_cmd.mutex);
+
+	if (adapter->access_ctrl.shost_recovering ||
+	    adapter->access_ctrl.host_removing ||
+	    adapter->access_ctrl.pcie_recovering) {
+		dev_info(&adapter->pdev->dev,
+			 "%s %s: Host is recovering, skip tm command!\n",
+			 __func__, adapter->adapter_attr.name);
+		return FAILED;
+	}
+
+	db = leapraid_readl(&adapter->iomem_base->db);
+	if (db & LEAPRAID_DB_USED) {
+		dev_info(&adapter->pdev->dev,
+			 "%s Unexpected db status, issuing hard reset!\n",
+			 adapter->adapter_attr.name);
+		dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n",
+			 __func__, __LINE__);
+		rc = leapraid_hard_reset_handler(adapter, FULL_RESET);
+		return !rc ? SUCCESS : FAILED;
+	}
+
+	if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_FAULT) {
+		dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n",
+			 __func__, __LINE__);
+		rc = leapraid_hard_reset_handler(adapter, FULL_RESET);
+		return !rc ? SUCCESS : FAILED;
+	}
+
+	if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK)
+		io_req_tracker =
+			leapraid_get_io_tracker_from_taskid(adapter,
+							    target_taskid);
+
+	adapter->driver_cmds.tm_cmd.status = LEAPRAID_CMD_PENDING;
+	scsi_tm_req =
+		leapraid_get_task_desc(adapter,
+				       adapter->driver_cmds.tm_cmd.hp_taskid);
+	leapraid_build_tm_req(scsi_tm_req, hdl, lun, type, tr_method,
+			      target_taskid);
+	memset(&adapter->driver_cmds.tm_cmd.reply, 0,
+	       sizeof(struct leapraid_scsi_tm_rep));
+	leapraid_set_tm_flg(adapter, hdl);
+	init_completion(&adapter->driver_cmds.tm_cmd.done);
+	if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK &&
+	    io_req_tracker &&
+	    io_req_tracker->msix_io < adapter->adapter_attr.rq_cnt)
+		msix_task = io_req_tracker->msix_io;
+	else
+		msix_task = 0;
+	taskid = adapter->driver_cmds.tm_cmd.hp_taskid;
+	leapraid_fire_hpr_task(adapter, taskid, msix_task);
+	wait_for_completion_timeout(&adapter->driver_cmds.tm_cmd.done,
+				    LEAPRAID_TM_CMD_TIMEOUT * HZ);
+	if (!(adapter->driver_cmds.tm_cmd.status & LEAPRAID_CMD_DONE)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: TM cmd timeout, status=0x%x\n",
+			__func__, adapter->driver_cmds.tm_cmd.status);
+		leapraid_log_req_context(adapter, taskid, scsi_tm_req);
+		issue_reset =
+			leapraid_check_reset(
+				adapter->driver_cmds.tm_cmd.status);
+		if (issue_reset) {
+			dev_info(&adapter->pdev->dev,
+				 "%s:%d: call hard_reset\n",
+				 __func__, __LINE__);
+			rc = leapraid_hard_reset_handler(adapter, FULL_RESET);
+			rc = !rc ? SUCCESS : FAILED;
+			goto out_cleanup;
+		}
+	}
+
+	leapraid_sync_irqs(adapter, false);
+
+	switch (type) {
+	case LEAPRAID_TM_TASKTYPE_TARGET_RESET:
+	case LEAPRAID_TM_TASKTYPE_ABRT_TASK_SET:
+	case LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET:
+		rc = leapraid_tm_post_processing(adapter, hdl, channel, id,
+						 lun, type, target_taskid);
+		break;
+	case LEAPRAID_TM_TASKTYPE_ABORT_TASK:
+		rc = SUCCESS;
+		scsiio_req = leapraid_get_task_desc(adapter, target_taskid);
+		if (le16_to_cpu(scsiio_req->dev_hdl) != hdl)
+			break;
+		dev_err(&adapter->pdev->dev, "%s: Abort failed, hdl=0x%04x\n",
+			adapter->adapter_attr.name, hdl);
+		rc = FAILED;
+		break;
+	case LEAPRAID_TM_TASKTYPE_QUERY_TASK:
+		rc = SUCCESS;
+		break;
+	default:
+		rc = FAILED;
+		break;
+	}
+
+out_cleanup:
+	leapraid_clear_tm_flg(adapter, hdl);
+	adapter->driver_cmds.tm_cmd.status = LEAPRAID_CMD_NOT_USED;
+	return rc;
+}
+
+int leapraid_issue_locked_tm(struct leapraid_adapter *adapter, u16 hdl,
+			     uint channel, uint id, uint lun, u8 type,
+			     u16 target_taskid, u8 tr_method)
+{
+	int rc;
+
+	mutex_lock(&adapter->driver_cmds.tm_cmd.mutex);
+	rc = leapraid_issue_tm(adapter, hdl, channel, id, lun, type,
+			       target_taskid, tr_method);
+	mutex_unlock(&adapter->driver_cmds.tm_cmd.mutex);
+
+	return rc;
+}
+
+void leapraid_smart_fault_detect(struct leapraid_adapter *adapter, u16 hdl)
+{
+	struct leapraid_starget_priv *starget_priv;
+	struct leapraid_sas_dev *sas_dev;
+	struct scsi_target *starget;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl);
+	if (!sas_dev) {
+		spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+		return;
+	}
+
+	starget = sas_dev->starget;
+	starget_priv = starget ? starget->hostdata : NULL;
+	if (!starget_priv) {
+		spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+		goto release_sdev;
+	}
+
+	if (starget_priv->flg & LEAPRAID_TGT_FLG_RAID_MEMBER ||
+	    starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME) {
+		spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+		goto release_sdev;
+	}
+
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+	leapraid_async_turn_on_led(adapter, hdl);
+release_sdev:
+	if (sas_dev)
+		leapraid_sdev_put(sas_dev);
+}
+
+static void leapraid_process_sense_data(struct leapraid_adapter *adapter,
+					struct leapraid_scsiio_rep *scsiio_rep,
+					struct scsi_cmnd *scmd, u16 taskid)
+{
+	struct sense_info data;
+	const void *sense_data;
+	u32 sz;
+
+	if (!(scsiio_rep->scsi_state & LEAPRAID_SCSI_STATE_AUTOSENSE_VALID))
+		return;
+
+	sense_data = leapraid_get_sense_buffer(adapter, taskid);
+	sz = min_t(u32, SCSI_SENSE_BUFFERSIZE,
+		   le32_to_cpu(scsiio_rep->sense_count));
+
+	memcpy(scmd->sense_buffer, sense_data, sz);
+	leapraid_get_sense_data(scmd->sense_buffer, &data);
+	if (data.asc == ASC_FAILURE_PREDICTION_THRESHOLD_EXCEEDED)
+		leapraid_smart_fault_detect(adapter,
+					    le16_to_cpu(scsiio_rep->dev_hdl));
+}
+
+static void leapraid_handle_data_underrun(
+		struct leapraid_scsiio_rep *scsiio_rep,
+		struct scsi_cmnd *scmd, u32 xfer_cnt)
+{
+	u8 scsi_status = scsiio_rep->scsi_status;
+	u8 scsi_state = scsiio_rep->scsi_state;
+
+	scmd->result = (DID_OK << LEAPRAID_SCSI_HOST_SHIFT) | scsi_status;
+
+	if (scsi_state & LEAPRAID_SCSI_STATE_AUTOSENSE_VALID)
+		return;
+
+	if (xfer_cnt < scmd->underflow) {
+		if (scsi_status == SAM_STAT_BUSY)
+			scmd->result = SAM_STAT_BUSY;
+		else
+			scmd->result = DID_SOFT_ERROR <<
+				LEAPRAID_SCSI_HOST_SHIFT;
+	} else if (scsi_state & (LEAPRAID_SCSI_STATE_AUTOSENSE_FAILED |
+				 LEAPRAID_SCSI_STATE_NO_SCSI_STATUS)) {
+		scmd->result = DID_SOFT_ERROR << LEAPRAID_SCSI_HOST_SHIFT;
+	} else if (scsi_state & LEAPRAID_SCSI_STATE_TERMINATED) {
+		scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
+	} else if (!xfer_cnt && scmd->cmnd[0] == REPORT_LUNS) {
+		scsiio_rep->scsi_state = LEAPRAID_SCSI_STATE_AUTOSENSE_VALID;
+		scsiio_rep->scsi_status = SAM_STAT_CHECK_CONDITION;
+		scsi_build_sense(scmd, 0, ILLEGAL_REQUEST,
+				 LEAPRAID_SCSI_ASC_INVALID_CMD_CODE,
+				 LEAPRAID_SCSI_ASCQ_DEFAULT);
+	}
+}
+
+static void leapraid_handle_success_status(
+		struct leapraid_scsiio_rep *scsiio_rep,
+		struct scsi_cmnd *scmd,
+		u32 response_code)
+{
+	u8 scsi_status = scsiio_rep->scsi_status;
+	u8 scsi_state = scsiio_rep->scsi_state;
+
+	scmd->result = (DID_OK << LEAPRAID_SCSI_HOST_SHIFT) | scsi_status;
+
+	if (response_code == LEAPRAID_TM_RSP_INVALID_FRAME ||
+	    (scsi_state & (LEAPRAID_SCSI_STATE_AUTOSENSE_FAILED |
+			   LEAPRAID_SCSI_STATE_NO_SCSI_STATUS)))
+		scmd->result = DID_SOFT_ERROR << LEAPRAID_SCSI_HOST_SHIFT;
+	else if (scsi_state & LEAPRAID_SCSI_STATE_TERMINATED)
+		scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
+}
+
+static void leapraid_scsiio_done_dispatch(
+		struct leapraid_adapter *adapter,
+		struct leapraid_scsiio_rep *scsiio_rep,
+		struct leapraid_sdev_priv *sdev_priv,
+		struct scsi_cmnd *scmd,
+		u16 taskid, u32 response_code)
+{
+	u8 scsi_status = scsiio_rep->scsi_status;
+	u8 scsi_state = scsiio_rep->scsi_state;
+	u16 adapter_status;
+	u32 xfer_cnt;
+	u32 sz;
+
+	adapter_status = le16_to_cpu(scsiio_rep->adapter_status) &
+				     LEAPRAID_ADAPTER_STATUS_MASK;
+
+	xfer_cnt = le32_to_cpu(scsiio_rep->transfer_count);
+	scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt);
+
+	if (adapter_status == LEAPRAID_ADAPTER_STATUS_SCSI_DATA_UNDERRUN &&
+	    xfer_cnt == 0 &&
+	    (scsi_status == LEAPRAID_SCSI_STATUS_BUSY ||
+	    scsi_status == LEAPRAID_SCSI_STATUS_RESERVATION_CONFLICT ||
+	    scsi_status == LEAPRAID_SCSI_STATUS_TASK_SET_FULL))
+		adapter_status = LEAPRAID_ADAPTER_STATUS_SUCCESS;
+
+	switch (adapter_status) {
+	case LEAPRAID_ADAPTER_STATUS_SCSI_DEVICE_NOT_THERE:
+		scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT;
+		break;
+
+	case LEAPRAID_ADAPTER_STATUS_BUSY:
+	case LEAPRAID_ADAPTER_STATUS_INSUFFICIENT_RESOURCES:
+		scmd->result = SAM_STAT_BUSY;
+		break;
+
+	case LEAPRAID_ADAPTER_STATUS_SCSI_RESIDUAL_MISMATCH:
+		if (xfer_cnt == 0 || scmd->underflow > xfer_cnt)
+			scmd->result = DID_SOFT_ERROR <<
+				LEAPRAID_SCSI_HOST_SHIFT;
+		else
+			scmd->result = (DID_OK << LEAPRAID_SCSI_HOST_SHIFT) |
+				scsi_status;
+		break;
+
+	case LEAPRAID_ADAPTER_STATUS_SCSI_ADAPTER_TERMINATED:
+		if (sdev_priv->block) {
+			scmd->result = DID_TRANSPORT_DISRUPTED <<
+				LEAPRAID_SCSI_HOST_SHIFT;
+			return;
+		}
+
+		if (scmd->device->channel == RAID_CHANNEL &&
+		    scsi_state == (LEAPRAID_SCSI_STATE_TERMINATED |
+				    LEAPRAID_SCSI_STATE_NO_SCSI_STATUS)) {
+			scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
+			break;
+		}
+
+		scmd->result = DID_SOFT_ERROR << LEAPRAID_SCSI_HOST_SHIFT;
+		break;
+
+	case LEAPRAID_ADAPTER_STATUS_SCSI_TASK_TERMINATED:
+	case LEAPRAID_ADAPTER_STATUS_SCSI_EXT_TERMINATED:
+		scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
+		break;
+
+	case LEAPRAID_ADAPTER_STATUS_SCSI_DATA_UNDERRUN:
+		leapraid_handle_data_underrun(scsiio_rep, scmd, xfer_cnt);
+		break;
+
+	case LEAPRAID_ADAPTER_STATUS_SCSI_DATA_OVERRUN:
+		scsi_set_resid(scmd, 0);
+		leapraid_handle_success_status(scsiio_rep, scmd,
+					       response_code);
+		break;
+	case LEAPRAID_ADAPTER_STATUS_SCSI_RECOVERED_ERROR:
+	case LEAPRAID_ADAPTER_STATUS_SUCCESS:
+		leapraid_handle_success_status(scsiio_rep, scmd,
+					       response_code);
+		break;
+
+	case LEAPRAID_ADAPTER_STATUS_SCSI_PROTOCOL_ERROR:
+	case LEAPRAID_ADAPTER_STATUS_INTERNAL_ERROR:
+	case LEAPRAID_ADAPTER_STATUS_SCSI_IO_DATA_ERROR:
+	case LEAPRAID_ADAPTER_STATUS_SCSI_TASK_MGMT_FAILED:
+	default:
+		scmd->result = DID_SOFT_ERROR << LEAPRAID_SCSI_HOST_SHIFT;
+		break;
+	}
+
+	if (!scmd->result)
+		return;
+
+	scsi_print_command(scmd);
+	dev_warn(&adapter->pdev->dev,
+		 "SCSI I/O: hdl=0x%x, status: 0x%x, 0x%x, 0x%x\n",
+		 le16_to_cpu(scsiio_rep->dev_hdl), adapter_status,
+		 scsi_status, scsi_state);
+
+	if (scsi_state & LEAPRAID_SCSI_STATE_AUTOSENSE_VALID) {
+		struct scsi_sense_hdr sshdr;
+
+		sz = min_t(u32, SCSI_SENSE_BUFFERSIZE,
+			   le32_to_cpu(scsiio_rep->sense_count));
+		if (scsi_normalize_sense(scmd->sense_buffer, sz,
+					 &sshdr))
+			dev_warn(&adapter->pdev->dev,
+				 "Sense: key=0x%x asc=0x%x ascq=0x%x\n",
+				 sshdr.sense_key, sshdr.asc,
+				 sshdr.ascq);
+		else
+			dev_warn(&adapter->pdev->dev,
+				 "Sense: Invalid sense data\n");
+	}
+}
+
+bool leapraid_scsiio_done(struct leapraid_adapter *adapter, u16 taskid,
+			  u8 msix_index, u32 rep)
+{
+	struct leapraid_scsiio_rep *scsiio_rep;
+	struct leapraid_sdev_priv *sdev_priv;
+	struct scsi_cmnd *scmd;
+	u32 response_code = 0;
+
+	scmd = leapraid_get_scmd_from_taskid(adapter, taskid);
+	if (!scmd)
+		return true;
+
+	scsiio_rep = leapraid_get_reply_vaddr(adapter, rep);
+	if (!scsiio_rep) {
+		scmd->result = DID_OK << LEAPRAID_SCSI_HOST_SHIFT;
+		goto out_scsiio_done;
+	}
+
+	sdev_priv = scmd->device->hostdata;
+	if (!sdev_priv ||
+	    !sdev_priv->starget_priv ||
+	    sdev_priv->starget_priv->deleted) {
+		scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT;
+		goto out_scsiio_done;
+	}
+
+	if (scsiio_rep->scsi_state & LEAPRAID_SCSI_STATE_RESPONSE_INFO_VALID)
+		response_code = le32_to_cpu(scsiio_rep->resp_info) & 0xFF;
+
+	leapraid_process_sense_data(adapter, scsiio_rep, scmd, taskid);
+	leapraid_scsiio_done_dispatch(adapter, scsiio_rep, sdev_priv, scmd,
+				      taskid, response_code);
+
+out_scsiio_done:
+	scsi_dma_unmap(scmd);
+	leapraid_free_taskid(adapter, taskid);
+	scsi_done(scmd);
+	return false;
+}
+
+static void leapraid_probe_raid(struct leapraid_adapter *adapter)
+{
+	struct leapraid_raid_volume *raid_volume, *next_raid_volume;
+	unsigned long flags;
+	LIST_HEAD(head);
+	int rc;
+
+	spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+	list_splice_init(&adapter->dev_topo.raid_volume_list, &head);
+	spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags);
+
+	list_for_each_entry_safe(raid_volume, next_raid_volume, &head, list) {
+		spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+		list_move_tail(&raid_volume->list,
+			       &adapter->dev_topo.raid_volume_list);
+		if (raid_volume->starget) {
+			spin_unlock_irqrestore(
+				&adapter->dev_topo.raid_volume_lock, flags);
+			continue;
+		}
+
+		leapraid_raid_volume_get(raid_volume);
+		spin_unlock_irqrestore(
+			&adapter->dev_topo.raid_volume_lock, flags);
+
+		rc = scsi_add_device(adapter->shost, RAID_CHANNEL,
+				     raid_volume->id, 0);
+		if (rc)
+			leapraid_raid_volume_remove(adapter, raid_volume);
+
+		leapraid_raid_volume_put(raid_volume);
+	}
+}
+
+static void leapraid_sas_dev_make_active(struct leapraid_adapter *adapter,
+					 struct leapraid_sas_dev *sas_dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	if (!list_empty(&sas_dev->list)) {
+		list_del_init(&sas_dev->list);
+		leapraid_sdev_put(sas_dev);
+	}
+
+	leapraid_sdev_get(sas_dev);
+	list_add_tail(&sas_dev->list, &adapter->dev_topo.sas_dev_list);
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+}
+
+static void leapraid_probe_sas(struct leapraid_adapter *adapter)
+{
+	struct leapraid_sas_dev *sas_dev;
+	bool added;
+
+	for (;;) {
+		sas_dev = leapraid_get_next_sas_dev_from_init_list(adapter);
+		if (!sas_dev)
+			break;
+		added = leapraid_transport_port_add(adapter,
+						    sas_dev->hdl,
+						    sas_dev->parent_sas_addr,
+						    sas_dev->card_port);
+		if (!added)
+			goto remove_dev;
+
+		if (!sas_dev->starget &&
+		    !adapter->scan_dev_desc.driver_loading) {
+			leapraid_transport_port_remove(adapter,
+						       sas_dev->sas_addr,
+						       sas_dev->parent_sas_addr,
+						       sas_dev->card_port);
+			goto remove_dev;
+		}
+
+		leapraid_sas_dev_make_active(adapter, sas_dev);
+		leapraid_sdev_put(sas_dev);
+		continue;
+
+remove_dev:
+		leapraid_sas_dev_remove(adapter, sas_dev);
+		leapraid_sdev_put(sas_dev);
+	}
+}
+
+static bool leapraid_get_boot_dev(struct leapraid_adapter *adapter,
+				  struct leapraid_boot_dev *boot_dev,
+				  void **pdev, u32 *pchnl)
+{
+	unsigned long flags;
+	void *dev;
+	u32 chnl;
+
+	spin_lock_irqsave(&adapter->boot_devs.lock, flags);
+	if (!boot_dev->dev) {
+		spin_unlock_irqrestore(&adapter->boot_devs.lock, flags);
+		return false;
+	}
+
+	dev = boot_dev->dev;
+	chnl = boot_dev->chnl;
+	leapraid_boot_dev_get(dev, chnl);
+	spin_unlock_irqrestore(&adapter->boot_devs.lock, flags);
+
+	*pdev = dev;
+	*pchnl = chnl;
+	return true;
+}
+
+static void leapraid_probe_boot_dev(struct leapraid_adapter *adapter)
+{
+	struct leapraid_raid_volume *raid_volume;
+	struct leapraid_sas_dev *sas_dev;
+	struct leapraid_sas_port *sport;
+	unsigned long flags;
+	void *dev = NULL;
+	u32 chnl;
+
+	if (leapraid_get_boot_dev(adapter,
+				  &adapter->boot_devs.requested_boot_dev,
+				  &dev, &chnl))
+		goto boot_dev_found;
+
+	if (leapraid_get_boot_dev(adapter,
+				  &adapter->boot_devs.requested_alt_boot_dev,
+				  &dev, &chnl))
+		goto boot_dev_found;
+
+	if (leapraid_get_boot_dev(adapter,
+				  &adapter->boot_devs.current_boot_dev,
+				  &dev, &chnl))
+		goto boot_dev_found;
+
+	return;
+
+boot_dev_found:
+	switch (chnl) {
+	case RAID_CHANNEL:
+		raid_volume = dev;
+
+		if (raid_volume->starget)
+			break;
+
+		/* TODO eedp */
+
+		if (scsi_add_device(adapter->shost, RAID_CHANNEL,
+				    raid_volume->id, 0))
+			leapraid_raid_volume_remove(adapter, raid_volume);
+		break;
+	default:
+		sas_dev = dev;
+
+		if (sas_dev->starget)
+			break;
+
+		spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+		list_move_tail(&sas_dev->list,
+			       &adapter->dev_topo.sas_dev_list);
+		spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+
+		if (!sas_dev->card_port)
+			break;
+
+		sport = leapraid_transport_port_add(adapter, sas_dev->hdl,
+						    sas_dev->parent_sas_addr,
+						    sas_dev->card_port);
+		if (!sport)
+			leapraid_sas_dev_remove(adapter, sas_dev);
+		break;
+	}
+
+	leapraid_boot_dev_put(dev, chnl);
+}
+
+static void leapraid_probe_devices(struct leapraid_adapter *adapter)
+{
+	leapraid_probe_boot_dev(adapter);
+
+	if (adapter->adapter_attr.raid_support) {
+		leapraid_probe_raid(adapter);
+		leapraid_probe_sas(adapter);
+	} else {
+		leapraid_probe_sas(adapter);
+	}
+}
+
+void leapraid_scan_dev_done(struct leapraid_adapter *adapter)
+{
+	if (adapter->scan_dev_desc.wait_scan_dev_done) {
+		adapter->scan_dev_desc.wait_scan_dev_done = 0;
+		leapraid_probe_devices(adapter);
+	}
+
+	adapter->scan_dev_desc.scan_start = 0;
+
+	leapraid_check_scheduled_fault_start(adapter);
+	leapraid_fw_log_start(adapter);
+	adapter->scan_dev_desc.driver_loading = 0;
+	wake_up(&adapter->scan_dev_desc.wait_driver_loading);
+}
+
+static const struct pci_device_id leapraid_pci_table[] = {
+	{ PCI_DEVICE_SUB(LEAPRAID_VENDOR_ID, LEAPRAID_DEVID_HBA,
+			 LEAPRAID_SUBVENDOR_ID,
+			 LEAPRAID_SUBDEVID_HBA) },
+	{ 0, }
+};
+
+static inline bool leapraid_is_scmd_permitted(struct leapraid_adapter *adapter,
+					      struct scsi_cmnd *scmd)
+{
+	u8 opcode;
+
+	if (adapter->access_ctrl.pcie_recovering ||
+	    atomic_read(&adapter->overheat_desc.thermal_alert))
+		return false;
+
+	if (adapter->access_ctrl.host_removing) {
+		if (leapraid_pci_removed(adapter))
+			return false;
+
+		opcode = scmd->cmnd[0];
+		return opcode == SYNCHRONIZE_CACHE || opcode == START_STOP;
+	}
+	return true;
+}
+
+static bool leapraid_should_queuecommand(struct leapraid_adapter *adapter,
+					 struct leapraid_sdev_priv *sdev_priv,
+					 struct scsi_cmnd *scmd,
+					 enum scsi_qc_status *rc)
+{
+	struct leapraid_starget_priv *starget_priv;
+
+	if (!sdev_priv || !sdev_priv->starget_priv)
+		goto no_connect;
+
+	if (!leapraid_is_scmd_permitted(adapter, scmd))
+		goto no_connect;
+
+	starget_priv = sdev_priv->starget_priv;
+	if (starget_priv->hdl == LEAPRAID_INVALID_DEV_HANDLE)
+		goto no_connect;
+
+	if (sdev_priv->block &&
+	    scmd->device->host->shost_state == SHOST_RECOVERY &&
+	    scmd->cmnd[0] == TEST_UNIT_READY) {
+		scsi_build_sense(scmd, 0, UNIT_ATTENTION,
+				 LEAPRAID_SCSI_ASC_POWER_ON_RESET,
+				 LEAPRAID_SCSI_ASCQ_POWER_ON_RESET);
+		goto scsiio_done;
+	}
+
+	if (adapter->access_ctrl.shost_recovering ||
+	    adapter->reset_desc.adapter_link_resetting) {
+		*rc = SCSI_MLQUEUE_HOST_BUSY;
+		return false;
+	}
+
+	if (starget_priv->deleted || sdev_priv->deleted)
+		goto no_connect;
+
+	if (starget_priv->tm_busy || sdev_priv->block) {
+		*rc = SCSI_MLQUEUE_DEVICE_BUSY;
+		return false;
+	}
+
+	return true;
+
+no_connect:
+	scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT;
+scsiio_done:
+	scsi_done(scmd);
+
+	return false;
+}
+
+static u32 build_scsiio_req_control(struct scsi_cmnd *scmd,
+				    struct leapraid_sdev_priv *sdev_priv)
+{
+	u32 control;
+
+	switch (scmd->sc_data_direction) {
+	case DMA_FROM_DEVICE:
+		control = LEAPRAID_SCSIIO_CTRL_READ;
+		break;
+	case DMA_TO_DEVICE:
+		control = LEAPRAID_SCSIIO_CTRL_WRITE;
+		break;
+	default:
+		control = LEAPRAID_SCSIIO_CTRL_NODATATRANSFER;
+		break;
+	}
+
+	control |= LEAPRAID_SCSIIO_CTRL_SIMPLEQ;
+
+	if (sdev_priv->ncq_cmd_prio_enable &&
+	    (IOPRIO_PRIO_CLASS(req_get_ioprio(scsi_cmd_to_rq(scmd))) ==
+	     IOPRIO_CLASS_RT))
+		control |= LEAPRAID_SCSIIO_CTRL_CMDPRI;
+	if (scmd->cmd_len == 32)
+		control |= LEAPRAID_SCSIIO_CTRL_CDB_32BYTE <<
+				LEAPRAID_SCSIIO_CTRL_CDB_LEN_SHIFT;
+
+	return control;
+}
+
+enum scsi_qc_status leapraid_queuecommand(struct Scsi_Host *shost,
+					  struct scsi_cmnd *scmd)
+{
+	struct leapraid_adapter *adapter = shost_priv(scmd->device->host);
+	struct leapraid_sdev_priv *sdev_priv = scmd->device->hostdata;
+	struct leapraid_starget_priv *starget_priv;
+	struct leapraid_scsiio_req *scsiio_req;
+	u32 control;
+	u16 taskid;
+	u16 hdl;
+	enum scsi_qc_status rc = 0;
+
+	if (!leapraid_should_queuecommand(adapter, sdev_priv, scmd, &rc))
+		return rc;
+
+	starget_priv = sdev_priv->starget_priv;
+	hdl = starget_priv->hdl;
+	control = build_scsiio_req_control(scmd, sdev_priv);
+
+	taskid = leapraid_alloc_scsiio_taskid(adapter, scmd);
+	scsiio_req = leapraid_get_task_desc(adapter, taskid);
+
+	scsiio_req->func = LEAPRAID_FUNC_SCSIIO;
+	if (sdev_priv->starget_priv->flg & LEAPRAID_TGT_FLG_RAID_MEMBER)
+		scsiio_req->func = LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH;
+	else
+		scsiio_req->func = LEAPRAID_FUNC_SCSIIO;
+
+	scsiio_req->dev_hdl = cpu_to_le16(hdl);
+	scsiio_req->data_len = cpu_to_le32(scsi_bufflen(scmd));
+	scsiio_req->ctrl = cpu_to_le32(control);
+	scsiio_req->io_flg = cpu_to_le16(scmd->cmd_len);
+	scsiio_req->msg_flg = 0;
+	scsiio_req->sense_buffer_len = SCSI_SENSE_BUFFERSIZE;
+	scsiio_req->sense_buffer_low_add =
+		leapraid_get_sense_buffer_dma(adapter, taskid);
+	scsiio_req->sgl_offset0 =
+		offsetof(struct leapraid_scsiio_req, sgl) /
+		LEAPRAID_DWORDS_BYTE_SIZE;
+	int_to_scsilun(sdev_priv->lun, (struct scsi_lun *)scsiio_req->lun);
+	memcpy(scsiio_req->cdb.cdb32, scmd->cmnd, scmd->cmd_len);
+	if (scsiio_req->data_len) {
+		if (leapraid_build_scmd_ieee_sg(adapter, scmd, taskid)) {
+			leapraid_free_taskid(adapter, taskid);
+			return SCSI_MLQUEUE_HOST_BUSY;
+		}
+	} else {
+		leapraid_build_ieee_nodata_sg(adapter, &scsiio_req->sgl);
+	}
+
+	if (likely(scsiio_req->func == LEAPRAID_FUNC_SCSIIO))
+		leapraid_fire_scsi_io(adapter, taskid,
+				      le16_to_cpu(scsiio_req->dev_hdl));
+	else
+		leapraid_fire_task(adapter, taskid);
+
+	dev_dbg(&adapter->pdev->dev,
+		"LEAPRAID_SCSIIO: Send Descriptor taskid %d, req type 0x%x\n",
+		taskid, scsiio_req->func);
+	return rc;
+}
+
+static int leapraid_init_cmd_priv(struct Scsi_Host *shost,
+				  struct scsi_cmnd *scmd)
+{
+	struct leapraid_adapter *adapter = shost_priv(shost);
+	struct leapraid_io_req_tracker *io_tracker;
+
+	io_tracker = scsi_cmd_priv(scmd);
+	leapraid_internal_init_cmd_priv(adapter, io_tracker);
+
+	return 0;
+}
+
+static int leapraid_exit_cmd_priv(struct Scsi_Host *shost,
+				  struct scsi_cmnd *scmd)
+{
+	struct leapraid_adapter *adapter = shost_priv(shost);
+	struct leapraid_io_req_tracker *io_tracker;
+
+	io_tracker = scsi_cmd_priv(scmd);
+	leapraid_internal_exit_cmd_priv(adapter, io_tracker);
+
+	return 0;
+}
+
+static int leapraid_error_handler(struct scsi_cmnd *scmd,
+				  const char *str, u8 type)
+{
+	struct leapraid_adapter *adapter = shost_priv(scmd->device->host);
+	struct scsi_target *starget = scmd->device->sdev_target;
+	struct leapraid_starget_priv *starget_priv = starget->hostdata;
+	struct leapraid_io_req_tracker *io_req_tracker = NULL;
+	struct leapraid_sdev_priv *sdev_priv;
+	struct leapraid_sas_dev *sas_dev = NULL;
+	u16 hdl;
+	int rc;
+
+	dev_info(&adapter->pdev->dev,
+		 "EH enter: type=%s, scmd=0x%p, req tag=%d\n", str, scmd,
+		 scsi_cmd_to_rq(scmd)->tag);
+	scsi_print_command(scmd);
+
+	if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK) {
+		io_req_tracker = scsi_cmd_priv(scmd);
+		dev_info(&adapter->pdev->dev,
+			 "EH ABORT: scmd=0x%p, pend=%ums, tout=%ums, tag=%d\n",
+			 scmd,
+			 jiffies_to_msecs(jiffies - scmd->jiffies_at_alloc),
+			 (scsi_cmd_to_rq(scmd)->timeout / HZ) * 1000,
+			 scsi_cmd_to_rq(scmd)->tag);
+	}
+
+	if (leapraid_pci_removed(adapter) ||
+	    adapter->access_ctrl.host_removing) {
+		dev_err(&adapter->pdev->dev,
+			"EH %s failed: %s scmd=0x%p\n", str,
+			(adapter->access_ctrl.host_removing ?
+			"shost removing!" : "pci_dev removed!"), scmd);
+		if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK &&
+		    io_req_tracker && io_req_tracker->taskid)
+			leapraid_free_taskid(adapter, io_req_tracker->taskid);
+		scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT;
+#ifdef FAST_IO_FAIL
+		rc = FAST_IO_FAIL;
+#else
+		rc = FAILED;
+#endif
+		goto out_eh_done;
+	}
+
+	sdev_priv = scmd->device->hostdata;
+	if (!sdev_priv || !sdev_priv->starget_priv) {
+		dev_warn(&adapter->pdev->dev,
+			 "EH %s: SAS dev or starget gone, scmd=0x%p\n",
+			 str, scmd);
+		scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT;
+		scsi_done(scmd);
+		rc = SUCCESS;
+		goto out_eh_done;
+	}
+
+	if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK) {
+		if (!io_req_tracker) {
+			dev_warn(&adapter->pdev->dev,
+				 "EH ABORT: No I/O tracker, scmd 0x%p\n", scmd);
+			scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
+			rc = SUCCESS;
+			goto out_eh_done;
+		}
+
+		if (sdev_priv->starget_priv->flg &
+			LEAPRAID_TGT_FLG_RAID_MEMBER ||
+		    sdev_priv->starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME) {
+			dev_err(&adapter->pdev->dev,
+				"EH ABORT: Skip RAID/VOLUME, scmd=0x%p\n",
+				scmd);
+			scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
+			rc = FAILED;
+			goto out_eh_done;
+		}
+
+		hdl = sdev_priv->starget_priv->hdl;
+	} else {
+		hdl = 0;
+		if (sdev_priv->starget_priv->flg &
+		    LEAPRAID_TGT_FLG_RAID_MEMBER) {
+			sas_dev = leapraid_get_sas_dev_from_tgt(adapter,
+								starget_priv);
+			if (sas_dev)
+				hdl = sas_dev->volume_hdl;
+		} else {
+			hdl = sdev_priv->starget_priv->hdl;
+		}
+
+		if (!hdl) {
+			dev_err(&adapter->pdev->dev,
+				"EH %s failed: Target handle 0, scmd=0x%p\n",
+				str, scmd);
+			scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT;
+			rc = FAILED;
+			goto out_eh_done;
+		}
+	}
+
+	dev_info(&adapter->pdev->dev,
+		 "EH issue TM: type=%s, scmd=0x%p, hdl=0x%x\n",
+		 str, scmd, hdl);
+
+	rc = leapraid_issue_locked_tm(
+			adapter,
+			hdl,
+			scmd->device->channel,
+			scmd->device->id,
+			(type == LEAPRAID_TM_TASKTYPE_TARGET_RESET ?
+				0 : scmd->device->lun),
+			type,
+			(type == LEAPRAID_TM_TASKTYPE_ABORT_TASK ?
+				io_req_tracker->taskid : 0),
+			LEAPRAID_TM_MSGFLAGS_LINK_RESET);
+
+out_eh_done:
+	if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK) {
+		if (rc != SUCCESS)
+			dev_err(&adapter->pdev->dev,
+				"EH ABORT result: failed, scmd=0x%p\n",
+				scmd);
+	} else {
+		if (rc != SUCCESS)
+			dev_err(&adapter->pdev->dev,
+				"EH %s result: failed, scmd=0x%p\n",
+				str, scmd);
+		if (sas_dev)
+			leapraid_sdev_put(sas_dev);
+	}
+	return rc;
+}
+
+static int leapraid_eh_abort_handler(struct scsi_cmnd *scmd)
+{
+	return leapraid_error_handler(scmd, "ABORT TASK",
+				      LEAPRAID_TM_TASKTYPE_ABORT_TASK);
+}
+
+static int leapraid_eh_device_reset_handler(struct scsi_cmnd *scmd)
+{
+	return leapraid_error_handler(scmd, "UNIT RESET",
+				      LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET);
+}
+
+static int leapraid_eh_target_reset_handler(struct scsi_cmnd *scmd)
+{
+	return leapraid_error_handler(scmd, "TARGET RESET",
+				      LEAPRAID_TM_TASKTYPE_TARGET_RESET);
+}
+
+static int leapraid_eh_host_reset_handler(struct scsi_cmnd *scmd)
+{
+	struct leapraid_adapter *adapter = shost_priv(scmd->device->host);
+	int rc;
+
+	dev_info(&adapter->pdev->dev,
+		 "EH HOST RESET enter: scmd=%p, req tag=%d\n",
+		 scmd,
+		 scsi_cmd_to_rq(scmd)->tag);
+	scsi_print_command(scmd);
+
+	if (adapter->scan_dev_desc.driver_loading ||
+	    adapter->access_ctrl.host_removing) {
+		dev_err(&adapter->pdev->dev,
+			"EH HOST RESET failed: %s scmd=0x%p\n",
+			(adapter->access_ctrl.host_removing ?
+			"shost removing!" : "driver loading!"), scmd);
+		rc = FAILED;
+		goto out_host_reset_done;
+	}
+
+	dev_info(&adapter->pdev->dev, "%s:%d: Issuing hard reset\n",
+		 __func__, __LINE__);
+	if (leapraid_hard_reset_handler(adapter, FULL_RESET) < 0)
+		rc = FAILED;
+	else
+		rc = SUCCESS;
+
+out_host_reset_done:
+	if (rc != SUCCESS)
+		dev_err(&adapter->pdev->dev,
+			"EH HOST RESET result: failed, scmd=0x%p\n",
+			scmd);
+	return rc;
+}
+
+static int leapraid_sdev_init(struct scsi_device *sdev)
+{
+	struct leapraid_raid_volume *raid_volume;
+	struct leapraid_starget_priv *stgt_priv;
+	struct leapraid_sdev_priv *sdev_priv;
+	struct leapraid_adapter *adapter;
+	struct leapraid_sas_dev *sas_dev;
+	struct scsi_target *tgt;
+	struct Scsi_Host *shost;
+	unsigned long flags;
+
+	sdev_priv = kzalloc_obj(*sdev_priv);
+	if (!sdev_priv)
+		return -ENOMEM;
+
+	sdev_priv->lun = sdev->lun;
+	sdev_priv->flg = LEAPRAID_DEVICE_FLG_INIT;
+	tgt = scsi_target(sdev);
+	stgt_priv = tgt->hostdata;
+	stgt_priv->num_luns++;
+	sdev_priv->starget_priv = stgt_priv;
+	sdev->hostdata = sdev_priv;
+	if (stgt_priv->flg & LEAPRAID_TGT_FLG_RAID_MEMBER)
+		sdev->no_uld_attach = LEAPRAID_NO_ULD_ATTACH;
+
+	shost = dev_to_shost(&tgt->dev);
+	adapter = shost_priv(shost);
+	if (tgt->channel == RAID_CHANNEL) {
+		raid_volume = leapraid_raid_volume_find_by_id(adapter,
+							      tgt->id,
+							      tgt->channel);
+		if (raid_volume) {
+			spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock,
+					  flags);
+			raid_volume->sdev = sdev;
+			spin_unlock_irqrestore(
+				&adapter->dev_topo.raid_volume_lock, flags);
+			leapraid_raid_volume_put(raid_volume);
+		}
+	}
+
+	if (!(stgt_priv->flg & LEAPRAID_TGT_FLG_VOLUME)) {
+		spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+		sas_dev = leapraid_hold_lock_get_sas_dev_by_addr(
+				adapter,
+				stgt_priv->sas_address,
+				stgt_priv->card_port);
+		if (sas_dev && !sas_dev->starget) {
+			sdev_printk(KERN_INFO, sdev,
+				    "%s: Assign starget to sas_dev\n",
+				    __func__);
+			sas_dev->starget = tgt;
+		}
+
+		if (sas_dev)
+			leapraid_sdev_put(sas_dev);
+		spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+	}
+	return 0;
+}
+
+static bool leapraid_slave_cfg_volume(struct scsi_device *sdev,
+				      struct queue_limits *lim)
+{
+	struct Scsi_Host *shost = sdev->host;
+	struct leapraid_adapter *adapter = shost_priv(shost);
+	struct leapraid_raid_volume *raid_volume;
+	struct leapraid_starget_priv *starget_priv;
+	struct leapraid_sdev_priv *sdev_priv;
+	int qd;
+	u16 hdl;
+
+	sdev_priv = sdev->hostdata;
+	starget_priv = sdev_priv->starget_priv;
+	hdl = starget_priv->hdl;
+
+	raid_volume = leapraid_raid_volume_find_by_hdl(adapter, hdl);
+	if (!raid_volume) {
+		sdev_printk(KERN_WARNING, sdev,
+			    "%s: RAID volume not found, hdl=0x%x\n",
+			    __func__, hdl);
+		return 1;
+	}
+
+	if (leapraid_get_volume_cap(adapter, raid_volume)) {
+		sdev_printk(KERN_ERR, sdev,
+			    "%s: Failed to get volume cap, hdl=0x%x\n",
+			    __func__, hdl);
+		leapraid_raid_volume_put(raid_volume);
+		return 1;
+	}
+
+	qd = (raid_volume->dev_info & LEAPRAID_DEVTYP_SSP_TGT) ?
+		adapter->adapter_attr.narrowport_max_queue_depth :
+		adapter->adapter_attr.sata_max_queue_depth;
+	if (raid_volume->vol_type != LEAPRAID_VOL_TYPE_RAID0)
+		qd = adapter->adapter_attr.raid_volume_max_queue_depth;
+
+	sdev_printk(KERN_INFO, sdev,
+		    "RAID volume: hdl=0x%04x, wwid=0x%016llx\n",
+		    raid_volume->hdl, (unsigned long long)raid_volume->wwid);
+
+	if (shost->max_sectors > LEAPRAID_MAX_SECTORS)
+		lim->max_hw_sectors = LEAPRAID_MAX_SECTORS;
+
+	leapraid_change_queue_depth(sdev, qd);
+	leapraid_raid_volume_put(raid_volume);
+	return 0;
+}
+
+static bool leapraid_slave_configure_extra(struct scsi_device *sdev,
+					   struct leapraid_sas_dev **psas_dev,
+					   u16 vol_hdl, u64 volume_wwid,
+					   bool *is_target_ssp, int *qd)
+{
+	struct leapraid_sas_dev *sas_dev;
+	struct leapraid_sdev_priv *sdev_priv;
+	struct Scsi_Host *shost = sdev->host;
+	struct leapraid_adapter *adapter = shost_priv(shost);
+	unsigned long flags;
+
+	sdev_priv = sdev->hostdata;
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	*is_target_ssp = false;
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_addr(
+			adapter,
+			sdev_priv->starget_priv->sas_address,
+			sdev_priv->starget_priv->card_port);
+	if (!sas_dev) {
+		spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+		sdev_printk(KERN_WARNING, sdev,
+			    "%s: SAS dev not found, sas=0x%llx\n",
+			    __func__, sdev_priv->starget_priv->sas_address);
+		return 1;
+	}
+
+	*psas_dev = sas_dev;
+	sas_dev->volume_hdl = vol_hdl;
+	sas_dev->volume_wwid = volume_wwid;
+	if (sas_dev->dev_info & LEAPRAID_DEVTYP_SSP_TGT) {
+		*qd = (sas_dev->port_connection > 1) ?
+			adapter->adapter_attr.wideport_max_queue_depth :
+			adapter->adapter_attr.narrowport_max_queue_depth;
+		*is_target_ssp = true;
+		if (sas_dev->dev_info & LEAPRAID_DEVTYP_SEP)
+			sdev_priv->sep = 1;
+	} else {
+		*qd = adapter->adapter_attr.sata_max_queue_depth;
+	}
+
+	sdev_printk(KERN_INFO, sdev,
+		    "device name=0x%016llx, SAS addr=0x%016llx\n",
+		    (unsigned long long)sas_dev->dev_name,
+		    (unsigned long long)sas_dev->sas_addr);
+	leapraid_sdev_put(sas_dev);
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+	return 0;
+}
+
+static int leapraid_sdev_configure(struct scsi_device *sdev,
+				   struct queue_limits *lim)
+{
+	struct leapraid_sas_dev *sas_dev;
+	struct leapraid_sdev_priv *sdev_priv;
+	struct Scsi_Host *shost = sdev->host;
+	struct leapraid_starget_priv *starget_priv;
+	struct leapraid_adapter *adapter;
+	u16 hdl, vol_hdl = 0;
+	bool is_target_ssp = false;
+	u64 volume_wwid = 0;
+	int qd = 1;
+
+	adapter = shost_priv(shost);
+	sdev_priv = sdev->hostdata;
+	sdev_priv->flg &= ~LEAPRAID_DEVICE_FLG_INIT;
+	starget_priv = sdev_priv->starget_priv;
+	hdl = starget_priv->hdl;
+	if (starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME)
+		return leapraid_slave_cfg_volume(sdev, lim);
+
+	if (starget_priv->flg & LEAPRAID_TGT_FLG_RAID_MEMBER) {
+		if (leapraid_cfg_get_volume_hdl(adapter, hdl, &vol_hdl)) {
+			sdev_printk(KERN_WARNING, sdev,
+				    "%s: Get volume hdl failed, hdl=0x%x\n",
+				    __func__, hdl);
+			return 1;
+		}
+
+		if (vol_hdl && leapraid_cfg_get_volume_wwid(adapter, vol_hdl,
+							    &volume_wwid)) {
+			sdev_printk(KERN_WARNING, sdev,
+				    "%s: Get wwid failed, volume_hdl=0x%x\n",
+				    __func__, vol_hdl);
+			return 1;
+		}
+	}
+
+	if (leapraid_slave_configure_extra(sdev, &sas_dev, vol_hdl,
+					   volume_wwid, &is_target_ssp, &qd)) {
+		sdev_printk(KERN_WARNING, sdev,
+			    "%s: slave_configure_extra failed\n", __func__);
+		return 1;
+	}
+
+	leapraid_change_queue_depth(sdev, qd);
+	if (is_target_ssp)
+		sas_read_port_mode_page(sdev);
+
+	return 0;
+}
+
+static void leapraid_sdev_destroy(struct scsi_device *sdev)
+{
+	struct leapraid_adapter *adapter;
+	struct Scsi_Host *shost;
+	struct leapraid_sas_dev *sas_dev;
+	struct leapraid_starget_priv *starget_priv;
+	struct scsi_target *stgt;
+	unsigned long flags;
+
+	if (!sdev->hostdata)
+		return;
+
+	stgt = scsi_target(sdev);
+	starget_priv = stgt->hostdata;
+	starget_priv->num_luns--;
+	shost = dev_to_shost(&stgt->dev);
+	adapter = shost_priv(shost);
+	if (!(starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME)) {
+		spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+		sas_dev = leapraid_hold_lock_get_sas_dev_from_tgt(adapter,
+								  starget_priv);
+		if (sas_dev && !starget_priv->num_luns)
+			sas_dev->starget = NULL;
+		if (sas_dev)
+			leapraid_sdev_put(sas_dev);
+		spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+	}
+
+	kfree(sdev->hostdata);
+	sdev->hostdata = NULL;
+}
+
+static int leapraid_target_alloc_raid(struct scsi_target *tgt)
+{
+	struct leapraid_starget_priv *starget_priv;
+	struct leapraid_raid_volume *raid_volume;
+	struct Scsi_Host *shost = dev_to_shost(&tgt->dev);
+	struct leapraid_adapter *adapter = shost_priv(shost);
+	unsigned long flags;
+
+	starget_priv = tgt->hostdata;
+	raid_volume = leapraid_raid_volume_find_by_id(adapter, tgt->id,
+						      tgt->channel);
+	if (raid_volume) {
+		spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+		starget_priv->hdl = raid_volume->hdl;
+		starget_priv->sas_address = raid_volume->wwid;
+		starget_priv->flg |= LEAPRAID_TGT_FLG_VOLUME;
+		raid_volume->starget = tgt;
+		spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock,
+				       flags);
+		leapraid_raid_volume_put(raid_volume);
+	}
+	return 0;
+}
+
+static int leapraid_target_alloc_sas(struct scsi_target *tgt)
+{
+	struct sas_rphy *rphy;
+	struct Scsi_Host *shost;
+	struct leapraid_sas_dev *sas_dev;
+	struct leapraid_adapter *adapter;
+	struct leapraid_starget_priv *starget_priv;
+	unsigned long flags;
+
+	shost = dev_to_shost(&tgt->dev);
+	adapter = shost_priv(shost);
+	starget_priv = tgt->hostdata;
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	rphy = dev_to_rphy(tgt->dev.parent);
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_addr_and_rphy(
+			adapter,
+			rphy->identify.sas_address,
+			rphy);
+	if (sas_dev) {
+		starget_priv->sas_dev = sas_dev;
+		starget_priv->card_port = sas_dev->card_port;
+		starget_priv->sas_address = sas_dev->sas_addr;
+		starget_priv->hdl = sas_dev->hdl;
+		sas_dev->channel = tgt->channel;
+		sas_dev->id = tgt->id;
+		sas_dev->starget = tgt;
+		if (sas_dev->hdl &&
+		    sas_dev->hdl <=
+		    adapter->adapter_attr.features.max_dev_handle &&
+		    test_bit(sas_dev->hdl, adapter->dev_topo.pd_hdls))
+			starget_priv->flg |= LEAPRAID_TGT_FLG_RAID_MEMBER;
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+
+	return 0;
+}
+
+static int leapraid_target_alloc(struct scsi_target *tgt)
+{
+	struct leapraid_starget_priv *starget_priv;
+
+	starget_priv = kzalloc_obj(*starget_priv);
+	if (!starget_priv)
+		return -ENOMEM;
+
+	tgt->hostdata = starget_priv;
+	starget_priv->starget = tgt;
+	starget_priv->hdl = LEAPRAID_INVALID_DEV_HANDLE;
+	if (tgt->channel == RAID_CHANNEL)
+		return leapraid_target_alloc_raid(tgt);
+
+	return leapraid_target_alloc_sas(tgt);
+}
+
+static void leapraid_target_destroy_raid(struct scsi_target *tgt)
+{
+	struct leapraid_raid_volume *raid_volume;
+	struct Scsi_Host *shost = dev_to_shost(&tgt->dev);
+	struct leapraid_adapter *adapter = shost_priv(shost);
+	unsigned long flags;
+
+	raid_volume = leapraid_raid_volume_find_by_id(adapter, tgt->id,
+						      tgt->channel);
+	if (raid_volume) {
+		spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags);
+		raid_volume->starget = NULL;
+		raid_volume->sdev = NULL;
+		spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock,
+				       flags);
+		leapraid_raid_volume_put(raid_volume);
+	}
+}
+
+static void leapraid_target_destroy_sas(struct scsi_target *tgt)
+{
+	struct leapraid_adapter *adapter;
+	struct leapraid_sas_dev *sas_dev;
+	struct leapraid_starget_priv *starget_priv;
+	struct Scsi_Host *shost;
+	unsigned long flags;
+
+	shost = dev_to_shost(&tgt->dev);
+	adapter = shost_priv(shost);
+	starget_priv = tgt->hostdata;
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	sas_dev = leapraid_hold_lock_get_sas_dev_from_tgt(adapter,
+							  starget_priv);
+	if (sas_dev &&
+	    sas_dev->starget == tgt &&
+	    sas_dev->id == tgt->id &&
+	    sas_dev->channel == tgt->channel)
+		sas_dev->starget = NULL;
+
+	if (sas_dev) {
+		starget_priv->sas_dev = NULL;
+		leapraid_sdev_put(sas_dev);
+		leapraid_sdev_put(sas_dev);
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+}
+
+static void leapraid_target_destroy(struct scsi_target *tgt)
+{
+	struct leapraid_starget_priv *starget_priv;
+
+	starget_priv = tgt->hostdata;
+	if (!starget_priv)
+		return;
+
+	if (tgt->channel == RAID_CHANNEL) {
+		leapraid_target_destroy_raid(tgt);
+		goto out_free;
+	}
+
+	leapraid_target_destroy_sas(tgt);
+
+out_free:
+	kfree(starget_priv);
+	tgt->hostdata = NULL;
+}
+
+static bool leapraid_scan_check_status(struct leapraid_adapter *adapter,
+				       bool *need_hard_reset)
+{
+	u32 adapter_state;
+
+	if (adapter->scan_dev_desc.scan_start) {
+		adapter_state = leapraid_get_adapter_state(adapter);
+		if (adapter_state == LEAPRAID_DB_FAULT) {
+			*need_hard_reset = true;
+			return true;
+		}
+		return false;
+	}
+
+	if (adapter->driver_cmds.scan_dev_cmd.status & LEAPRAID_CMD_RESET) {
+		dev_err(&adapter->pdev->dev,
+			"Device scan: Aborted due to reset\n");
+		adapter->driver_cmds.scan_dev_cmd.status =
+			LEAPRAID_CMD_NOT_USED;
+		adapter->scan_dev_desc.driver_loading = 0;
+		wake_up(&adapter->scan_dev_desc.wait_driver_loading);
+		return true;
+	}
+
+	if (adapter->scan_dev_desc.scan_start_failed) {
+		dev_err(&adapter->pdev->dev,
+			"Device scan: Failed with adapter_status=0x%08x\n",
+			adapter->scan_dev_desc.scan_start_failed);
+		adapter->scan_dev_desc.driver_loading = 0;
+		wake_up(&adapter->scan_dev_desc.wait_driver_loading);
+		adapter->scan_dev_desc.wait_scan_dev_done = 0;
+		adapter->access_ctrl.host_removing = 1;
+		return true;
+	}
+
+	adapter->driver_cmds.scan_dev_cmd.status = LEAPRAID_CMD_NOT_USED;
+	leapraid_scan_dev_done(adapter);
+	return true;
+}
+
+static int leapraid_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+	struct leapraid_adapter *adapter = shost_priv(shost);
+	bool need_hard_reset = false;
+
+	if (time >= (LEAPRAID_SCAN_DEV_CMD_TIMEOUT * HZ)) {
+		adapter->driver_cmds.scan_dev_cmd.status =
+			LEAPRAID_CMD_NOT_USED;
+		dev_err(&adapter->pdev->dev,
+			"Device scan: Failed with timeout 300s\n");
+		adapter->scan_dev_desc.driver_loading = 0;
+		wake_up(&adapter->scan_dev_desc.wait_driver_loading);
+		return 1;
+	}
+
+	if (!leapraid_scan_check_status(adapter, &need_hard_reset))
+		return 0;
+
+	if (need_hard_reset) {
+		adapter->driver_cmds.scan_dev_cmd.status =
+			LEAPRAID_CMD_NOT_USED;
+		dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n",
+			 __func__, __LINE__);
+		if (leapraid_hard_reset_handler(adapter, PART_RESET)) {
+			adapter->scan_dev_desc.driver_loading = 0;
+			wake_up(&adapter->scan_dev_desc.wait_driver_loading);
+		}
+	}
+
+	return 1;
+}
+
+static void leapraid_scan_start(struct Scsi_Host *shost)
+{
+	struct leapraid_adapter *adapter = shost_priv(shost);
+
+	adapter->scan_dev_desc.scan_start = 1;
+	leapraid_scan_dev(adapter, true);
+}
+
+static u32 leapraid_get_raid_qd(struct leapraid_adapter *adapter,
+				const struct leapraid_raid_volume *raid_volume,
+				bool default_qd)
+{
+	const struct leapraid_adapter_attr *attr = &adapter->adapter_attr;
+
+	if (raid_volume->vol_type != LEAPRAID_VOL_TYPE_RAID0)
+		return default_qd ? LEAPRAID_RAID_QUEUE_DEPTH :
+			attr->raid_volume_max_queue_depth;
+
+	if (raid_volume->dev_info & LEAPRAID_DEVTYP_SSP_TGT)
+		return default_qd ? LEAPRAID_SAS_QUEUE_DEPTH :
+			attr->narrowport_max_queue_depth;
+
+	return default_qd ? LEAPRAID_SATA_QUEUE_DEPTH :
+		attr->sata_max_queue_depth;
+}
+
+static int leapraid_calc_max_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+	struct Scsi_Host *shost;
+	struct leapraid_adapter *adapter;
+	struct leapraid_starget_priv *starget_priv;
+	struct leapraid_sdev_priv *sdev_priv;
+	struct leapraid_raid_volume *raid_volume;
+	struct leapraid_sas_dev *sas_dev;
+	int max_depth;
+	u32 default_qdepth = 0;
+	u32 fw_qdepth = 0;
+
+	shost = sdev->host;
+	adapter = shost_priv(shost);
+	max_depth = shost->can_queue;
+
+	sdev_priv = sdev->hostdata;
+	if (!sdev_priv)
+		goto out_tag_check;
+
+	starget_priv = sdev_priv->starget_priv;
+	if (!starget_priv)
+		goto out_tag_check;
+
+	if (starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME) {
+		raid_volume = leapraid_raid_volume_find_by_hdl(
+					adapter, starget_priv->hdl);
+		if (raid_volume) {
+			default_qdepth = leapraid_get_raid_qd(
+					adapter, raid_volume, true);
+			fw_qdepth = leapraid_get_raid_qd(
+					adapter, raid_volume, false);
+			leapraid_raid_volume_put(raid_volume);
+		}
+		goto out_limit_check;
+	}
+
+	sas_dev = leapraid_get_sas_dev_from_tgt(adapter, starget_priv);
+	if (sas_dev) {
+		if (sas_dev->dev_info & LEAPRAID_DEVTYP_SSP_TGT) {
+			default_qdepth = LEAPRAID_SAS_QUEUE_DEPTH;
+			fw_qdepth = (sas_dev->port_connection > 1) ?
+			      adapter->adapter_attr.wideport_max_queue_depth :
+			      adapter->adapter_attr.narrowport_max_queue_depth;
+		}
+		if (sas_dev->dev_info & LEAPRAID_DEVTYP_SATA_DEV) {
+			default_qdepth = LEAPRAID_SATA_QUEUE_DEPTH;
+			fw_qdepth = adapter->adapter_attr.sata_max_queue_depth;
+		}
+		leapraid_sdev_put(sas_dev);
+	}
+
+out_limit_check:
+	if (fw_qdepth > shost->can_queue && default_qdepth)
+		fw_qdepth = default_qdepth;
+
+	if (fw_qdepth)
+		max_depth = min_t(int, max_depth, fw_qdepth);
+
+out_tag_check:
+	if (!sdev->tagged_supported)
+		max_depth = 1;
+
+	if (qdepth > max_depth)
+		qdepth = max_depth;
+
+	return qdepth;
+}
+
+int leapraid_change_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+	qdepth = leapraid_calc_max_queue_depth(sdev, qdepth);
+	scsi_change_queue_depth(sdev, qdepth);
+	return sdev->queue_depth;
+}
+
+static void leapraid_map_queues(struct Scsi_Host *shost)
+{
+	struct leapraid_adapter *adapter;
+	struct blk_mq_queue_map *queue_map;
+	int msix_queue_count;
+	int poll_queue_count;
+	int queue_offset;
+	int map_index;
+
+	adapter = (struct leapraid_adapter *)shost->hostdata;
+	if (shost->nr_hw_queues == 1)
+		return;
+
+	msix_queue_count = adapter->notification_desc.iopoll_qdex;
+	poll_queue_count = adapter->adapter_attr.rq_cnt - msix_queue_count;
+
+	queue_offset = 0;
+	for (map_index = 0; map_index < shost->nr_maps; map_index++) {
+		queue_map = &shost->tag_set.map[map_index];
+		queue_map->nr_queues = 0;
+
+		switch (map_index) {
+		case HCTX_TYPE_DEFAULT:
+			queue_map->nr_queues = msix_queue_count;
+			queue_map->queue_offset = queue_offset;
+			WARN_ON_ONCE(!queue_map->nr_queues);
+			blk_mq_map_hw_queues(queue_map,
+					     &adapter->pdev->dev, 0);
+			break;
+		case HCTX_TYPE_POLL:
+			queue_map->nr_queues = poll_queue_count;
+			queue_map->queue_offset = queue_offset;
+			blk_mq_map_queues(queue_map);
+			break;
+		default:
+			queue_map->queue_offset = queue_offset;
+			blk_mq_map_hw_queues(queue_map,
+					     &adapter->pdev->dev, 0);
+			break;
+		}
+		queue_offset += queue_map->nr_queues;
+	}
+}
+
+int leapraid_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num)
+{
+	struct leapraid_adapter *adapter =
+		(struct leapraid_adapter *)shost->hostdata;
+	struct leapraid_blk_mq_poll_rq *blk_mq_poll_rq;
+	int num_entries;
+	int qid = queue_num - adapter->notification_desc.iopoll_qdex;
+
+	blk_mq_poll_rq = &adapter->notification_desc.blk_mq_poll_rqs[qid];
+	if (atomic_read(&blk_mq_poll_rq->pause) ||
+	    !atomic_add_unless(&blk_mq_poll_rq->busy, 1, 1))
+		return 0;
+
+	num_entries = leapraid_rep_queue_handler(&blk_mq_poll_rq->rq);
+	atomic_dec(&blk_mq_poll_rq->busy);
+	return num_entries;
+}
+
+static int leapraid_bios_param(struct scsi_device *sdev,
+			       struct gendisk *disk, sector_t capacity,
+			       int geom[])
+{
+	int heads;
+	int sectors;
+	sector_t cylinders;
+
+	if (scsi_partsize(disk, capacity, geom))
+		return 0;
+
+	if ((ulong)capacity >= LEAPRAID_LARGE_DISK_THRESHOLD) {
+		heads = LEAPRAID_LARGE_DISK_HEADS;
+		sectors = LEAPRAID_LARGE_DISK_SECTORS;
+	} else {
+		heads = LEAPRAID_SMALL_DISK_HEADS;
+		sectors = LEAPRAID_SMALL_DISK_SECTORS;
+	}
+
+	cylinders = capacity;
+	sector_div(cylinders, heads * sectors);
+
+	geom[0] = heads;
+	geom[1] = sectors;
+	geom[2] = cylinders;
+	return 0;
+}
+
+static ssize_t fw_queue_depth_show(struct device *cdev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct Scsi_Host *shost = class_to_shost(cdev);
+	struct leapraid_adapter *adapter = shost_priv(shost);
+
+	return sysfs_emit(buf, "%02d\n",
+			  adapter->adapter_attr.features.req_slot);
+}
+
+static ssize_t host_sas_address_show(struct device *cdev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct Scsi_Host *shost = class_to_shost(cdev);
+	struct leapraid_adapter *adapter = shost_priv(shost);
+
+	return sysfs_emit(buf, "0x%016llx\n",
+		(unsigned long long)adapter->dev_topo.card.sas_address);
+}
+
+static ssize_t board_name_show(struct device *cdev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct Scsi_Host *shost = class_to_shost(cdev);
+	struct leapraid_adapter *adapter = shost_priv(shost);
+
+	return sysfs_emit(buf, "%s\n", adapter->adapter_attr.board_name);
+}
+
+static DEVICE_ATTR_RO(fw_queue_depth);
+static DEVICE_ATTR_RO(host_sas_address);
+static DEVICE_ATTR_RO(board_name);
+
+static struct attribute *leapraid_shost_attrs[] = {
+	&dev_attr_fw_queue_depth.attr,
+	&dev_attr_host_sas_address.attr,
+	&dev_attr_board_name.attr,
+	NULL,
+};
+
+ATTRIBUTE_GROUPS(leapraid_shost);
+
+static ssize_t sas_device_handle_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct leapraid_sdev_priv *sas_device_priv_data = sdev->hostdata;
+
+	if (!sas_device_priv_data || !sas_device_priv_data->starget_priv) {
+		dev_err(&sdev->sdev_gendev,
+			"%s: Invalid sdev_priv or starget_priv\n", __func__);
+		return -EINVAL;
+	}
+
+	return sysfs_emit(buf, "0x%04x\n",
+			  sas_device_priv_data->starget_priv->hdl);
+}
+
+static ssize_t ncq_cmd_prio_enable_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct leapraid_sdev_priv *sas_device_priv_data = sdev->hostdata;
+
+	if (!sas_device_priv_data) {
+		dev_err(&sdev->sdev_gendev,
+			"%s: Invalid sdev_priv\n", __func__);
+		return -EINVAL;
+	}
+
+	return sysfs_emit(buf, "%d\n",
+			  sas_device_priv_data->ncq_cmd_prio_enable);
+}
+
+static ssize_t ncq_cmd_prio_enable_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct leapraid_sdev_priv *sas_device_priv_data = sdev->hostdata;
+	struct scsi_vpd *vpd_pg89;
+	int ncq_cmd_prio_enable;
+	bool ncq_supported;
+
+	if (!sas_device_priv_data) {
+		dev_err(&sdev->sdev_gendev,
+			"%s: Invalid sdev_priv\n", __func__);
+		return -EINVAL;
+	}
+
+	if (kstrtoint(buf, 0, &ncq_cmd_prio_enable))
+		return -EINVAL;
+
+	if (ncq_cmd_prio_enable != 0 && ncq_cmd_prio_enable != 1) {
+		dev_err(&sdev->sdev_gendev,
+			"%s: Invalid NCQ cmd prio %d (0/1 only)\n",
+			__func__, ncq_cmd_prio_enable);
+		return -EINVAL;
+	}
+
+	rcu_read_lock();
+	vpd_pg89 = rcu_dereference(sdev->vpd_pg89);
+	if (!vpd_pg89 || vpd_pg89->len < LEAPRAID_VPD_PG89_MIN_LEN) {
+		rcu_read_unlock();
+		return -EINVAL;
+	}
+
+	ncq_supported = (vpd_pg89->data[LEAPRAID_VPD_PG89_NCQ_BYTE_IDX] >>
+			 LEAPRAID_VPD_PG89_NCQ_BIT_SHIFT) &
+			LEAPRAID_VPD_PG89_NCQ_BIT_MASK;
+	rcu_read_unlock();
+	if (ncq_supported)
+		sas_device_priv_data->ncq_cmd_prio_enable =
+			ncq_cmd_prio_enable;
+	return count;
+}
+
+static DEVICE_ATTR_RO(sas_device_handle);
+
+static DEVICE_ATTR_RW(ncq_cmd_prio_enable);
+
+static struct attribute *leapraid_sdev_attrs[] = {
+	&dev_attr_sas_device_handle.attr,
+	&dev_attr_ncq_cmd_prio_enable.attr,
+	NULL,
+};
+
+ATTRIBUTE_GROUPS(leapraid_sdev);
+
+static struct scsi_host_template leapraid_driver_template = {
+	.module = THIS_MODULE,
+	.name = "LEAPIO RAID Host",
+	.proc_name = LEAPRAID_DRIVER_NAME,
+	.queuecommand = leapraid_queuecommand,
+	.cmd_size = sizeof(struct leapraid_io_req_tracker),
+	.init_cmd_priv = leapraid_init_cmd_priv,
+	.exit_cmd_priv = leapraid_exit_cmd_priv,
+	.eh_abort_handler = leapraid_eh_abort_handler,
+	.eh_device_reset_handler = leapraid_eh_device_reset_handler,
+	.eh_target_reset_handler = leapraid_eh_target_reset_handler,
+	.eh_host_reset_handler = leapraid_eh_host_reset_handler,
+	.sdev_init = leapraid_sdev_init,
+	.sdev_destroy = leapraid_sdev_destroy,
+	.sdev_configure = leapraid_sdev_configure,
+	.target_alloc = leapraid_target_alloc,
+	.target_destroy = leapraid_target_destroy,
+	.scan_finished = leapraid_scan_finished,
+	.scan_start = leapraid_scan_start,
+	.change_queue_depth = leapraid_change_queue_depth,
+	.map_queues = leapraid_map_queues,
+	.mq_poll = leapraid_blk_mq_poll,
+	.bios_param = leapraid_bios_param,
+	.can_queue = LEAPRAID_CAN_QUEUE_MIN,
+	.this_id = LEAPRAID_THIS_ID_NONE,
+	.sg_tablesize = LEAPRAID_SG_DEPTH,
+	.max_sectors = LEAPRAID_MAX_SECTORS,
+	.max_segment_size = LEAPRAID_MAX_SEGMENT_SIZE,
+	.cmd_per_lun = LEAPRAID_CMD_PER_LUN,
+	.shost_groups = leapraid_shost_groups,
+	.sdev_groups = leapraid_sdev_groups,
+	.track_queue_depth = 1,
+};
+
+static void leapraid_lock_init(struct leapraid_adapter *adapter)
+{
+	mutex_init(&adapter->reset_desc.adapter_reset_mutex);
+	mutex_init(&adapter->reset_desc.host_diag_mutex);
+	mutex_init(&adapter->access_ctrl.pci_access_lock);
+
+	spin_lock_init(&adapter->reset_desc.adapter_reset_lock);
+	spin_lock_init(&adapter->dynamic_task_desc.task_lock);
+	spin_lock_init(&adapter->dev_topo.sas_dev_lock);
+	spin_lock_init(&adapter->dev_topo.topo_node_lock);
+	spin_lock_init(&adapter->fw_evt_s.fw_evt_lock);
+	spin_lock_init(&adapter->dev_topo.raid_volume_lock);
+	spin_lock_init(&adapter->dev_topo.enc_lock);
+	spin_lock_init(&adapter->boot_devs.lock);
+}
+
+static void leapraid_list_init(struct leapraid_adapter *adapter)
+{
+	INIT_LIST_HEAD(&adapter->dev_topo.sas_dev_list);
+	INIT_LIST_HEAD(&adapter->dev_topo.card_port_list);
+	INIT_LIST_HEAD(&adapter->dev_topo.sas_dev_init_list);
+	INIT_LIST_HEAD(&adapter->dev_topo.exp_list);
+	INIT_LIST_HEAD(&adapter->dev_topo.enc_list);
+	INIT_LIST_HEAD(&adapter->fw_evt_s.fw_evt_list);
+	INIT_LIST_HEAD(&adapter->dev_topo.raid_volume_list);
+	INIT_LIST_HEAD(&adapter->dev_topo.card.sas_port_list);
+}
+
+static int leapraid_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct leapraid_adapter *adapter;
+	struct Scsi_Host *shost;
+	int iopoll_q_count;
+	int rc;
+
+	shost = scsi_host_alloc(&leapraid_driver_template,
+				sizeof(struct leapraid_adapter));
+	if (!shost) {
+		dev_err(&pdev->dev,
+			"%s: SCSI host alloc failed\n", __func__);
+		return -ENODEV;
+	}
+
+	adapter = shost_priv(shost);
+	memset(adapter, 0, sizeof(struct leapraid_adapter));
+
+	init_waitqueue_head(&adapter->access_ctrl.recovery_waitq);
+	adapter->adapter_attr.id = atomic_inc_return(&leapraid_ids) - 1;
+
+	adapter->adapter_attr.enable_mp = enable_mp;
+
+	adapter = shost_priv(shost);
+	INIT_LIST_HEAD(&adapter->list);
+
+	adapter->shost = shost;
+	adapter->pdev = pdev;
+	adapter->fw_log_desc.open_pcie_trace = open_pcie_trace;
+	atomic_set(&adapter->fw_log_desc.mmap_refcnt, 0);
+	init_waitqueue_head(&adapter->fw_log_desc.mmap_waitq);
+	leapraid_lock_init(adapter);
+	leapraid_list_init(adapter);
+	snprintf(adapter->adapter_attr.name, LEAPRAID_NAME_LENGTH, "%s%d",
+		 LEAPRAID_DRIVER_NAME, adapter->adapter_attr.id);
+
+	shost->max_cmd_len = LEAPRAID_MAX_CDB_LEN;
+	shost->max_lun = LEAPRAID_MAX_LUNS;
+	shost->transportt = leapraid_transport_template;
+	shost->unique_id = adapter->adapter_attr.id;
+
+	snprintf(adapter->fw_evt_s.fw_evt_name,
+		 sizeof(adapter->fw_evt_s.fw_evt_name),
+		 "fw_event_%s%d", LEAPRAID_DRIVER_NAME,
+		 adapter->adapter_attr.id);
+	adapter->fw_evt_s.fw_evt_thread =
+		alloc_ordered_workqueue(adapter->fw_evt_s.fw_evt_name, 0);
+	if (!adapter->fw_evt_s.fw_evt_thread) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Failed to create fw event workqueue\n", __func__);
+		rc = -ENODEV;
+		goto evt_wq_fail;
+	}
+
+	shost->host_tagset = 1;
+	init_waitqueue_head(&adapter->scan_dev_desc.wait_driver_loading);
+	adapter->scan_dev_desc.driver_loading = 1;
+	if (leapraid_ctrl_init(adapter)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Adapter init failed\n", __func__);
+		rc = -ENODEV;
+		goto ctrl_init_fail;
+	}
+
+	shost->nr_hw_queues = 1;
+	if (shost->host_tagset) {
+		shost->nr_hw_queues = adapter->adapter_attr.rq_cnt;
+		iopoll_q_count = adapter->adapter_attr.rq_cnt -
+				 adapter->notification_desc.iopoll_qdex;
+		shost->nr_maps = iopoll_q_count ? 3 : 1;
+		dev_info(&adapter->pdev->dev,
+			 "Max scsi I/O cmds %d shared with nr_hw_queues=%d\n",
+			 shost->can_queue, shost->nr_hw_queues);
+	}
+
+	rc = scsi_add_host(shost, &pdev->dev);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: SCSI host add failed\n", __func__);
+		goto scsi_add_shost_fail;
+	}
+
+	spin_lock(&leapraid_adapter_lock);
+	list_add_tail(&adapter->list, &leapraid_adapter_list);
+	spin_unlock(&leapraid_adapter_lock);
+
+	scsi_scan_host(shost);
+	return 0;
+
+scsi_add_shost_fail:
+	leapraid_remove_ctrl(adapter);
+ctrl_init_fail:
+	leapraid_overheat_cleanup(adapter);
+	destroy_workqueue(adapter->fw_evt_s.fw_evt_thread);
+evt_wq_fail:
+	scsi_host_put(shost);
+	return rc;
+}
+
+void leapraid_cleanup_lists(struct leapraid_adapter *adapter)
+{
+	struct leapraid_raid_volume *raid_volume, *next_raid_volume;
+	struct leapraid_starget_priv *starget_priv_data;
+	struct leapraid_sas_port *leapraid_port, *next_port;
+	struct leapraid_card_port *port, *port_next;
+	struct leapraid_vphy *vphy, *vphy_next;
+
+	list_for_each_entry_safe(raid_volume, next_raid_volume,
+				 &adapter->dev_topo.raid_volume_list, list) {
+		if (raid_volume->starget) {
+			starget_priv_data = raid_volume->starget->hostdata;
+			if (starget_priv_data)
+				starget_priv_data->deleted = 1;
+			scsi_remove_target(&raid_volume->starget->dev);
+		}
+		dev_info(&adapter->pdev->dev,
+			 "removing hdl=0x%04x, wwid=0x%016llx\n",
+			 raid_volume->hdl,
+			 (unsigned long long)raid_volume->wwid);
+		leapraid_raid_volume_remove(adapter, raid_volume);
+	}
+
+	list_for_each_entry_safe(leapraid_port, next_port,
+				 &adapter->dev_topo.card.sas_port_list,
+				 port_list) {
+		if (leapraid_port->remote_identify.device_type ==
+		    SAS_END_DEVICE)
+			leapraid_sas_dev_remove_by_sas_address(
+				adapter,
+				leapraid_port->remote_identify.sas_address,
+				leapraid_port->card_port);
+		else if (leapraid_port->remote_identify.device_type ==
+				SAS_EDGE_EXPANDER_DEVICE ||
+			 leapraid_port->remote_identify.device_type ==
+				SAS_FANOUT_EXPANDER_DEVICE)
+			leapraid_exp_rm(
+				adapter,
+				leapraid_port->remote_identify.sas_address,
+				leapraid_port->card_port);
+	}
+
+	list_for_each_entry_safe(port, port_next,
+				 &adapter->dev_topo.card_port_list, list) {
+		if (port->vphys_mask)
+			list_for_each_entry_safe(vphy, vphy_next,
+						 &port->vphys_list, list) {
+				list_del(&vphy->list);
+				kfree(vphy);
+			}
+		list_del(&port->list);
+		kfree(port);
+	}
+
+	if (adapter->dev_topo.card.phys_num) {
+		kfree(adapter->dev_topo.card.card_phy);
+		adapter->dev_topo.card.card_phy = NULL;
+		adapter->dev_topo.card.phys_num = 0;
+	}
+}
+
+static void leapraid_remove(struct pci_dev *pdev)
+{
+	struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
+	struct Scsi_Host *shost = pci_get_drvdata(pdev);
+	struct workqueue_struct *wq;
+	unsigned long flags;
+
+	if (!shost || !adapter) {
+		dev_err(&pdev->dev, "Unable to remove!\n");
+		return;
+	}
+
+	wait_event(adapter->scan_dev_desc.wait_driver_loading,
+		   !adapter->scan_dev_desc.driver_loading);
+	wait_event(adapter->scan_dev_desc.wait_driver_loading,
+		   !atomic_read(&adapter->overheat_desc.thermal_alert));
+
+	WRITE_ONCE(adapter->access_ctrl.host_removing, 1);
+	spin_lock(&leapraid_adapter_lock);
+	list_del(&adapter->list);
+	spin_unlock(&leapraid_adapter_lock);
+
+	leapraid_wait_cmds_done(adapter);
+
+	if (leapraid_pci_removed(adapter)) {
+		leapraid_mq_polling_pause(adapter);
+		leapraid_clean_active_scsi_cmds(adapter);
+	}
+	leapraid_clean_active_fw_evt(adapter);
+
+	spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags);
+	wq = adapter->fw_evt_s.fw_evt_thread;
+	adapter->fw_evt_s.fw_evt_thread = NULL;
+	spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags);
+	if (wq)
+		destroy_workqueue(wq);
+
+	sas_remove_host(shost);
+	leapraid_cleanup_lists(adapter);
+	leapraid_remove_ctrl(adapter);
+	scsi_host_put(shost);
+}
+
+static void leapraid_shutdown(struct pci_dev *pdev)
+{
+	struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
+	struct Scsi_Host *shost = pci_get_drvdata(pdev);
+	struct workqueue_struct *wq;
+	unsigned long flags;
+
+	if (!shost || !adapter) {
+		dev_err(&pdev->dev, "Unable to shutdown!\n");
+		return;
+	}
+
+	adapter->access_ctrl.host_removing = 1;
+	leapraid_wait_cmds_done(adapter);
+	leapraid_clean_active_fw_evt(adapter);
+	leapraid_overheat_cleanup(adapter);
+	leapraid_fw_log_stop(adapter);
+	spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags);
+	wq = adapter->fw_evt_s.fw_evt_thread;
+	adapter->fw_evt_s.fw_evt_thread = NULL;
+	spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags);
+	if (wq)
+		destroy_workqueue(wq);
+
+	leapraid_disable_controller(adapter);
+}
+
+static pci_ers_result_t leapraid_pci_error_detected(struct pci_dev *pdev,
+						    pci_channel_state_t state)
+{
+	struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
+	struct Scsi_Host *shost = pci_get_drvdata(pdev);
+
+	if (!shost || !adapter) {
+		dev_err(&pdev->dev, "Failed to error detected for device\n");
+		return PCI_ERS_RESULT_DISCONNECT;
+	}
+
+	dev_err(&pdev->dev, "%s: PCI error detected, state=%d\n",
+		adapter->adapter_attr.name, state);
+
+	switch (state) {
+	case pci_channel_io_normal:
+		return PCI_ERS_RESULT_CAN_RECOVER;
+	case pci_channel_io_frozen:
+		adapter->access_ctrl.pcie_recovering = 1;
+		scsi_block_requests(adapter->shost);
+		leapraid_overheat_cleanup(adapter);
+		leapraid_check_scheduled_fault_stop(adapter);
+		leapraid_fw_log_stop(adapter);
+		leapraid_disable_controller(adapter);
+		return PCI_ERS_RESULT_NEED_RESET;
+	case pci_channel_io_perm_failure:
+		adapter->access_ctrl.pcie_recovering = 1;
+		leapraid_overheat_cleanup(adapter);
+		leapraid_check_scheduled_fault_stop(adapter);
+		leapraid_fw_log_stop(adapter);
+		leapraid_mq_polling_pause(adapter);
+		leapraid_clean_active_scsi_cmds(adapter);
+		return PCI_ERS_RESULT_DISCONNECT;
+	}
+
+	return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t leapraid_pci_mmio_enabled(struct pci_dev *pdev)
+{
+	struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
+	struct Scsi_Host *shost = pci_get_drvdata(pdev);
+
+	if (!shost || !adapter) {
+		dev_err(&pdev->dev,
+			"Failed to enable mmio for device\n");
+		return PCI_ERS_RESULT_DISCONNECT;
+	}
+
+	dev_info(&pdev->dev, "%s: PCI error mmio enabled\n",
+		 adapter->adapter_attr.name);
+
+	return PCI_ERS_RESULT_RECOVERED;
+}
+
+static pci_ers_result_t leapraid_pci_slot_reset(struct pci_dev *pdev)
+{
+	struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
+	struct Scsi_Host *shost = pci_get_drvdata(pdev);
+	int rc;
+
+	if (!shost || !adapter) {
+		dev_err(&pdev->dev,
+			"Failed to slot reset for device\n");
+		return PCI_ERS_RESULT_DISCONNECT;
+	}
+
+	dev_err(&pdev->dev, "%s PCI error slot reset\n",
+		adapter->adapter_attr.name);
+
+	adapter->pdev = pdev;
+	pci_restore_state(pdev);
+	if (leapraid_set_pcie_and_notification(adapter)) {
+		dev_err(&pdev->dev,
+			"%s: Failed to set PCIe state and notification\n",
+			__func__);
+		return PCI_ERS_RESULT_DISCONNECT;
+	}
+
+	adapter->access_ctrl.pcie_recovering = 0;
+	dev_info(&pdev->dev, "%s: Hard reset triggered by PCI slot reset\n",
+		 adapter->adapter_attr.name);
+	dev_info(&adapter->pdev->dev, "%s: %d: call hard_reset\n",
+		 __func__, __LINE__);
+	rc = leapraid_hard_reset_handler(adapter, FULL_RESET);
+	if (rc)
+		dev_err(&pdev->dev, "%s hard reset: failed\n",
+			adapter->adapter_attr.name);
+
+	return rc == 0 ? PCI_ERS_RESULT_RECOVERED :
+		 PCI_ERS_RESULT_DISCONNECT;
+}
+
+static void leapraid_pci_resume(struct pci_dev *pdev)
+{
+	struct Scsi_Host *shost = pci_get_drvdata(pdev);
+	struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
+
+	if (!shost || !adapter) {
+		dev_err(&pdev->dev, "Failed to resume\n");
+		return;
+	}
+
+	dev_err(&pdev->dev, "PCI error resume!\n");
+
+	pci_aer_clear_nonfatal_status(pdev);
+	leapraid_check_scheduled_fault_start(adapter);
+	leapraid_fw_log_start(adapter);
+	scsi_unblock_requests(adapter->shost);
+}
+
+MODULE_DEVICE_TABLE(pci, leapraid_pci_table);
+static struct pci_error_handlers leapraid_err_handler = {
+	.error_detected = leapraid_pci_error_detected,
+	.mmio_enabled = leapraid_pci_mmio_enabled,
+	.slot_reset = leapraid_pci_slot_reset,
+	.resume = leapraid_pci_resume,
+};
+
+#ifdef CONFIG_PM
+static int leapraid_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
+	struct Scsi_Host *shost = pci_get_drvdata(pdev);
+	pci_power_t device_state;
+
+	if (!shost || !adapter) {
+		dev_err(&pdev->dev,
+			"Suspend failed, invalid host or adapter\n");
+		return -ENXIO;
+	}
+
+	leapraid_overheat_cleanup(adapter);
+	leapraid_check_scheduled_fault_stop(adapter);
+	leapraid_fw_log_stop(adapter);
+	scsi_block_requests(shost);
+	device_state = pci_choose_state(pdev, state);
+
+	dev_info(&pdev->dev, "Entering PCI power state D%d, (slot=%s)\n",
+		 device_state, pci_name(pdev));
+
+	pci_save_state(pdev);
+	leapraid_disable_controller(adapter);
+	pci_set_power_state(pdev, device_state);
+	return 0;
+}
+
+static int leapraid_resume(struct pci_dev *pdev)
+{
+	struct leapraid_adapter *adapter = pdev_to_adapter(pdev);
+	struct Scsi_Host *shost = pci_get_drvdata(pdev);
+	pci_power_t device_state = pdev->current_state;
+	int rc;
+
+	if (!shost || !adapter) {
+		dev_err(&pdev->dev,
+			"Resume failed, invalid host or adapter\n");
+		return -ENXIO;
+	}
+
+	dev_info(&pdev->dev,
+		 "Resuming device %s, previous state D%d\n",
+		 pci_name(pdev), device_state);
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_enable_wake(pdev, PCI_D0, 0);
+	pci_restore_state(pdev);
+	adapter->pdev = pdev;
+	rc = leapraid_set_pcie_and_notification(adapter);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: Failed to set PCIe state and notification\n",
+			__func__);
+		return rc;
+	}
+
+	dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n",
+		 __func__, __LINE__);
+	rc = leapraid_hard_reset_handler(adapter, PART_RESET);
+	if (rc) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Hard reset failed during resume, rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+	scsi_unblock_requests(shost);
+	leapraid_check_scheduled_fault_start(adapter);
+	leapraid_fw_log_start(adapter);
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct pci_driver leapraid_driver = {
+	.name = LEAPRAID_DRIVER_NAME,
+	.id_table = leapraid_pci_table,
+	.probe = leapraid_probe,
+	.remove = leapraid_remove,
+	.shutdown = leapraid_shutdown,
+	.err_handler = &leapraid_err_handler,
+#ifdef CONFIG_PM
+	.suspend = leapraid_suspend,
+	.resume = leapraid_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init leapraid_init(void)
+{
+	int error;
+
+	pr_info("%s initializing\n", LEAPRAID_DRIVER_NAME);
+
+	leapraid_transport_template =
+		sas_attach_transport(&leapraid_transport_functions);
+	if (!leapraid_transport_template) {
+		pr_err("%s: Failed to attach SAS transport\n",
+		       LEAPRAID_DRIVER_NAME);
+		return -ENODEV;
+	}
+
+	error = pci_register_driver(&leapraid_driver);
+	if (error) {
+		pr_err("%s: PCI driver registration failed: %d\n",
+		       LEAPRAID_DRIVER_NAME, error);
+		sas_release_transport(leapraid_transport_template);
+		return error;
+	}
+
+	error = leapraid_ctl_init();
+	if (error) {
+		pci_unregister_driver(&leapraid_driver);
+		sas_release_transport(leapraid_transport_template);
+		return error;
+	}
+
+	return 0;
+}
+
+static void __exit leapraid_exit(void)
+{
+	pr_info("%s exiting\n", LEAPRAID_DRIVER_NAME);
+
+	leapraid_ctl_exit();
+	pci_unregister_driver(&leapraid_driver);
+	sas_release_transport(leapraid_transport_template);
+}
+
+module_init(leapraid_init);
+module_exit(leapraid_exit);
diff --git a/drivers/scsi/leapraid/leapraid_transport.c b/drivers/scsi/leapraid/leapraid_transport.c
new file mode 100644
index 000000000000..77ffbd6ddce4
--- /dev/null
+++ b/drivers/scsi/leapraid/leapraid_transport.c
@@ -0,0 +1,1403 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 LeapIO Tech Inc.
+ *
+ * LeapRAID storage and RAID controller driver.
+ */
+#include <scsi/scsi_host.h>
+
+#include "leapraid_func.h"
+
+static struct leapraid_topo_node *leapraid_transport_topo_node_by_sas_addr(
+		struct leapraid_adapter *adapter,
+		u64 sas_addr,
+		struct leapraid_card_port *card_port)
+{
+	if (adapter->dev_topo.card.sas_address == sas_addr)
+		return &adapter->dev_topo.card;
+
+	return leapraid_exp_find_by_sas_address(adapter, sas_addr, card_port);
+}
+
+static u8 leapraid_get_port_id_by_expander(struct leapraid_adapter *adapter,
+					   struct sas_rphy *rphy)
+{
+	struct leapraid_topo_node *topo_node_exp;
+	unsigned long flags;
+	u8 port_id = 0xFF;
+
+	spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+	list_for_each_entry(topo_node_exp, &adapter->dev_topo.exp_list, list) {
+		if (topo_node_exp->rphy == rphy) {
+			port_id = topo_node_exp->card_port->port_id;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags);
+
+	return port_id;
+}
+
+static u8 leapraid_get_port_id_by_end_dev(struct leapraid_adapter *adapter,
+					  struct sas_rphy *rphy)
+{
+	struct leapraid_sas_dev *sas_dev;
+	unsigned long flags;
+	u8 port_id = 0xFF;
+
+	spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags);
+	sas_dev = leapraid_hold_lock_get_sas_dev_by_addr_and_rphy(
+			adapter,
+			rphy->identify.sas_address,
+			rphy);
+	if (sas_dev) {
+		port_id = sas_dev->card_port->port_id;
+		leapraid_sdev_put(sas_dev);
+	}
+	spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags);
+
+	return port_id;
+}
+
+static u8 leapraid_transport_get_port_id_by_rphy(
+		struct leapraid_adapter *adapter,
+		struct sas_rphy *rphy)
+{
+	if (!rphy)
+		return 0xFF;
+
+	switch (rphy->identify.device_type) {
+	case SAS_EDGE_EXPANDER_DEVICE:
+	case SAS_FANOUT_EXPANDER_DEVICE:
+		return leapraid_get_port_id_by_expander(adapter, rphy);
+	case SAS_END_DEVICE:
+		return leapraid_get_port_id_by_end_dev(adapter, rphy);
+	default:
+		return 0xFF;
+	}
+}
+
+static enum sas_linkrate leapraid_transport_convert_phy_link_rate(u8 link_rate)
+{
+	unsigned int i;
+
+	#define SAS_RATE_12G SAS_LINK_RATE_12_0_GBPS
+
+	const struct linkrate_map {
+		u8 in;
+		enum sas_linkrate out;
+	} linkrate_table[] = {
+		{
+			LEAPRAID_SAS_NEG_LINK_RATE_1_5,
+			SAS_LINK_RATE_1_5_GBPS
+		},
+		{
+			LEAPRAID_SAS_NEG_LINK_RATE_3_0,
+			SAS_LINK_RATE_3_0_GBPS
+		},
+		{
+			LEAPRAID_SAS_NEG_LINK_RATE_6_0,
+			SAS_LINK_RATE_6_0_GBPS
+		},
+		{
+			LEAPRAID_SAS_NEG_LINK_RATE_12_0,
+			SAS_RATE_12G
+		},
+		{
+			LEAPRAID_SAS_NEG_LINK_RATE_PHY_DISABLED,
+			SAS_PHY_DISABLED
+		},
+		{
+			LEAPRAID_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED,
+			SAS_LINK_RATE_FAILED
+		},
+		{
+			LEAPRAID_SAS_NEG_LINK_RATE_PORT_SELECTOR,
+			SAS_SATA_PORT_SELECTOR
+		},
+		{
+			LEAPRAID_SAS_NEG_LINK_RATE_SMP_RESETTING,
+			SAS_LINK_RATE_UNKNOWN
+		},
+		{
+			LEAPRAID_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE,
+			SAS_LINK_RATE_UNKNOWN
+		},
+		{
+			LEAPRAID_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE,
+			SAS_LINK_RATE_UNKNOWN
+		},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(linkrate_table); i++)
+		if (linkrate_table[i].in == link_rate)
+			return linkrate_table[i].out;
+
+	return SAS_LINK_RATE_UNKNOWN;
+}
+
+static void leapraid_set_identify_protocol_flags(u32 dev_info,
+						 struct sas_identify *identify)
+{
+	unsigned int i;
+
+	const struct protocol_mapping {
+		u32 mask;
+		u32 *target;
+		u32 protocol;
+	} mappings[] = {
+		{
+			LEAPRAID_DEVTYP_SSP_INIT,
+			&identify->initiator_port_protocols,
+			SAS_PROTOCOL_SSP
+		},
+		{
+			LEAPRAID_DEVTYP_STP_INIT,
+			&identify->initiator_port_protocols,
+			SAS_PROTOCOL_STP
+		},
+		{
+			LEAPRAID_DEVTYP_SMP_INIT,
+			&identify->initiator_port_protocols,
+			SAS_PROTOCOL_SMP
+		},
+		{
+			LEAPRAID_DEVTYP_SATA_HOST,
+			&identify->initiator_port_protocols,
+			SAS_PROTOCOL_SATA
+		},
+		{
+			LEAPRAID_DEVTYP_SSP_TGT,
+			&identify->target_port_protocols,
+			SAS_PROTOCOL_SSP
+		},
+		{
+			LEAPRAID_DEVTYP_STP_TGT,
+			&identify->target_port_protocols,
+			SAS_PROTOCOL_STP
+		},
+		{
+			LEAPRAID_DEVTYP_SMP_TGT,
+			&identify->target_port_protocols,
+			SAS_PROTOCOL_SMP
+		},
+		{
+			LEAPRAID_DEVTYP_SATA_DEV,
+			&identify->target_port_protocols,
+			SAS_PROTOCOL_SATA
+		},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(mappings); i++)
+		if (dev_info & mappings[i].mask && mappings[i].target)
+			*mappings[i].target |= mappings[i].protocol;
+}
+
+static int leapraid_transport_set_identify(struct leapraid_adapter *adapter,
+					   u16 hdl,
+					   struct sas_identify *identify)
+{
+	union cfg_param_1 cfgp1 = {0};
+	union cfg_param_2 cfgp2 = {0};
+	struct leapraid_sas_dev_p0 sas_dev_pg0;
+	u32 dev_info;
+
+	if ((adapter->access_ctrl.shost_recovering &&
+	     !adapter->scan_dev_desc.driver_loading) ||
+	    adapter->access_ctrl.pcie_recovering) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Failed, shost_recovering=%d pcie_recovering=%d\n",
+			 __func__,
+			 adapter->access_ctrl.shost_recovering,
+			 adapter->access_ctrl.pcie_recovering);
+		return -EFAULT;
+	}
+
+	cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL;
+	cfgp2.handle = hdl;
+	if (leapraid_op_config_page(adapter, &sas_dev_pg0, cfgp1,
+				    cfgp2, GET_SAS_DEVICE_PG0))
+		return -ENXIO;
+
+	memset(identify, 0, sizeof(struct sas_identify));
+	dev_info = le32_to_cpu(sas_dev_pg0.dev_info);
+	identify->sas_address = le64_to_cpu(sas_dev_pg0.sas_address);
+	identify->phy_identifier = sas_dev_pg0.phy_num;
+
+	switch (dev_info & LEAPRAID_DEVTYP_MASK_DEV_TYPE) {
+	case LEAPRAID_DEVTYP_NO_DEV:
+		identify->device_type = SAS_PHY_UNUSED;
+		break;
+	case LEAPRAID_DEVTYP_END_DEV:
+		identify->device_type = SAS_END_DEVICE;
+		break;
+	case LEAPRAID_DEVTYP_EDGE_EXPANDER:
+		identify->device_type = SAS_EDGE_EXPANDER_DEVICE;
+		break;
+	case LEAPRAID_DEVTYP_FANOUT_EXPANDER:
+		identify->device_type = SAS_FANOUT_EXPANDER_DEVICE;
+		break;
+	}
+
+	leapraid_set_identify_protocol_flags(dev_info, identify);
+
+	return 0;
+}
+
+static void leapraid_transport_exp_set_edev(struct leapraid_adapter *adapter,
+					    void *data_out,
+					    struct sas_expander_device *edev)
+{
+	struct leapraid_smp_passthrough_rep *smp_passthrough_rep;
+	struct leapraid_rep_manu_reply *rep_manu_reply;
+	u8 *component_id;
+
+	smp_passthrough_rep =
+		(void *)&adapter->driver_cmds.transport_cmd.reply;
+	if (le16_to_cpu(smp_passthrough_rep->resp_data_len) !=
+	    sizeof(struct leapraid_rep_manu_reply))
+		return;
+
+	rep_manu_reply = data_out + sizeof(struct leapraid_rep_manu_request);
+
+	memcpy(edev->vendor_id, rep_manu_reply->vendor_identification,
+	       SAS_EXPANDER_VENDOR_ID_LEN);
+	edev->vendor_id[SAS_EXPANDER_VENDOR_ID_LEN] = '\0';
+
+	memcpy(edev->product_id, rep_manu_reply->product_identification,
+	       SAS_EXPANDER_PRODUCT_ID_LEN);
+	edev->product_id[SAS_EXPANDER_PRODUCT_ID_LEN] = '\0';
+
+	memcpy(edev->product_rev, rep_manu_reply->product_revision_level,
+	       SAS_EXPANDER_PRODUCT_REV_LEN);
+	edev->product_rev[SAS_EXPANDER_PRODUCT_REV_LEN] = '\0';
+
+	edev->level = rep_manu_reply->sas_format & 1;
+	if (edev->level) {
+		memcpy(edev->component_vendor_id,
+		       rep_manu_reply->comp_vendor_identification,
+		       SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
+		edev->component_vendor_id[
+			SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN] = '\0';
+
+		component_id = (u8 *)&rep_manu_reply->component_id;
+		edev->component_id = component_id[0] << 8 | component_id[1];
+		edev->component_revision_id =
+			rep_manu_reply->component_revision_level;
+	}
+}
+
+static int leapraid_transport_exp_report_manu(struct leapraid_adapter *adapter,
+					      u64 sas_address,
+					      struct sas_expander_device *edev,
+					      u8 port_id)
+{
+	struct leapraid_smp_passthrough_req *smp_passthrough_req;
+	struct leapraid_rep_manu_request *rep_manu_request;
+	dma_addr_t h2c_dma_addr;
+	dma_addr_t c2h_dma_addr;
+	bool issue_reset = false;
+	void *data_out = NULL;
+	u16 inter_taskid;
+	size_t c2h_size;
+	size_t h2c_size;
+	void *psge;
+	int rc;
+
+	if (adapter->access_ctrl.shost_recovering ||
+	    adapter->access_ctrl.pcie_recovering) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Failed, shost_recovering=%d pcie_recovering=%d\n",
+			 __func__,
+			 adapter->access_ctrl.shost_recovering,
+			 adapter->access_ctrl.pcie_recovering);
+		return -EFAULT;
+	}
+
+	mutex_lock(&adapter->driver_cmds.transport_cmd.mutex);
+	adapter->driver_cmds.transport_cmd.status = LEAPRAID_CMD_PENDING;
+	rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT,
+					  __func__);
+	if (rc)
+		goto out_cleanup;
+
+	h2c_size = sizeof(struct leapraid_rep_manu_request);
+	c2h_size = sizeof(struct leapraid_rep_manu_reply);
+	data_out = dma_alloc_coherent(&adapter->pdev->dev,
+				      h2c_size + c2h_size,
+				      &h2c_dma_addr,
+				      GFP_ATOMIC);
+	if (!data_out) {
+		rc = -ENOMEM;
+		goto out_cleanup;
+	}
+
+	rep_manu_request = data_out;
+	rep_manu_request->smp_frame_type =
+		SMP_REPORT_MANUFACTURER_INFORMATION_FRAME_TYPE;
+	rep_manu_request->function = SMP_REPORT_MANUFACTURER_INFORMATION_FUNC;
+	rep_manu_request->allocated_response_length = 0;
+	rep_manu_request->request_length = 0;
+
+	inter_taskid = adapter->driver_cmds.transport_cmd.inter_taskid;
+	smp_passthrough_req = leapraid_get_task_desc(adapter, inter_taskid);
+	memset(smp_passthrough_req, 0,
+	       sizeof(struct leapraid_smp_passthrough_req));
+	smp_passthrough_req->func = LEAPRAID_FUNC_SMP_PASSTHROUGH;
+	smp_passthrough_req->physical_port = port_id;
+	smp_passthrough_req->sas_address = cpu_to_le64(sas_address);
+	smp_passthrough_req->req_data_len = cpu_to_le16(h2c_size);
+	psge = &smp_passthrough_req->sgl;
+	c2h_dma_addr = h2c_dma_addr + sizeof(struct leapraid_rep_manu_request);
+	leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, h2c_size,
+			       c2h_dma_addr, c2h_size);
+
+	init_completion(&adapter->driver_cmds.transport_cmd.done);
+	leapraid_fire_task(adapter, inter_taskid);
+	wait_for_completion_timeout(&adapter->driver_cmds.transport_cmd.done,
+				    LEAPRAID_TRANSPORT_CMD_TIMEOUT * HZ);
+	if (!(adapter->driver_cmds.transport_cmd.status & LEAPRAID_CMD_DONE)) {
+		rc = -ETIMEDOUT;
+		dev_err(&adapter->pdev->dev,
+			"%s: SMP passthrough timeout, st=0x%x\n",
+			__func__, adapter->driver_cmds.transport_cmd.status);
+		leapraid_log_req_context(adapter, inter_taskid,
+					 smp_passthrough_req);
+		if (!(adapter->driver_cmds.transport_cmd.status &
+		      LEAPRAID_CMD_RESET))
+			issue_reset = true;
+
+		goto hard_reset;
+	}
+
+	if (adapter->driver_cmds.transport_cmd.status &
+	    LEAPRAID_CMD_REPLY_VALID)
+		leapraid_transport_exp_set_edev(adapter, data_out, edev);
+
+hard_reset:
+	if (issue_reset) {
+		dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n",
+			 __func__, __LINE__);
+		rc = leapraid_hard_reset_handler(adapter, FULL_RESET);
+	}
+out_cleanup:
+	adapter->driver_cmds.transport_cmd.status = LEAPRAID_CMD_NOT_USED;
+	if (data_out)
+		dma_free_coherent(&adapter->pdev->dev, h2c_size + c2h_size,
+				  data_out, h2c_dma_addr);
+
+	mutex_unlock(&adapter->driver_cmds.transport_cmd.mutex);
+	return rc;
+}
+
+static void leapraid_transport_del_port(struct leapraid_adapter *adapter,
+					struct leapraid_sas_port *sas_port)
+{
+	dev_info(&sas_port->port->dev,
+		 "Remove port: SAS addr=0x%016llx\n",
+		 (unsigned long long)sas_port->remote_identify.sas_address);
+	switch (sas_port->remote_identify.device_type) {
+	case SAS_END_DEVICE:
+		leapraid_sas_dev_remove_by_sas_address(
+			adapter,
+			sas_port->remote_identify.sas_address,
+			sas_port->card_port);
+		break;
+	case SAS_EDGE_EXPANDER_DEVICE:
+	case SAS_FANOUT_EXPANDER_DEVICE:
+		leapraid_exp_rm(adapter, sas_port->remote_identify.sas_address,
+				sas_port->card_port);
+		break;
+	default:
+		break;
+	}
+}
+
+static void leapraid_transport_del_phy(struct leapraid_adapter *adapter,
+				       struct leapraid_sas_port *sas_port,
+				       struct leapraid_card_phy *card_phy)
+{
+	dev_info(&card_phy->phy->dev,
+		 "Remove PHY: SAS addr=0x%016llx, phy=%d\n",
+		 (unsigned long long)sas_port->remote_identify.sas_address,
+		 card_phy->phy_id);
+	list_del(&card_phy->port_siblings);
+	sas_port->phys_num--;
+	sas_port_delete_phy(sas_port->port, card_phy->phy);
+	card_phy->phy_is_assigned = 0;
+}
+
+static void leapraid_transport_add_phy(struct leapraid_adapter *adapter,
+				       struct leapraid_sas_port *sas_port,
+				       struct leapraid_card_phy *card_phy)
+{
+	dev_info(&card_phy->phy->dev,
+		 "Add PHY: SAS addr=0x%016llx, phy=%d\n",
+		 (unsigned long long)sas_port->remote_identify.sas_address,
+		 card_phy->phy_id);
+	list_add_tail(&card_phy->port_siblings, &sas_port->phy_list);
+	sas_port->phys_num++;
+	sas_port_add_phy(sas_port->port, card_phy->phy);
+	card_phy->phy_is_assigned = 1;
+}
+
+void leapraid_transport_attach_phy_to_port(
+		struct leapraid_adapter *adapter,
+		struct leapraid_topo_node *topo_node,
+		struct leapraid_card_phy *card_phy,
+		u64 sas_address,
+		struct leapraid_card_port *card_port)
+{
+	struct leapraid_sas_port *sas_port;
+	struct leapraid_card_phy *card_phy_srch;
+
+	if (card_phy->phy_is_assigned)
+		return;
+
+	if (!card_port)
+		return;
+
+	list_for_each_entry(sas_port, &topo_node->sas_port_list, port_list) {
+		if (sas_port->remote_identify.sas_address != sas_address)
+			continue;
+
+		if (sas_port->card_port != card_port)
+			continue;
+
+		list_for_each_entry(card_phy_srch, &sas_port->phy_list,
+				    port_siblings) {
+			if (card_phy_srch == card_phy)
+				return;
+		}
+		leapraid_transport_add_phy(adapter, sas_port, card_phy);
+		return;
+	}
+}
+
+void leapraid_transport_detach_phy_to_port(
+		struct leapraid_adapter *adapter,
+		struct leapraid_topo_node *topo_node,
+		struct leapraid_card_phy *target_card_phy)
+{
+	struct leapraid_sas_port *sas_port, *sas_port_next;
+	struct leapraid_card_phy *cur_card_phy;
+
+	if (!target_card_phy->phy_is_assigned)
+		return;
+
+	list_for_each_entry_safe(sas_port, sas_port_next,
+				 &topo_node->sas_port_list, port_list) {
+		list_for_each_entry(cur_card_phy, &sas_port->phy_list,
+				    port_siblings) {
+			if (cur_card_phy != target_card_phy)
+				continue;
+
+			if (sas_port->phys_num == 1 &&
+			    !adapter->access_ctrl.shost_recovering)
+				leapraid_transport_del_port(adapter, sas_port);
+			else
+				leapraid_transport_del_phy(adapter, sas_port,
+							   target_card_phy);
+			return;
+		}
+	}
+}
+
+static void leapraid_detach_phy_from_old_port(
+		struct leapraid_adapter *adapter,
+		struct leapraid_topo_node *topo_node,
+		u64 sas_address,
+		struct leapraid_card_port *card_port)
+{
+	int i;
+
+	for (i = 0; i < topo_node->phys_num; i++) {
+		if (topo_node->card_phy[i].remote_identify.sas_address !=
+			sas_address ||
+		    topo_node->card_phy[i].card_port != card_port)
+			continue;
+		if (topo_node->card_phy[i].phy_is_assigned)
+			leapraid_transport_detach_phy_to_port(
+				adapter,
+				topo_node,
+				&topo_node->card_phy[i]);
+	}
+}
+
+static struct leapraid_sas_port *leapraid_prepare_sas_port(
+		struct leapraid_adapter *adapter,
+		u16 handle, u64 sas_address,
+		struct leapraid_card_port *card_port,
+		struct leapraid_topo_node **out_topo_node)
+{
+	struct leapraid_topo_node *topo_node;
+	struct leapraid_sas_port *sas_port;
+	unsigned long flags;
+
+	sas_port = kzalloc_obj(*sas_port);
+	if (!sas_port) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Failed alloc sas_port\n", __func__);
+		return NULL;
+	}
+
+	INIT_LIST_HEAD(&sas_port->port_list);
+	INIT_LIST_HEAD(&sas_port->phy_list);
+
+	spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+	topo_node = leapraid_transport_topo_node_by_sas_addr(adapter,
+							     sas_address,
+							     card_port);
+	spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags);
+
+	if (!topo_node) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Failed to locate parent for SAS 0x%016llx!\n",
+			__func__, sas_address);
+		goto out_cleanup;
+	}
+
+	if (leapraid_transport_set_identify(adapter, handle,
+					    &sas_port->remote_identify))
+		goto out_cleanup;
+
+	if (sas_port->remote_identify.device_type == SAS_PHY_UNUSED) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Failed, device type is SAS_PHY_UNUSED\n",
+			 __func__);
+		goto out_cleanup;
+	}
+
+	sas_port->card_port = card_port;
+	*out_topo_node = topo_node;
+
+	return sas_port;
+
+out_cleanup:
+	kfree(sas_port);
+	return NULL;
+}
+
+static int leapraid_bind_phys_and_vphy(struct leapraid_adapter *adapter,
+				       struct leapraid_sas_port *sas_port,
+				       struct leapraid_topo_node *topo_node,
+				       struct leapraid_card_port *card_port,
+				       struct leapraid_vphy **out_vphy)
+{
+	struct leapraid_vphy *vphy = NULL;
+	int i;
+
+	for (i = 0; i < topo_node->phys_num; i++) {
+		if (topo_node->card_phy[i].remote_identify.sas_address !=
+			sas_port->remote_identify.sas_address ||
+		    topo_node->card_phy[i].card_port != card_port)
+			continue;
+
+		list_add_tail(&topo_node->card_phy[i].port_siblings,
+			      &sas_port->phy_list);
+		sas_port->phys_num++;
+
+		if (topo_node->hdl <= adapter->dev_topo.card.phys_num) {
+			if (!topo_node->card_phy[i].vphy) {
+				card_port->phy_mask |= BIT(i);
+				continue;
+			}
+
+			vphy = leapraid_get_vphy_by_phy(card_port, i);
+			if (!vphy)
+				return LEAPRAID_OPERATION_FAILED;
+		}
+	}
+
+	*out_vphy = vphy;
+	return sas_port->phys_num ? 0 : LEAPRAID_OPERATION_FAILED;
+}
+
+static struct sas_rphy *leapraid_create_and_register_rphy(
+		struct leapraid_adapter *adapter,
+		struct leapraid_sas_port *sas_port,
+		struct leapraid_topo_node *topo_node,
+		struct leapraid_card_port *card_port,
+		struct leapraid_vphy *vphy)
+{
+	struct leapraid_sas_dev *sas_dev = NULL;
+	struct leapraid_card_phy *card_phy;
+	struct sas_port *port;
+	struct sas_rphy *rphy;
+
+	if (sas_port->remote_identify.device_type == SAS_END_DEVICE) {
+		sas_dev = leapraid_get_sas_dev_by_addr(
+				adapter,
+				sas_port->remote_identify.sas_address,
+				card_port);
+		if (!sas_dev)
+			return NULL;
+		sas_dev->pend_sas_rphy_add = 1;
+	}
+
+	if (!topo_node->parent_dev) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: topo_node parent device is NULL\n",  __func__);
+		goto cleanup_sas_dev;
+	}
+
+	port = sas_port_alloc_num(topo_node->parent_dev);
+	if (!port) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Failed to allocate SAS port\n", __func__);
+		goto cleanup_sas_dev;
+	}
+
+	if (sas_port_add(port)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Failed to add SAS port\n", __func__);
+		goto out_delete_port;
+	}
+
+	list_for_each_entry(card_phy, &sas_port->phy_list, port_siblings) {
+		sas_port_add_phy(port, card_phy->phy);
+		card_phy->phy_is_assigned = 1;
+		card_phy->card_port = card_port;
+	}
+
+	if (sas_dev) {
+		rphy = sas_end_device_alloc(port);
+		sas_dev->rphy = rphy;
+
+		if (topo_node->hdl <= adapter->dev_topo.card.phys_num) {
+			if (!vphy)
+				card_port->sas_address = sas_dev->sas_addr;
+			else
+				vphy->sas_address = sas_dev->sas_addr;
+		}
+	} else {
+		rphy = sas_expander_alloc(
+				port,
+				sas_port->remote_identify.device_type);
+		if (topo_node->hdl <= adapter->dev_topo.card.phys_num)
+			card_port->sas_address =
+				sas_port->remote_identify.sas_address;
+	}
+
+	if (!rphy) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Failed to allocate RPHY\n", __func__);
+		goto cleanup_card_phy;
+	}
+
+	rphy->identify = sas_port->remote_identify;
+
+	if (sas_rphy_add(rphy)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Failed to add RPHY\n", __func__);
+		if (sas_dev)
+			sas_dev->rphy = NULL;
+
+		sas_rphy_free(rphy);
+		goto cleanup_card_phy;
+	}
+	sas_port->port = port;
+
+	if (sas_dev) {
+		sas_dev->pend_sas_rphy_add = 0;
+		leapraid_sdev_put(sas_dev);
+	}
+
+	return rphy;
+
+cleanup_card_phy:
+	if (topo_node->hdl <= adapter->dev_topo.card.phys_num) {
+		if (!vphy)
+			card_port->sas_address = 0;
+		else
+			vphy->sas_address = 0;
+	}
+
+	list_for_each_entry(card_phy, &sas_port->phy_list, port_siblings) {
+		card_phy->phy_is_assigned = 0;
+		card_phy->card_port = NULL;
+	}
+
+out_delete_port:
+	sas_port_delete(port);
+
+cleanup_sas_dev:
+	if (sas_dev) {
+		sas_dev->pend_sas_rphy_add = 0;
+		leapraid_sdev_put(sas_dev);
+	}
+	return NULL;
+}
+
+struct leapraid_sas_port *leapraid_transport_port_add(
+		struct leapraid_adapter *adapter,
+		u16 hdl, u64 sas_address,
+		struct leapraid_card_port *card_port)
+{
+	struct leapraid_card_phy *card_phy, *card_phy_next;
+	struct leapraid_topo_node *topo_node = NULL;
+	struct leapraid_sas_port *sas_port;
+	struct leapraid_vphy *vphy = NULL;
+	struct sas_rphy *rphy;
+	unsigned long flags;
+
+	if (!card_port) {
+		dev_warn(&adapter->pdev->dev,
+			 "%s: Invalid card_port\n", __func__);
+		return NULL;
+	}
+
+	sas_port = leapraid_prepare_sas_port(adapter, hdl, sas_address,
+					     card_port, &topo_node);
+	if (!sas_port)
+		return NULL;
+
+	leapraid_detach_phy_from_old_port(
+		adapter,
+		topo_node,
+		sas_port->remote_identify.sas_address,
+		card_port);
+
+	if (leapraid_bind_phys_and_vphy(adapter, sas_port, topo_node,
+					card_port, &vphy)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Failed to bind phy to vphy\n", __func__);
+		goto out_fail;
+	}
+
+	rphy = leapraid_create_and_register_rphy(adapter, sas_port, topo_node,
+						 card_port, vphy);
+	if (!rphy) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Failed to create rphy\n", __func__);
+		goto out_fail;
+	}
+
+	dev_info(&rphy->dev,
+		 "%s: Added dev: hdl=0x%04x, SAS addr=0x%016llx\n",
+		 __func__, hdl,
+		 (unsigned long long)sas_port->remote_identify.sas_address);
+
+	sas_port->rphy = rphy;
+
+	spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+	list_add_tail(&sas_port->port_list, &topo_node->sas_port_list);
+	spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags);
+
+	if (sas_port->remote_identify.device_type ==
+	    SAS_EDGE_EXPANDER_DEVICE ||
+	    sas_port->remote_identify.device_type ==
+	    SAS_FANOUT_EXPANDER_DEVICE)
+		leapraid_transport_exp_report_manu(
+			adapter,
+			sas_port->remote_identify.sas_address,
+			rphy_to_expander_device(rphy),
+			card_port->port_id);
+
+	return sas_port;
+
+out_fail:
+	list_for_each_entry_safe(card_phy, card_phy_next,
+				 &sas_port->phy_list, port_siblings) {
+		if (topo_node->hdl <= adapter->dev_topo.card.phys_num &&
+		    !card_phy->vphy)
+			card_port->phy_mask &= ~BIT(card_phy->phy_id);
+
+		list_del_init(&card_phy->port_siblings);
+	}
+
+	kfree(sas_port);
+	return NULL;
+}
+
+static struct leapraid_sas_port *leapraid_find_and_remove_sas_port(
+		struct leapraid_topo_node *topo_node,
+		u64 sas_address,
+		struct leapraid_card_port *remove_card_port,
+		bool *found)
+{
+	struct leapraid_sas_port *sas_port, *sas_port_next;
+
+	list_for_each_entry_safe(sas_port, sas_port_next,
+				 &topo_node->sas_port_list, port_list) {
+		if (sas_port->remote_identify.sas_address != sas_address)
+			continue;
+
+		if (sas_port->card_port != remove_card_port)
+			continue;
+
+		*found = true;
+		list_del(&sas_port->port_list);
+		return sas_port;
+	}
+	return NULL;
+}
+
+static void leapraid_cleanup_card_port_and_vphys(
+		struct leapraid_adapter *adapter,
+		u64 sas_address,
+		struct leapraid_card_port *remove_card_port)
+{
+	struct leapraid_card_port *card_port, *card_port_next;
+	struct leapraid_vphy *vphy, *vphy_next;
+
+	if (remove_card_port->vphys_mask) {
+		list_for_each_entry_safe(vphy, vphy_next,
+					 &remove_card_port->vphys_list, list) {
+			if (vphy->sas_address != sas_address)
+				continue;
+
+			dev_dbg(&adapter->pdev->dev,
+				"%s: Remove vphy=%p from port=%p port_id=%d\n",
+				__func__, vphy, remove_card_port,
+				remove_card_port->port_id);
+
+			remove_card_port->vphys_mask &= ~vphy->phy_mask;
+			list_del(&vphy->list);
+			kfree(vphy);
+		}
+
+		if (!remove_card_port->vphys_mask &&
+		    !remove_card_port->sas_address) {
+			dev_dbg(&adapter->pdev->dev,
+				"%s: Remove empty hba_port: %p, port_id=%d\n",
+				__func__,
+				remove_card_port,
+				remove_card_port->port_id);
+			list_del(&remove_card_port->list);
+			kfree(remove_card_port);
+			remove_card_port = NULL;
+		}
+	}
+
+	list_for_each_entry_safe(card_port, card_port_next,
+				 &adapter->dev_topo.card_port_list, list) {
+		if (card_port != remove_card_port)
+			continue;
+
+		if (card_port->sas_address != sas_address)
+			continue;
+
+		if (!remove_card_port->vphys_mask) {
+			dev_dbg(&adapter->pdev->dev,
+				"%s: Remove hba_port: %p, port_id=%d\n",
+				__func__, card_port, card_port->port_id);
+			list_del(&card_port->list);
+			kfree(card_port);
+		} else {
+			dev_dbg(&adapter->pdev->dev,
+				"Clear sas_address of port=%p, port_id=%d\n",
+				card_port, card_port->port_id);
+			remove_card_port->sas_address = 0;
+		}
+		break;
+	}
+}
+
+static void leapraid_clear_topo_node_phys(struct leapraid_topo_node *topo_node,
+					  u64 sas_address)
+{
+	int i;
+
+	for (i = 0; i < topo_node->phys_num; i++)
+		if (topo_node->card_phy[i].remote_identify.sas_address ==
+		    sas_address) {
+			memset(&topo_node->card_phy[i].remote_identify, 0,
+			       sizeof(struct sas_identify));
+			topo_node->card_phy[i].vphy = 0;
+		}
+}
+
+void leapraid_transport_port_remove(
+		struct leapraid_adapter *adapter,
+		u64 sas_address,
+		u64 sas_address_parent,
+		struct leapraid_card_port *remove_card_port)
+{
+	struct leapraid_card_phy *card_phy, *card_phy_next;
+	struct leapraid_sas_port *sas_port;
+	struct leapraid_topo_node *topo_node;
+	unsigned long flags;
+	bool found = false;
+
+	if (!remove_card_port)
+		return;
+
+	spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+
+	topo_node = leapraid_transport_topo_node_by_sas_addr(adapter,
+							     sas_address_parent,
+							     remove_card_port);
+	if (!topo_node) {
+		spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock,
+				       flags);
+		return;
+	}
+
+	sas_port = leapraid_find_and_remove_sas_port(topo_node, sas_address,
+						     remove_card_port, &found);
+
+	if (!found) {
+		spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock,
+				       flags);
+		return;
+	}
+
+	if (topo_node->hdl <= adapter->dev_topo.card.phys_num &&
+	    adapter->adapter_attr.enable_mp)
+		leapraid_cleanup_card_port_and_vphys(adapter, sas_address,
+						     remove_card_port);
+
+	leapraid_clear_topo_node_phys(topo_node, sas_address);
+	spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags);
+
+	list_for_each_entry_safe(card_phy, card_phy_next,
+				 &sas_port->phy_list, port_siblings) {
+		card_phy->phy_is_assigned = 0;
+		if (!adapter->access_ctrl.host_removing)
+			sas_port_delete_phy(sas_port->port, card_phy->phy);
+
+		list_del(&card_phy->port_siblings);
+	}
+
+	if (!adapter->access_ctrl.host_removing)
+		sas_port_delete(sas_port->port);
+
+	dev_dbg(&adapter->pdev->dev,
+		"%s: Removed sas_port for SAS addr=0x%016llx\n",
+		__func__, (unsigned long long)sas_address);
+
+	kfree(sas_port);
+}
+
+static void leapraid_init_sas_or_exp_phy(struct leapraid_adapter *adapter,
+					 struct leapraid_card_phy *card_phy,
+					 struct sas_phy *phy,
+					 struct leapraid_sas_phy_p0 *phy_pg0,
+					 struct leapraid_exp_p1 *exp_pg1)
+{
+	if (exp_pg1 && phy_pg0)
+		return;
+
+	if (!exp_pg1 && !phy_pg0)
+		return;
+
+	phy->identify = card_phy->identify;
+	phy->identify.phy_identifier = card_phy->phy_id;
+	phy->negotiated_linkrate = phy_pg0 ?
+		leapraid_transport_convert_phy_link_rate(
+			phy_pg0->neg_link_rate &
+			LEAPRAID_SAS_NEG_LINK_RATE_MASK_PHYSICAL) :
+		 leapraid_transport_convert_phy_link_rate(
+			exp_pg1->neg_link_rate &
+			LEAPRAID_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
+	phy->minimum_linkrate_hw = phy_pg0 ?
+		 leapraid_transport_convert_phy_link_rate(
+			phy_pg0->hw_link_rate &
+			LEAPRAID_SAS_HWRATE_MIN_RATE_MASK) :
+		 leapraid_transport_convert_phy_link_rate(
+			exp_pg1->hw_link_rate &
+			LEAPRAID_SAS_HWRATE_MIN_RATE_MASK);
+	phy->maximum_linkrate_hw = phy_pg0 ?
+		 leapraid_transport_convert_phy_link_rate(
+			phy_pg0->hw_link_rate >>
+			 LEAPRAID_SAS_NEG_LINK_RATE_SHIFT) :
+		 leapraid_transport_convert_phy_link_rate(
+			exp_pg1->hw_link_rate >>
+			 LEAPRAID_SAS_NEG_LINK_RATE_SHIFT);
+	phy->minimum_linkrate = phy_pg0 ?
+		 leapraid_transport_convert_phy_link_rate(
+			phy_pg0->p_link_rate &
+			LEAPRAID_SAS_PRATE_MIN_RATE_MASK) :
+		 leapraid_transport_convert_phy_link_rate(
+			exp_pg1->p_link_rate &
+			LEAPRAID_SAS_PRATE_MIN_RATE_MASK);
+	phy->maximum_linkrate = phy_pg0 ?
+		 leapraid_transport_convert_phy_link_rate(
+			phy_pg0->p_link_rate >>
+			 LEAPRAID_SAS_NEG_LINK_RATE_SHIFT) :
+		 leapraid_transport_convert_phy_link_rate(
+			exp_pg1->p_link_rate >>
+			 LEAPRAID_SAS_NEG_LINK_RATE_SHIFT);
+	phy->hostdata = card_phy->card_port;
+}
+
+void leapraid_transport_add_card_phy(struct leapraid_adapter *adapter,
+				     struct leapraid_card_phy *card_phy,
+				     struct leapraid_sas_phy_p0 *phy_pg0,
+				     struct device *parent_dev)
+{
+	struct sas_phy *phy;
+	int ret;
+
+	INIT_LIST_HEAD(&card_phy->port_siblings);
+	phy = sas_phy_alloc(parent_dev, card_phy->phy_id);
+	if (!phy) {
+		dev_err(&adapter->pdev->dev,
+			"%s: sas_phy_alloc failed!\n", __func__);
+		return;
+	}
+
+	if (leapraid_transport_set_identify(adapter, card_phy->hdl,
+					    &card_phy->identify)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Set PHY handle identify failed!\n", __func__);
+		sas_phy_free(phy);
+		return;
+	}
+
+	card_phy->attached_hdl = le16_to_cpu(phy_pg0->attached_dev_hdl);
+	if (card_phy->attached_hdl) {
+		ret = leapraid_transport_set_identify(
+				adapter,
+				card_phy->attached_hdl,
+				&card_phy->remote_identify);
+		if (ret) {
+			dev_err(&adapter->pdev->dev,
+				"%s: Set PHY attached hdl identify failed!\n",
+				__func__);
+			sas_phy_free(phy);
+			return;
+		}
+	}
+
+	leapraid_init_sas_or_exp_phy(adapter, card_phy, phy, phy_pg0, NULL);
+
+	if (sas_phy_add(phy)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: SAS PHY add failed!\n", __func__);
+		sas_phy_free(phy);
+		return;
+	}
+
+	card_phy->phy = phy;
+}
+
+int leapraid_transport_add_exp_phy(struct leapraid_adapter *adapter,
+				   struct leapraid_card_phy *card_phy,
+				   struct leapraid_exp_p1 *exp_pg1,
+				   struct device *parent_dev)
+{
+	struct sas_phy *phy;
+	int ret;
+
+	INIT_LIST_HEAD(&card_phy->port_siblings);
+	phy = sas_phy_alloc(parent_dev, card_phy->phy_id);
+	if (!phy) {
+		dev_err(&adapter->pdev->dev,
+			"%s: sas_phy_alloc failed!\n", __func__);
+		return -EFAULT;
+	}
+
+	if (leapraid_transport_set_identify(adapter, card_phy->hdl,
+					    &card_phy->identify)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Set PHY hdl identify failed!\n", __func__);
+		sas_phy_free(phy);
+		return -EFAULT;
+	}
+
+	card_phy->attached_hdl = le16_to_cpu(exp_pg1->attached_dev_hdl);
+	if (card_phy->attached_hdl) {
+		ret = leapraid_transport_set_identify(
+				adapter,
+				card_phy->attached_hdl,
+				&card_phy->remote_identify);
+		if (ret)
+			dev_warn(&adapter->pdev->dev,
+				 "%s: Set PHY attached hdl identify failed!\n",
+				 __func__);
+	}
+
+	leapraid_init_sas_or_exp_phy(adapter, card_phy, phy, NULL, exp_pg1);
+
+	if (sas_phy_add(phy)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: SAS PHY add failed!\n", __func__);
+		sas_phy_free(phy);
+		return -EFAULT;
+	}
+
+	card_phy->phy = phy;
+	return 0;
+}
+
+void leapraid_transport_update_links(
+		struct leapraid_adapter *adapter,
+		u64 sas_address, u16 hdl, u8 phy_index,
+		u8 link_rate, struct leapraid_card_port *target_card_port)
+{
+	struct leapraid_topo_node *topo_node;
+	struct leapraid_card_phy *card_phy;
+	struct leapraid_card_port *card_port;
+	unsigned long flags;
+
+	if (adapter->access_ctrl.shost_recovering ||
+	    adapter->access_ctrl.pcie_recovering)
+		return;
+
+	spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags);
+	topo_node = leapraid_transport_topo_node_by_sas_addr(adapter,
+							     sas_address,
+							     target_card_port);
+	if (!topo_node) {
+		spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock,
+				       flags);
+		return;
+	}
+
+	card_phy = &topo_node->card_phy[phy_index];
+	card_phy->attached_hdl = hdl;
+	spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags);
+
+	if (hdl && link_rate >= LEAPRAID_SAS_NEG_LINK_RATE_1_5) {
+		leapraid_transport_set_identify(adapter, hdl,
+						&card_phy->remote_identify);
+		if (topo_node->hdl <= adapter->dev_topo.card.phys_num &&
+		    adapter->adapter_attr.enable_mp) {
+			list_for_each_entry(card_port,
+					    &adapter->dev_topo.card_port_list,
+					    list) {
+				if (card_port->sas_address == sas_address &&
+				    card_port == target_card_port)
+					card_port->phy_mask |=
+						BIT(card_phy->phy_id);
+			}
+		}
+		leapraid_transport_attach_phy_to_port(
+			adapter,
+			topo_node,
+			card_phy,
+			card_phy->remote_identify.sas_address,
+			target_card_port);
+	} else {
+		memset(&card_phy->remote_identify, 0,
+		       sizeof(struct sas_identify));
+	}
+
+	if (card_phy->phy)
+		card_phy->phy->negotiated_linkrate =
+			leapraid_transport_convert_phy_link_rate(link_rate);
+}
+
+static int leapraid_dma_map_buffer(struct device *dev, struct bsg_buffer *buf,
+				   dma_addr_t *dma_addr,
+				   size_t *dma_len, void **p)
+{
+	if (buf->sg_cnt > 1) {
+		*p = dma_alloc_coherent(dev, buf->payload_len, dma_addr,
+					GFP_KERNEL);
+		if (!*p)
+			return -ENOMEM;
+
+		*dma_len = buf->payload_len;
+	} else {
+		if (!dma_map_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL)) {
+			dev_err(dev,
+				"%s: Failed to map sg buffer for dma\n",
+				__func__);
+			return -ENOMEM;
+		}
+
+		*dma_addr = sg_dma_address(buf->sg_list);
+		*dma_len = sg_dma_len(buf->sg_list);
+		*p = NULL;
+	}
+	return 0;
+}
+
+static void leapraid_dma_unmap_buffer(struct device *dev,
+				      struct bsg_buffer *buf,
+				      dma_addr_t dma_addr,
+				      void *p)
+{
+	if (p)
+		dma_free_coherent(dev, buf->payload_len, p, dma_addr);
+	else
+		dma_unmap_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL);
+}
+
+static void leapraid_build_smp_task(struct leapraid_adapter *adapter,
+				    struct sas_rphy *rphy,
+				    dma_addr_t h2c_dma_addr, size_t h2c_size,
+				    dma_addr_t c2h_dma_addr, size_t c2h_size)
+{
+	struct leapraid_smp_passthrough_req *smp_passthrough_req;
+	u16 inter_taskid;
+	void *psge;
+
+	inter_taskid = adapter->driver_cmds.transport_cmd.inter_taskid;
+	smp_passthrough_req = leapraid_get_task_desc(adapter, inter_taskid);
+	memset(smp_passthrough_req, 0, sizeof(*smp_passthrough_req));
+
+	smp_passthrough_req->func = LEAPRAID_FUNC_SMP_PASSTHROUGH;
+	smp_passthrough_req->physical_port =
+		leapraid_transport_get_port_id_by_rphy(adapter, rphy);
+	smp_passthrough_req->sas_address = (rphy) ?
+		cpu_to_le64(rphy->identify.sas_address) :
+		cpu_to_le64(adapter->dev_topo.card.sas_address);
+	smp_passthrough_req->req_data_len =
+		cpu_to_le16(h2c_size - LEAPRAID_SMP_FRAME_HEADER_SIZE);
+	psge = &smp_passthrough_req->sgl;
+	leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr,
+			       h2c_size - LEAPRAID_SMP_FRAME_HEADER_SIZE,
+			       c2h_dma_addr,
+			       c2h_size - LEAPRAID_SMP_FRAME_HEADER_SIZE);
+}
+
+static int leapraid_send_smp_req(struct leapraid_adapter *adapter)
+{
+	const struct leapraid_smp_passthrough_req *smp_passthrough_req;
+	u16 inter_taskid;
+
+	dev_dbg(&adapter->pdev->dev,
+		"%s: Sending smp request\n", __func__);
+	inter_taskid = adapter->driver_cmds.transport_cmd.inter_taskid;
+	smp_passthrough_req = leapraid_get_task_desc(adapter, inter_taskid);
+	init_completion(&adapter->driver_cmds.transport_cmd.done);
+	leapraid_fire_task(adapter, inter_taskid);
+	wait_for_completion_timeout(&adapter->driver_cmds.transport_cmd.done,
+				    LEAPRAID_TRANSPORT_CMD_TIMEOUT * HZ);
+	if (!(adapter->driver_cmds.transport_cmd.status & LEAPRAID_CMD_DONE)) {
+		dev_err(&adapter->pdev->dev, "%s: timeout, st=0x%x\n",
+			__func__, adapter->driver_cmds.transport_cmd.status);
+		leapraid_log_req_context(adapter, inter_taskid,
+					 smp_passthrough_req);
+		if (!(adapter->driver_cmds.transport_cmd.status &
+		      LEAPRAID_CMD_RESET)) {
+			dev_dbg(&adapter->pdev->dev,
+				"%s:%d: call hard_reset\n",
+				__func__, __LINE__);
+			leapraid_hard_reset_handler(adapter, FULL_RESET);
+			return -ETIMEDOUT;
+		}
+	}
+
+	dev_dbg(&adapter->pdev->dev, "%s: SMP request complete\n", __func__);
+	if (!(adapter->driver_cmds.transport_cmd.status &
+	      LEAPRAID_CMD_REPLY_VALID)) {
+		dev_err(&adapter->pdev->dev,
+			"%s: SMP request no reply\n", __func__);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void leapraid_handle_smp_rep(struct leapraid_adapter *adapter,
+				    struct bsg_job *job, void *addr_in,
+				    unsigned int *reslen)
+{
+	struct leapraid_smp_passthrough_rep *smp_passthrough_rep;
+
+	smp_passthrough_rep =
+		(void *)&adapter->driver_cmds.transport_cmd.reply;
+
+	dev_dbg(&adapter->pdev->dev, "%s: Response data len=%d\n",
+		__func__, le16_to_cpu(smp_passthrough_rep->resp_data_len));
+
+	memcpy(job->reply, smp_passthrough_rep, sizeof(*smp_passthrough_rep));
+	job->reply_len = sizeof(*smp_passthrough_rep);
+	*reslen = le16_to_cpu(smp_passthrough_rep->resp_data_len);
+
+	if (addr_in)
+		sg_copy_from_buffer(job->reply_payload.sg_list,
+				    job->reply_payload.sg_cnt, addr_in,
+				    job->reply_payload.payload_len);
+}
+
+static void leapraid_transport_smp_handler(struct bsg_job *job,
+					   struct Scsi_Host *shost,
+					   struct sas_rphy *rphy)
+{
+	struct leapraid_adapter *adapter = shost_priv(shost);
+	dma_addr_t c2h_dma_addr;
+	dma_addr_t h2c_dma_addr;
+	void *addr_in = NULL;
+	void *addr_out = NULL;
+	size_t c2h_size;
+	size_t h2c_size;
+	int rc;
+	unsigned int reslen = 0;
+
+	if (adapter->access_ctrl.shost_recovering ||
+	    adapter->access_ctrl.pcie_recovering) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Failed, shost_recovering=%d pcie_recovering=%d\n",
+			__func__,
+			adapter->access_ctrl.shost_recovering,
+			adapter->access_ctrl.pcie_recovering);
+		rc = -EFAULT;
+		goto exit_bsg_job;
+	}
+
+	rc = mutex_lock_interruptible(&adapter->driver_cmds
+				      .transport_cmd.mutex);
+	if (rc)
+		goto exit_bsg_job;
+
+	adapter->driver_cmds.transport_cmd.status = LEAPRAID_CMD_PENDING;
+	rc = leapraid_dma_map_buffer(&adapter->pdev->dev,
+				     &job->request_payload,
+				     &h2c_dma_addr, &h2c_size, &addr_out);
+	if (rc)
+		goto release_lock;
+
+	if (h2c_size < LEAPRAID_SMP_FRAME_HEADER_SIZE) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Invalid SMP request payload size=%zu\n",
+			__func__, h2c_size);
+		rc = -EINVAL;
+		goto free_req_buf;
+	}
+
+	if (addr_out)
+		sg_copy_to_buffer(job->request_payload.sg_list,
+				  job->request_payload.sg_cnt, addr_out,
+				  job->request_payload.payload_len);
+
+	rc = leapraid_dma_map_buffer(&adapter->pdev->dev, &job->reply_payload,
+				     &c2h_dma_addr, &c2h_size, &addr_in);
+	if (rc)
+		goto free_req_buf;
+
+	if (c2h_size < LEAPRAID_SMP_FRAME_HEADER_SIZE) {
+		dev_err(&adapter->pdev->dev,
+			"%s: Invalid SMP reply payload size=%zu\n",
+			__func__, c2h_size);
+		rc = -EINVAL;
+		goto free_rep_buf;
+	}
+
+	rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT,
+					  __func__);
+	if (rc)
+		goto free_rep_buf;
+
+	leapraid_build_smp_task(adapter, rphy, h2c_dma_addr,
+				h2c_size, c2h_dma_addr, c2h_size);
+
+	rc = leapraid_send_smp_req(adapter);
+	if (rc)
+		goto free_rep_buf;
+
+	leapraid_handle_smp_rep(adapter, job, addr_in, &reslen);
+
+free_rep_buf:
+	leapraid_dma_unmap_buffer(&adapter->pdev->dev, &job->reply_payload,
+				  c2h_dma_addr, addr_in);
+free_req_buf:
+	leapraid_dma_unmap_buffer(&adapter->pdev->dev, &job->request_payload,
+				  h2c_dma_addr, addr_out);
+release_lock:
+	adapter->driver_cmds.transport_cmd.status = LEAPRAID_CMD_NOT_USED;
+	mutex_unlock(&adapter->driver_cmds.transport_cmd.mutex);
+exit_bsg_job:
+	bsg_job_done(job, rc, reslen);
+}
+
+struct sas_function_template leapraid_transport_functions = {
+	.smp_handler = leapraid_transport_smp_handler,
+};
+
+struct scsi_transport_template *leapraid_transport_template;
diff --git a/drivers/scsi/mpt3sas/Makefile b/drivers/scsi/mpt3sas/Makefile
index e76d994dbed3..18e2d87eb4a2 100644
--- a/drivers/scsi/mpt3sas/Makefile
+++ b/drivers/scsi/mpt3sas/Makefile
@@ -9,3 +9,5 @@ mpt3sas-y +=  mpt3sas_base.o     \
 		mpt3sas_trigger_diag.o \
 		mpt3sas_warpdrive.o \
 		mpt3sas_debugfs.o \
+
+mpt3sas-$(CONFIG_HWMON) += mpt3sas_hwmon.o
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
index 79052f2accbd..791a3c5fbf44 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -8369,11 +8369,7 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc)
 		return r; /* scan_start and scan_finished support */
 	}
 
-	r = _base_send_port_enable(ioc);
-	if (r)
-		return r;
-
-	return r;
+	return _base_send_port_enable(ioc);
 }
 
 /**
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h
index d4597d058705..47255bf9cdda 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.h
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.h
@@ -1629,6 +1629,9 @@ struct MPT3SAS_ADAPTER {
 	u8		is_aero_ioc;
 	struct dentry	*debugfs_root;
 	struct dentry	*ioc_dump;
+#if IS_REACHABLE(CONFIG_HWMON)
+	struct mpt3sas_hwmon *hwmon;
+#endif
 	PUT_SMID_IO_FP_HIP put_smid_scsi_io;
 	PUT_SMID_IO_FP_HIP put_smid_fast_path;
 	PUT_SMID_IO_FP_HIP put_smid_hi_priority;
@@ -1904,6 +1907,9 @@ int mpt3sas_config_get_iounit_pg3(struct MPT3SAS_ADAPTER *ioc,
 	Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz);
 int mpt3sas_config_set_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
 	*mpi_reply, Mpi2IOUnitPage1_t *config_page);
+int mpt3sas_config_get_iounit_pg7(struct MPT3SAS_ADAPTER *ioc,
+				  Mpi2ConfigReply_t *mpi_reply,
+				  Mpi2IOUnitPage7_t *config_page);
 int mpt3sas_config_get_iounit_pg8(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t
 	*mpi_reply, Mpi2IOUnitPage8_t *config_page);
 int mpt3sas_config_get_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc,
@@ -2047,6 +2053,20 @@ void mpt3sas_destroy_debugfs(struct MPT3SAS_ADAPTER *ioc);
 void mpt3sas_init_debugfs(void);
 void mpt3sas_exit_debugfs(void);
 
+#if IS_REACHABLE(CONFIG_HWMON)
+int mpt3sas_hwmon_register(struct MPT3SAS_ADAPTER *ioc);
+void mpt3sas_hwmon_unregister(struct MPT3SAS_ADAPTER *ioc);
+#else
+static inline int mpt3sas_hwmon_register(struct MPT3SAS_ADAPTER *ioc)
+{
+	return 0;
+}
+
+static inline void mpt3sas_hwmon_unregister(struct MPT3SAS_ADAPTER *ioc)
+{
+}
+#endif
+
 /**
  * _scsih_is_pcie_scsi_device - determines if device is an pcie scsi device
  * @device_info: bitfield providing information about the device.
diff --git a/drivers/scsi/mpt3sas/mpt3sas_config.c b/drivers/scsi/mpt3sas/mpt3sas_config.c
index 45ac853e1289..b0d5ef893600 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_config.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_config.c
@@ -991,6 +991,42 @@ mpt3sas_config_get_iounit_pg3(struct MPT3SAS_ADAPTER *ioc,
 	return r;
 }
 
+/**
+ * mpt3sas_config_get_iounit_pg7 - obtain iounit page 7
+ * @ioc: per adapter object
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int mpt3sas_config_get_iounit_pg7(struct MPT3SAS_ADAPTER *ioc,
+				  Mpi2ConfigReply_t *mpi_reply,
+				  Mpi2IOUnitPage7_t *config_page)
+{
+	Mpi2ConfigRequest_t mpi_request;
+	int r;
+
+	memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+	mpi_request.Function = MPI2_FUNCTION_CONFIG;
+	mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+	mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
+	mpi_request.Header.PageNumber = 7;
+	mpi_request.Header.PageVersion = MPI2_IOUNITPAGE7_PAGEVERSION;
+	ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE);
+	r = _config_request(ioc, &mpi_request, mpi_reply,
+			    MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+	if (r)
+		goto out;
+
+	mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+	r = _config_request(ioc, &mpi_request, mpi_reply,
+			    MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT,
+			    config_page, sizeof(*config_page));
+ out:
+	return r;
+}
+
 /**
  * mpt3sas_config_get_iounit_pg8 - obtain iounit page 8
  * @ioc: per adapter object
diff --git a/drivers/scsi/mpt3sas/mpt3sas_hwmon.c b/drivers/scsi/mpt3sas/mpt3sas_hwmon.c
new file mode 100644
index 000000000000..297a8efecadb
--- /dev/null
+++ b/drivers/scsi/mpt3sas/mpt3sas_hwmon.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Hardware monitoring (hwmon) support for the LSI / Broadcom mpt3sas
+ * SAS HBA driver. Exposes the IOC and board temperature sensors by
+ * reading MPI IO Unit Page 7.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/slab.h>
+
+#include "mpt3sas_base.h"
+
+struct mpt3sas_hwmon {
+	struct MPT3SAS_ADAPTER *ioc;
+	struct device *hwmon_dev;
+	bool ioc_present;
+	bool board_present;
+};
+
+/*
+ * Convert a (raw, units) reading to millidegrees Celsius.
+ * Returns -ENODATA when the sensor reports "not present" or
+ * unknown units. Temperature values are interpreted as signed
+ * two's-complement integers.
+ *
+ * The MPI2_IOUNITPAGE7_IOC_TEMP_* and MPI2_IOUNITPAGE7_BOARD_TEMP_*
+ * defines in mpi2_cnfg.h share the same values; the IOC ones are
+ * used for both channels.
+ */
+static int _hwmon_to_mdegc(s16 raw, u8 units, long *out)
+{
+	switch (units) {
+	case MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS:
+		*out = (long)raw * 1000;
+		return 0;
+	case MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT:
+		/* (F - 32) * 5 / 9, expressed in milli-units */
+		*out = ((long)raw - 32) * 5000 / 9;
+		return 0;
+	default:
+		return -ENODATA;
+	}
+}
+
+static umode_t _hwmon_is_visible(const void *drvdata,
+				 enum hwmon_sensor_types type,
+				 u32 attr, int channel)
+{
+	const struct mpt3sas_hwmon *h = drvdata;
+
+	if (type != hwmon_temp)
+		return 0;
+	if (attr != hwmon_temp_input && attr != hwmon_temp_label)
+		return 0;
+	if (channel == 0 && h->ioc_present)
+		return 0444;
+	if (channel == 1 && h->board_present)
+		return 0444;
+	return 0;
+}
+
+static int _hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+		       u32 attr, int channel, long *val)
+{
+	struct mpt3sas_hwmon *h = dev_get_drvdata(dev);
+	Mpi2ConfigReply_t mpi_reply;
+	Mpi2IOUnitPage7_t page;
+	int r;
+
+	if (type != hwmon_temp || attr != hwmon_temp_input)
+		return -EOPNOTSUPP;
+
+	r = mpt3sas_config_get_iounit_pg7(h->ioc, &mpi_reply, &page);
+	if (r)
+		return r;
+
+	if (channel == 0)
+		return _hwmon_to_mdegc((s16)le16_to_cpu(page.IOCTemperature),
+				       page.IOCTemperatureUnits, val);
+	if (channel == 1)
+		return _hwmon_to_mdegc((s16)le16_to_cpu(page.BoardTemperature),
+				       page.BoardTemperatureUnits, val);
+	return -EOPNOTSUPP;
+}
+
+static const char * const mpt3sas_hwmon_temp_labels[] = {
+	"IOC",
+	"Board",
+};
+
+static int _hwmon_read_string(struct device *dev,
+			      enum hwmon_sensor_types type,
+			      u32 attr, int channel, const char **str)
+{
+	if (type != hwmon_temp || attr != hwmon_temp_label)
+		return -EOPNOTSUPP;
+	*str = mpt3sas_hwmon_temp_labels[channel];
+	return 0;
+}
+
+static const struct hwmon_channel_info * const mpt3sas_hwmon_info[] = {
+	HWMON_CHANNEL_INFO(temp,
+			   HWMON_T_INPUT | HWMON_T_LABEL,
+			   HWMON_T_INPUT | HWMON_T_LABEL),
+	NULL,
+};
+
+static const struct hwmon_ops mpt3sas_hwmon_ops = {
+	.is_visible	= _hwmon_is_visible,
+	.read		= _hwmon_read,
+	.read_string	= _hwmon_read_string,
+};
+
+static const struct hwmon_chip_info mpt3sas_hwmon_chip_info = {
+	.ops	= &mpt3sas_hwmon_ops,
+	.info	= mpt3sas_hwmon_info,
+};
+
+/**
+ * mpt3sas_hwmon_register - register an hwmon device for the IOC
+ * @ioc: per adapter object
+ * Context: sleep.
+ *
+ * Succeeds without registering when no temperature sensors are present,
+ * so cards without thermal monitoring do not expose an empty hwmon node.
+ * Paired with mpt3sas_hwmon_unregister() from the driver's remove path.
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+int mpt3sas_hwmon_register(struct MPT3SAS_ADAPTER *ioc)
+{
+	struct device *parent = &ioc->pdev->dev;
+	struct mpt3sas_hwmon *h;
+	struct device *hwdev;
+	Mpi2ConfigReply_t mpi_reply;
+	Mpi2IOUnitPage7_t page;
+	int r;
+
+	r = mpt3sas_config_get_iounit_pg7(ioc, &mpi_reply, &page);
+	if (r)
+		return r;
+
+	/*
+	 * A page where both *TemperatureUnits are NOT_PRESENT covers
+	 * two cases: cards that genuinely lack sensors, and firmware
+	 * errors that left the page zero-filled (the accessor mirrors
+	 * _config_request() behaviour). Either way: skip registration.
+	 */
+	if (page.IOCTemperatureUnits == MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT &&
+	    page.BoardTemperatureUnits == MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT)
+		return 0;
+
+	h = kzalloc_obj(*h);
+	if (!h)
+		return -ENOMEM;
+
+	h->ioc = ioc;
+	h->ioc_present = page.IOCTemperatureUnits != MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT;
+	h->board_present = page.BoardTemperatureUnits != MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT;
+
+	hwdev = hwmon_device_register_with_info(parent, "mpt3sas", h,
+						&mpt3sas_hwmon_chip_info,
+						NULL);
+	if (IS_ERR(hwdev)) {
+		kfree(h);
+		return PTR_ERR(hwdev);
+	}
+
+	h->hwmon_dev = hwdev;
+	ioc->hwmon = h;
+	return 0;
+}
+
+/**
+ * mpt3sas_hwmon_unregister - tear down the hwmon device, if any
+ * @ioc: per adapter object
+ *
+ * Safe to call when registration was skipped (no sensors) or
+ * failed; in those cases ioc->hwmon is NULL and this is a no-op.
+ */
+void mpt3sas_hwmon_unregister(struct MPT3SAS_ADAPTER *ioc)
+{
+	struct mpt3sas_hwmon *h = ioc->hwmon;
+
+	if (!h)
+		return;
+	hwmon_device_unregister(h->hwmon_dev);
+	kfree(h);
+	ioc->hwmon = NULL;
+}
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index 12caffeed3a0..dea78688cc9b 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -12562,6 +12562,7 @@ static void scsih_remove(struct pci_dev *pdev)
 	/* release all the volumes */
 	_scsih_ir_shutdown(ioc);
 	mpt3sas_destroy_debugfs(ioc);
+	mpt3sas_hwmon_unregister(ioc);
 	sas_remove_host(shost);
 	list_for_each_entry_safe(raid_device, next, &ioc->raid_device_list,
 	    list) {
@@ -13651,6 +13652,11 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	}
 
 	scsi_scan_host(shost);
+
+	if (mpt3sas_hwmon_register(ioc))
+		ioc_warn(ioc,
+			 "hwmon registration failed; temperatures not exposed\n");
+
 	mpt3sas_setup_debugfs(ioc);
 	return 0;
 out_add_shost_fail:
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 6a05ce195aa0..a4ca22024ede 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -580,7 +580,6 @@ qla2x00_sysfs_read_vpd(struct file *filp, struct kobject *kobj,
 	ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);
 	mutex_unlock(&ha->optrom_mutex);
 
-	ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size);
 skip:
 	return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size);
 }
@@ -816,7 +815,9 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj,
 			    "Unable to allocate memory for VPD information update.\n");
 			return -ENOMEM;
 		}
+		mutex_lock(&ha->optrom_mutex);
 		ha->isp_ops->get_flash_version(vha, tmp_data);
+		mutex_unlock(&ha->optrom_mutex);
 		vfree(tmp_data);
 		break;
 	}
@@ -1472,6 +1473,9 @@ qla2x00_optrom_gold_fw_version_show(struct device *dev,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
+	if (IS_QLA29XX(ha))
+		return -EPERM;
+
 	if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
 	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
@@ -1500,6 +1504,9 @@ qla24xx_84xx_fw_version_show(struct device *dev,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
+	if (IS_QLA29XX(ha))
+		return -EPERM;
+
 	if (!IS_QLA84XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
@@ -1521,7 +1528,7 @@ qla2x00_serdes_version_show(struct device *dev, struct device_attribute *attr,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
 	return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n",
@@ -1537,7 +1544,7 @@ qla2x00_mpi_version_show(struct device *dev, struct device_attribute *attr,
 	struct qla_hw_data *ha = vha->hw;
 
 	if (!IS_QLA81XX(ha) && !IS_QLA8031(ha) && !IS_QLA8044(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
 	return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%x)\n",
@@ -1566,6 +1573,9 @@ qla2x00_flash_block_size_show(struct device *dev,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
+	if (IS_QLA29XX(ha))
+		return -EPERM;
+
 	return scnprintf(buf, PAGE_SIZE, "0x%x\n", ha->fdt_block_size);
 }
 
@@ -1574,6 +1584,10 @@ qla2x00_vlan_id_show(struct device *dev, struct device_attribute *attr,
     char *buf)
 {
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+	struct qla_hw_data *ha = vha->hw;
+
+	if (IS_QLA29XX(ha))
+		return -EPERM;
 
 	if (!IS_CNA_CAPABLE(vha->hw))
 		return scnprintf(buf, PAGE_SIZE, "\n");
@@ -1586,6 +1600,10 @@ qla2x00_vn_port_mac_address_show(struct device *dev,
     struct device_attribute *attr, char *buf)
 {
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+	struct qla_hw_data *ha = vha->hw;
+
+	if (IS_QLA29XX(ha))
+		return -EPERM;
 
 	if (!IS_CNA_CAPABLE(vha->hw))
 		return scnprintf(buf, PAGE_SIZE, "\n");
@@ -1660,10 +1678,8 @@ qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr,
 	rval = qla2x00_get_firmware_state(vha, state);
 	mutex_unlock(&vha->hw->optrom_mutex);
 out:
-	if (rval != QLA_SUCCESS) {
+	if (rval != QLA_SUCCESS)
 		memset(state, -1, sizeof(state));
-		rval = qla2x00_get_firmware_state(vha, state);
-	}
 
 	return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
 	    state[0], state[1], state[2], state[3], state[4], state[5]);
@@ -1717,6 +1733,10 @@ qla2x00_allow_cna_fw_dump_show(struct device *dev,
 	struct device_attribute *attr, char *buf)
 {
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+	struct qla_hw_data *ha = vha->hw;
+
+	if (IS_QLA29XX(ha))
+		return -EPERM;
 
 	if (!IS_P3P_TYPE(vha->hw))
 		return scnprintf(buf, PAGE_SIZE, "\n");
@@ -1750,7 +1770,7 @@ qla2x00_pep_version_show(struct device *dev, struct device_attribute *attr,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
 	return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n",
@@ -1764,10 +1784,11 @@ qla2x00_min_supported_speed_show(struct device *dev,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
 	return scnprintf(buf, PAGE_SIZE, "%s\n",
+	    ha->min_supported_speed == 7 ? "128Gps" :
 	    ha->min_supported_speed == 6 ? "64Gps" :
 	    ha->min_supported_speed == 5 ? "32Gps" :
 	    ha->min_supported_speed == 4 ? "16Gps" :
@@ -1783,10 +1804,11 @@ qla2x00_max_supported_speed_show(struct device *dev,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
 	return scnprintf(buf, PAGE_SIZE, "%s\n",
+	    ha->max_supported_speed  == 3 ? "128Gps" :
 	    ha->max_supported_speed  == 2 ? "64Gps" :
 	    ha->max_supported_speed  == 1 ? "32Gps" :
 	    ha->max_supported_speed  == 0 ? "16Gps" : "unknown");
@@ -1802,7 +1824,7 @@ qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr,
 	int mode = QLA_SET_DATA_RATE_LR;
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) {
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) {
 		ql_log(ql_log_warn, vha, 0x70d8,
 		    "Speed setting not supported \n");
 		return -EINVAL;
@@ -1813,7 +1835,7 @@ qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr,
 		return rval;
 	speed = type;
 	if (type == 40 || type == 80 || type == 160 ||
-	    type == 320) {
+	    type == 320 || type == 640 || type == 1280) {
 		ql_dbg(ql_dbg_user, vha, 0x70d9,
 		    "Setting will be affected after a loss of sync\n");
 		type = type/10;
@@ -1838,6 +1860,12 @@ qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr,
 	case 32:
 		ha->set_data_rate = PORT_SPEED_32GB;
 		break;
+	case 64:
+		ha->set_data_rate = PORT_SPEED_64GB;
+		break;
+	case 128:
+		ha->set_data_rate = PORT_SPEED_128GB;
+		break;
 	default:
 		ql_log(ql_log_warn, vha, 0x1199,
 		    "Unrecognized speed setting:%lx. Setting Autoneg\n",
@@ -1867,6 +1895,7 @@ static const struct {
 	{ PORT_SPEED_16GB, "16" },
 	{ PORT_SPEED_32GB, "32" },
 	{ PORT_SPEED_64GB, "64" },
+	{ PORT_SPEED_128GB, "128" },
 	{ PORT_SPEED_10GB, "10" },
 };
 
@@ -2366,7 +2395,7 @@ qla2x00_fw_attr_show(struct device *dev,
 	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return scnprintf(buf, PAGE_SIZE, "\n");
 
 	return scnprintf(buf, PAGE_SIZE, "%llx\n",
@@ -2413,7 +2442,7 @@ qla2x00_mpi_fw_state_show(struct device *dev, struct device_attribute *attr,
 	u16 mpi_state;
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!(IS_QLA27XX(ha) || IS_QLA28XX(ha)))
+	if (!(IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)))
 		return scnprintf(buf, PAGE_SIZE,
 				"MPI state reporting is not supported for this HBA.\n");
 
@@ -2648,6 +2677,9 @@ qla2x00_get_host_speed(struct Scsi_Host *shost)
 	case PORT_SPEED_64GB:
 		speed = FC_PORTSPEED_64GBIT;
 		break;
+	case PORT_SPEED_128GB:
+		speed = FC_PORTSPEED_128GBIT;
+		break;
 	default:
 		speed = FC_PORTSPEED_UNKNOWN;
 		break;
@@ -2964,7 +2996,8 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
 		p->error_frames =
 		    le32_to_cpu(stats->dropped_frames) +
 		    le32_to_cpu(stats->discarded_frames);
-		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+		    IS_QLA29XX(ha)) {
 			p->rx_words = le64_to_cpu(stats->fpm_recv_word_cnt);
 			p->tx_words = le64_to_cpu(stats->fpm_xmit_word_cnt);
 		} else {
@@ -3409,6 +3442,8 @@ qla2x00_get_host_supported_speeds(scsi_qla_host_t *vha, uint speeds)
 {
 	uint supported_speeds = FC_PORTSPEED_UNKNOWN;
 
+	if (speeds & FDMI_PORT_SPEED_128GB)
+		supported_speeds |= FC_PORTSPEED_128GBIT;
 	if (speeds & FDMI_PORT_SPEED_64GB)
 		supported_speeds |= FC_PORTSPEED_64GBIT;
 	if (speeds & FDMI_PORT_SPEED_32GB)
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 5e910b5ca670..ab559048dbb8 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -160,6 +160,12 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct bsg_job *bsg_job)
 		goto exit_fcp_prio_cfg;
 	}
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t)) {
+		ret = -EINVAL;
+		goto exit_fcp_prio_cfg;
+	}
+
 	/* Get the sub command */
 	oper = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
 
@@ -758,6 +764,10 @@ qla2x00_process_loopback(struct bsg_job *bsg_job)
 		return -EIO;
 	}
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 3 * sizeof(uint32_t))
+		return -EINVAL;
+
 	memset(&elreq, 0, sizeof(elreq));
 
 	elreq.req_sg_cnt = dma_map_sg(&ha->pdev->dev,
@@ -990,6 +1000,10 @@ qla84xx_reset(struct bsg_job *bsg_job)
 		return -EINVAL;
 	}
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
+		return -EINVAL;
+
 	flag = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
 
 	rval = qla84xx_reset_chip(vha, flag == A84_ISSUE_RESET_DIAG_FW);
@@ -1034,6 +1048,10 @@ qla84xx_updatefw(struct bsg_job *bsg_job)
 		return -EINVAL;
 	}
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
+		return -EINVAL;
+
 	sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
 		bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
 	if (!sg_cnt) {
@@ -1407,23 +1425,14 @@ qla24xx_iidma(struct bsg_job *bsg_job)
 
 static int
 qla2x00_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha,
-	uint8_t is_update)
+	uint32_t start, uint8_t is_update)
 {
-	struct fc_bsg_request *bsg_request = bsg_job->request;
-	uint32_t start = 0;
 	int valid = 0;
 	struct qla_hw_data *ha = vha->hw;
 
 	if (unlikely(pci_channel_offline(ha->pdev)))
 		return -EINVAL;
 
-	start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
-	if (start > ha->optrom_size) {
-		ql_log(ql_log_warn, vha, 0x7055,
-		    "start %d > optrom_size %d.\n", start, ha->optrom_size);
-		return -EINVAL;
-	}
-
 	if (ha->optrom_state != QLA_SWAITING) {
 		ql_log(ql_log_info, vha, 0x7056,
 		    "optrom_state %d.\n", ha->optrom_state);
@@ -1431,42 +1440,79 @@ qla2x00_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha,
 	}
 
 	ha->optrom_region_start = start;
-	ql_dbg(ql_dbg_user, vha, 0x7057, "is_update=%d.\n", is_update);
-	if (is_update) {
-		if (ha->optrom_size == OPTROM_SIZE_2300 && start == 0)
-			valid = 1;
-		else if (start == (ha->flt_region_boot * 4) ||
-		    start == (ha->flt_region_fw * 4))
-			valid = 1;
-		else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) ||
-		    IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
-		    IS_QLA28XX(ha))
-			valid = 1;
-		if (!valid) {
-			ql_log(ql_log_warn, vha, 0x7058,
-			    "Invalid start region 0x%x/0x%x.\n", start,
-			    bsg_job->request_payload.payload_len);
+
+	if (IS_QLA29XX(ha)) {
+		if (start > ha->optrom_size) {
+			ql_log(ql_log_warn, vha, 0x7055,
+			    "start %d > optrom_size %d.\n", start,
+			    ha->optrom_size);
 			return -EINVAL;
 		}
 
-		ha->optrom_region_size = start +
-		    bsg_job->request_payload.payload_len > ha->optrom_size ?
-		    ha->optrom_size - start :
-		    bsg_job->request_payload.payload_len;
-		ha->optrom_state = QLA_SWRITING;
+		if (is_update) {
+			ha->optrom_region_size = start +
+			    bsg_job->request_payload.payload_len >
+			    ha->optrom_size ?
+			    ha->optrom_size - start :
+			    bsg_job->request_payload.payload_len;
+			ha->optrom_state = QLA_SWRITING;
+		} else {
+			ha->optrom_region_size = start +
+			    bsg_job->reply_payload.payload_len >
+			    ha->optrom_size ?
+			    ha->optrom_size - start :
+			    bsg_job->reply_payload.payload_len;
+			ha->optrom_state = QLA_SREADING;
+		}
 	} else {
-		ha->optrom_region_size = start +
-		    bsg_job->reply_payload.payload_len > ha->optrom_size ?
-		    ha->optrom_size - start :
-		    bsg_job->reply_payload.payload_len;
-		ha->optrom_state = QLA_SREADING;
+		if (start > ha->optrom_size) {
+			ql_log(ql_log_warn, vha, 0x7055,
+			    "start %d > optrom_size %d.\n", start,
+			    ha->optrom_size);
+			return -EINVAL;
+		}
+
+		ql_dbg(ql_dbg_user, vha, 0x7057,
+		    "is_update=%d.\n", is_update);
+		if (is_update) {
+			if (ha->optrom_size == OPTROM_SIZE_2300 && start == 0)
+				valid = 1;
+			else if (start == (ha->flt_region_boot * 4) ||
+			    start == (ha->flt_region_fw * 4))
+				valid = 1;
+			else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) ||
+			    IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) ||
+			    IS_QLA27XX(ha) || IS_QLA28XX(ha))
+				valid = 1;
+			if (!valid) {
+				ql_log(ql_log_warn, vha, 0x7058,
+				    "Invalid start region 0x%x/0x%x.\n",
+				    start,
+				    bsg_job->request_payload.payload_len);
+				return -EINVAL;
+			}
+
+			ha->optrom_region_size = start +
+			    bsg_job->request_payload.payload_len >
+			    ha->optrom_size ?
+			    ha->optrom_size - start :
+			    bsg_job->request_payload.payload_len;
+			ha->optrom_state = QLA_SWRITING;
+		} else {
+			ha->optrom_region_size = start +
+			    bsg_job->reply_payload.payload_len >
+			    ha->optrom_size ?
+			    ha->optrom_size - start :
+			    bsg_job->reply_payload.payload_len;
+			ha->optrom_state = QLA_SREADING;
+		}
 	}
 
 	ha->optrom_buffer = vzalloc(ha->optrom_region_size);
 	if (!ha->optrom_buffer) {
 		ql_log(ql_log_warn, vha, 0x7059,
-		    "Read: Unable to allocate memory for optrom retrieval "
-		    "(%x)\n", ha->optrom_region_size);
+		    "%s: Unable to allocate memory for optrom retrieval (%x)\n",
+		    __func__, ha->optrom_region_size);
 
 		ha->optrom_state = QLA_SWAITING;
 		return -ENOMEM;
@@ -1478,17 +1524,31 @@ qla2x00_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha,
 static int
 qla2x00_read_optrom(struct bsg_job *bsg_job)
 {
+	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
+	uint32_t start;
 	int rval = 0;
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
+		return -EINVAL;
+
+	start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
+
 	if (ha->flags.nic_core_reset_hdlr_active)
 		return -EBUSY;
 
+	if (IS_QLA29XX(ha)) {
+		ql_log(ql_log_warn, vha, 0x7070,
+		    "Legacy optrom read not supported on 29xx.\n");
+		return -EINVAL;
+	}
+
 	mutex_lock(&ha->optrom_mutex);
-	rval = qla2x00_optrom_setup(bsg_job, vha, 0);
+	rval = qla2x00_optrom_setup(bsg_job, vha, start, 0);
 	if (rval) {
 		mutex_unlock(&ha->optrom_mutex);
 		return rval;
@@ -1515,14 +1575,22 @@ qla2x00_read_optrom(struct bsg_job *bsg_job)
 static int
 qla2x00_update_optrom(struct bsg_job *bsg_job)
 {
+	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
+	uint32_t start;
 	int rval = 0;
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
+		return -EINVAL;
+
+	start = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
+
 	mutex_lock(&ha->optrom_mutex);
-	rval = qla2x00_optrom_setup(bsg_job, vha, 1);
+	rval = qla2x00_optrom_setup(bsg_job, vha, start, 1);
 	if (rval) {
 		mutex_unlock(&ha->optrom_mutex);
 		return rval;
@@ -1554,6 +1622,379 @@ qla2x00_update_optrom(struct bsg_job *bsg_job)
 	return rval;
 }
 
+/**
+ * qla29xx_bsg_flash_block_read - Read flash block for QLA29XX.
+ * @bsg_job: BSG job structure.
+ *
+ * Returns 0 on success, error code on failure.
+ */
+static int qla29xx_bsg_flash_block_read(struct bsg_job *bsg_job)
+{
+	struct fc_bsg_request *bsg_req = bsg_job->request;
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
+	scsi_qla_host_t *vha = shost_priv(host);
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_block_rw *brcmd;
+	void *buf;
+	uint16_t opts = 0;
+	int rval = 0;
+
+	if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
+	    2 * sizeof(uint32_t) + sizeof(struct qla_block_rw))
+		return -EINVAL;
+
+	brcmd =
+	(struct qla_block_rw *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2];
+
+	ql_log(ql_log_info, vha, 0x7062,
+	       "%s: region 0x%x options 0x%x rw_length 0x%x offset 0x%x chunk_length 0x%x\n",
+		__func__, brcmd->region, brcmd->options, brcmd->rw_length,
+		brcmd->region_offset, brcmd->chunk_length);
+
+	if (brcmd->region > U16_MAX) {
+		ql_log(ql_log_warn, vha, 0x7066,
+		       "%s: invalid region 0x%x\n", __func__, brcmd->region);
+		return -EINVAL;
+	}
+
+	mutex_lock(&ha->optrom_mutex);
+	rval = qla2x00_optrom_setup(bsg_job, vha, brcmd->region_offset, 0);
+	if (rval) {
+		mutex_unlock(&ha->optrom_mutex);
+		return rval;
+	}
+
+	check_and_set_mbc_bits(brcmd->options, opts, QLA_IS_TIM, BIT_15);
+	check_and_set_mbc_bits(brcmd->options, opts, QLA_IS_SECURE, BIT_7);
+	check_and_set_mbc_bits(brcmd->options, opts, QLA_UPDATE_MBR, BIT_8);
+
+	if (!ha->isp_ops->read_optrom_region) {
+		vfree(ha->optrom_buffer);
+		ha->optrom_buffer = NULL;
+		ha->optrom_state = QLA_SWAITING;
+		mutex_unlock(&ha->optrom_mutex);
+		return -EINVAL;
+	}
+
+	buf = ha->isp_ops->read_optrom_region(vha, brcmd->region, opts,
+				ha->optrom_buffer, ha->optrom_region_start,
+				ha->optrom_region_size);
+	if (!buf) {
+		ql_log(ql_log_warn, vha, 0x7063,
+			"%s failed to read flash region 0x%x\n",
+			__func__, brcmd->region);
+		bsg_reply->result = -EINVAL;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+							EXT_STATUS_MAILBOX;
+		bsg_reply->reply_payload_rcv_len = 0;
+	} else {
+		bsg_reply->result = DID_OK;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+			EXT_STATUS_OK;
+
+		ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, vha, 0x72a6,
+			       ha->optrom_buffer, ha->optrom_region_size);
+
+		sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+				    bsg_job->reply_payload.sg_cnt,
+				    ha->optrom_buffer,
+				    ha->optrom_region_size);
+
+		bsg_reply->reply_payload_rcv_len = ha->optrom_region_size;
+	}
+	vfree(ha->optrom_buffer);
+	ha->optrom_buffer = NULL;
+	ha->optrom_state = QLA_SWAITING;
+	mutex_unlock(&ha->optrom_mutex);
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
+
+	return 0;
+}
+
+/**
+ * qla29xx_bsg_flash_block_write - Write flash block for QLA29XX.
+ * @bsg_job: BSG job structure.
+ *
+ * Returns 0 on success, error code on failure.
+ */
+static int qla29xx_bsg_flash_block_write(struct bsg_job *bsg_job)
+{
+	struct fc_bsg_request *bsg_req = bsg_job->request;
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
+	scsi_qla_host_t *vha = shost_priv(host);
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_block_rw *bwcmd;
+	uint16_t opts = 0;
+	int rval = 0;
+
+	if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
+	    2 * sizeof(uint32_t) + sizeof(struct qla_block_rw))
+		return -EINVAL;
+
+	bwcmd =
+	   (struct qla_block_rw *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2];
+
+	ql_log(ql_log_info, vha, 0x7064,
+	       "%s: region 0x%x options 0x%x rw_length 0x%x offset 0x%x chunk_length 0x%x\n",
+		__func__, bwcmd->region, bwcmd->options, bwcmd->rw_length,
+		bwcmd->region_offset, bwcmd->chunk_length);
+
+	if (bwcmd->region > U16_MAX) {
+		ql_log(ql_log_warn, vha, 0x7067,
+		       "%s: invalid region 0x%x\n", __func__, bwcmd->region);
+		return -EINVAL;
+	}
+
+	mutex_lock(&ha->optrom_mutex);
+	rval = qla2x00_optrom_setup(bsg_job, vha, bwcmd->region_offset, 1);
+	if (rval) {
+		mutex_unlock(&ha->optrom_mutex);
+		return rval;
+	}
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			bsg_job->request_payload.sg_cnt, ha->optrom_buffer,
+			ha->optrom_region_size);
+
+	ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, vha, 0x73a6,
+		       ha->optrom_buffer, ha->optrom_region_size);
+
+	check_and_set_mbc_bits(bwcmd->options, opts, QLA_IS_TIM, BIT_15);
+	check_and_set_mbc_bits(bwcmd->options, opts, QLA_IS_SECURE, BIT_7);
+	check_and_set_mbc_bits(bwcmd->options, opts, QLA_UPDATE_MBR, BIT_8);
+
+	if (!ha->isp_ops->write_optrom_region) {
+		vfree(ha->optrom_buffer);
+		ha->optrom_buffer = NULL;
+		ha->optrom_state = QLA_SWAITING;
+		mutex_unlock(&ha->optrom_mutex);
+		return -EINVAL;
+	}
+
+	rval = ha->isp_ops->write_optrom_region(vha, bwcmd->region, opts,
+				ha->optrom_buffer, ha->optrom_region_start,
+				ha->optrom_region_size);
+	if (rval) {
+		ql_log(ql_log_warn, vha, 0x7065,
+			"%s failed to write flash %x\n", __func__, rval);
+		bsg_reply->result = -EINVAL;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+							EXT_STATUS_MAILBOX;
+	} else {
+		bsg_reply->result = DID_OK;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+			EXT_STATUS_OK;
+	}
+	vfree(ha->optrom_buffer);
+	ha->optrom_buffer = NULL;
+	ha->optrom_state = QLA_SWAITING;
+	mutex_unlock(&ha->optrom_mutex);
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+	bsg_job_done(bsg_job, bsg_reply->result,
+			bsg_reply->reply_payload_rcv_len);
+	return 0;
+}
+
+static int
+qla29xx_mpi_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha,
+	uint32_t start, uint8_t is_update)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	if (unlikely(pci_channel_offline(ha->pdev)))
+		return -EINVAL;
+
+	if (ha->optrom_state != QLA_SWAITING) {
+		ql_log(ql_log_info, vha, 0x7068,
+		       "optrom_state %d.\n", ha->optrom_state);
+		return -EBUSY;
+	}
+
+	ha->optrom_region_start = start;
+	if (is_update) {
+		ha->optrom_region_size = bsg_job->request_payload.payload_len;
+		ha->optrom_state = QLA_SWRITING;
+	} else {
+		ha->optrom_region_size = bsg_job->reply_payload.payload_len;
+		ha->optrom_state = QLA_SREADING;
+	}
+
+	ha->optrom_buffer = vzalloc(ha->optrom_region_size);
+	if (!ha->optrom_buffer) {
+		ql_log(ql_log_warn, vha, 0x7069,
+		       "%s: Unable to allocate memory for optrom retrieval (%x)\n",
+		       __func__, ha->optrom_region_size);
+		ha->optrom_state = QLA_SWAITING;
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int qla2900_bsg_dump_mpi(struct bsg_job *bsg_job)
+{
+	struct fc_bsg_request *bsg_req = bsg_job->request;
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
+	scsi_qla_host_t *vha = shost_priv(host);
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_load_dump_mpi *dmcmd;
+	uint16_t opts = 0;
+	int rval = 0;
+
+	if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
+	    2 * sizeof(uint32_t) + sizeof(struct qla_load_dump_mpi))
+		return -EINVAL;
+
+	if (!IS_QLA29XX(ha)) {
+		ql_log(ql_log_warn, vha, 0x706c,
+		       "%s: MPI dump not supported on this adapter.\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	dmcmd =
+	(struct qla_load_dump_mpi *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2];
+
+	ql_log(ql_log_info, vha, 0xffff,
+	       "%s: mpi_address 0x%x mpi options 0x%x length 0x%x\n",
+		__func__, dmcmd->mpi_address, dmcmd->options, dmcmd->length);
+
+	mutex_lock(&ha->optrom_mutex);
+	rval = qla29xx_mpi_optrom_setup(bsg_job, vha, dmcmd->mpi_address, 1);
+	if (rval) {
+		mutex_unlock(&ha->optrom_mutex);
+		return rval;
+	}
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			  bsg_job->request_payload.sg_cnt, ha->optrom_buffer,
+			  ha->optrom_region_size);
+
+	ql_dump_buffer(ql_dbg_init, vha, 0x00d7, ha->optrom_buffer,
+			ha->optrom_region_size);
+
+	check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_SECURE_ENABLE,
+			       BIT_3);
+	check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_OTP_PROV, BIT_4);
+	check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_DEV_CSR, BIT_5);
+	check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_AUTH_CMD_BIN,
+			       BIT_6);
+	check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_MLDSA_ALGO,
+			       BIT_9);
+
+	rval = qla29xx_mpi_optrom_data(vha, opts, ha->optrom_buffer,
+				       ha->optrom_region_start,
+				       ha->optrom_region_size,
+				       QLA29XX_MPI_OP_DUMP);
+	if (rval) {
+		ql_log(ql_log_warn, vha, 0xffff,
+			"%s failed mpi dump %x\n", __func__, rval);
+		bsg_reply->result = -EINVAL;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+							EXT_STATUS_MAILBOX;
+	} else {
+		bsg_reply->result = DID_OK;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+			EXT_STATUS_OK;
+	}
+
+	vfree(ha->optrom_buffer);
+	ha->optrom_buffer = NULL;
+	ha->optrom_state = QLA_SWAITING;
+	mutex_unlock(&ha->optrom_mutex);
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+	bsg_job_done(bsg_job, bsg_reply->result,
+			bsg_reply->reply_payload_rcv_len);
+	return 0;
+}
+
+static int qla2900_bsg_load_mpi(struct bsg_job *bsg_job)
+{
+	struct fc_bsg_request *bsg_req = bsg_job->request;
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
+	scsi_qla_host_t *vha = shost_priv(host);
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_load_dump_mpi *lmcmd;
+	uint16_t opts = 0;
+	int rval = 0;
+
+	if (bsg_job->request_len < sizeof(struct fc_bsg_request) +
+	    2 * sizeof(uint32_t) + sizeof(struct qla_load_dump_mpi))
+		return -EINVAL;
+
+	if (!IS_QLA29XX(ha)) {
+		ql_log(ql_log_warn, vha, 0x706d,
+		       "%s: MPI load not supported on this adapter.\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	lmcmd =
+	(struct qla_load_dump_mpi *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2];
+
+	ql_log(ql_log_info, vha, 0xffff,
+	       "%s: mpi_address 0x%x mpi options 0x%x length 0x%x\n",
+		__func__, lmcmd->mpi_address, lmcmd->options, lmcmd->length);
+
+	mutex_lock(&ha->optrom_mutex);
+	rval = qla29xx_mpi_optrom_setup(bsg_job, vha, lmcmd->mpi_address, 0);
+	if (rval) {
+		mutex_unlock(&ha->optrom_mutex);
+		return rval;
+	}
+
+	check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_SECURE_ENABLE,
+			       BIT_3);
+	check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_DEV_CSR, BIT_5);
+	check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_SHADOW_REGS,
+			       BIT_7);
+	check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_CA_CSR, BIT_8);
+	check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_MLDSA_ALGO,
+			       BIT_9);
+	check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_MLDSA_SIGNATURE,
+			       BIT_10);
+
+	rval = qla29xx_mpi_optrom_data(vha, opts, ha->optrom_buffer,
+				       ha->optrom_region_start,
+				       ha->optrom_region_size,
+				       QLA29XX_MPI_OP_LOAD);
+	if (rval) {
+		ql_log(ql_log_warn, vha, 0xffff,
+			"%s failed mpi load %x\n", __func__, rval);
+		bsg_reply->result = -EINVAL;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+							EXT_STATUS_MAILBOX;
+	} else {
+		bsg_reply->result = DID_OK;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+			EXT_STATUS_OK;
+	}
+
+	ql_dump_buffer(ql_dbg_init, vha, 0x00d7, ha->optrom_buffer,
+			ha->optrom_region_size);
+
+	sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+			    bsg_job->reply_payload.sg_cnt,
+			    ha->optrom_buffer,
+			    ha->optrom_region_size);
+
+	bsg_reply->reply_payload_rcv_len = ha->optrom_region_size;
+	vfree(ha->optrom_buffer);
+	ha->optrom_buffer = NULL;
+	ha->optrom_state = QLA_SWAITING;
+	mutex_unlock(&ha->optrom_mutex);
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
+
+	return 0;
+}
+
 static int
 qla2x00_update_fru_versions(struct bsg_job *bsg_job)
 {
@@ -1562,12 +2003,12 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job)
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_image_version_list *list = (void *)bsg;
 	struct qla_image_version *image;
 	uint32_t count;
 	dma_addr_t sfp_dma;
-	void *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	void *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1580,6 +2021,13 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job)
 
 	image = list->version;
 	count = list->count;
+
+	if (struct_size(list, version, count) > sizeof(bsg)) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+		    EXT_STATUS_INVALID_PARAM;
+		goto dealloc;
+	}
+
 	while (count--) {
 		memcpy(sfp, &image->field_info, sizeof(image->field_info));
 		rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
@@ -1615,10 +2063,10 @@ qla2x00_read_fru_status(struct bsg_job *bsg_job)
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1666,10 +2114,10 @@ qla2x00_write_fru_status(struct bsg_job *bsg_job)
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_status_reg *sr = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1713,10 +2161,10 @@ qla2x00_write_i2c(struct bsg_job *bsg_job)
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1727,6 +2175,12 @@ qla2x00_write_i2c(struct bsg_job *bsg_job)
 	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
 	    bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
 
+	if (i2c->length > sizeof(i2c->buffer)) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+		    EXT_STATUS_INVALID_PARAM;
+		goto dealloc;
+	}
+
 	memcpy(sfp, i2c->buffer, i2c->length);
 	rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
 	    i2c->device, i2c->offset, i2c->length, i2c->option);
@@ -1759,10 +2213,10 @@ qla2x00_read_i2c(struct bsg_job *bsg_job)
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
 	int rval = 0;
-	uint8_t bsg[DMA_POOL_SIZE];
+	uint8_t bsg[DMA_POOL_SIZE] = {};
 	struct qla_i2c_access *i2c = (void *)bsg;
 	dma_addr_t sfp_dma;
-	uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+	uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
 
 	if (!sfp) {
 		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
@@ -1773,6 +2227,12 @@ qla2x00_read_i2c(struct bsg_job *bsg_job)
 	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
 	    bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c));
 
+	if (i2c->length > sizeof(i2c->buffer)) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+		    EXT_STATUS_INVALID_PARAM;
+		goto dealloc;
+	}
+
 	rval = qla2x00_read_sfp(vha, sfp_dma, sfp,
 		i2c->device, i2c->offset, i2c->length, i2c->option);
 
@@ -1993,6 +2453,11 @@ qlafx00_mgmt_cmd(struct bsg_job *bsg_job)
 	struct fc_port *fcport;
 	char  *type = "FC_BSG_HST_FX_MGMT";
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + sizeof(uint32_t) +
+	    sizeof(struct qla_mt_iocb_rqst_fx00))
+		return -EINVAL;
+
 	/* Copy the IOCB specific information */
 	piocb_rqst = (struct qla_mt_iocb_rqst_fx00 *)
 	    &bsg_request->rqst_data.h_vendor.vendor_cmd[1];
@@ -2195,7 +2660,7 @@ qla27xx_get_flash_upd_cap(struct bsg_job *bsg_job)
 	struct qla_hw_data *ha = vha->hw;
 	struct qla_flash_update_caps cap;
 
-	if (!(IS_QLA27XX(ha)) && !IS_QLA28XX(ha))
+	if (!(IS_QLA27XX(ha)) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return -EPERM;
 
 	memset(&cap, 0, sizeof(cap));
@@ -2228,7 +2693,7 @@ qla27xx_set_flash_upd_cap(struct bsg_job *bsg_job)
 	uint64_t online_fw_attr = 0;
 	struct qla_flash_update_caps cap;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return -EPERM;
 
 	memset(&cap, 0, sizeof(cap));
@@ -2276,7 +2741,7 @@ qla27xx_get_bbcr_data(struct bsg_job *bsg_job)
 	uint8_t domain, area, al_pa, state;
 	int rval;
 
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return -EPERM;
 
 	memset(&bbcr, 0, sizeof(bbcr));
@@ -2392,10 +2857,10 @@ qla2x00_do_dport_diagnostics(struct bsg_job *bsg_job)
 	struct qla_dport_diag *dd;
 
 	if (!IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) &&
-	    !IS_QLA28XX(vha->hw))
+	    !IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw))
 		return -EPERM;
 
-	dd = kmalloc_obj(*dd);
+	dd = kzalloc_obj(*dd);
 	if (!dd) {
 		ql_log(ql_log_warn, vha, 0x70db,
 		    "Failed to allocate memory for dport.\n");
@@ -2518,8 +2983,13 @@ qla2x00_get_flash_image_status(struct bsg_job *bsg_job)
 	struct qla_active_regions regions = { };
 	struct active_regions active_regions = { };
 
-	qla27xx_get_active_image(vha, &active_regions);
-	regions.global_image = active_regions.global;
+	if (IS_QLA29XX(ha))
+		return -EPERM;
+
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		qla27xx_get_active_image(vha, &active_regions);
+		regions.global_image = active_regions.global;
+	}
 
 	if (IS_QLA27XX(ha))
 		regions.nvme_params = QLA27XX_PRIMARY_IMAGE;
@@ -2555,11 +3025,14 @@ qla2x00_get_flash_image_status(struct bsg_job *bsg_job)
 static int
 qla2x00_get_drv_attr(struct bsg_job *bsg_job)
 {
+	scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
 	struct qla_drv_attr drv_attr;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct qla_hw_data *ha = vha->hw;
 
 	memset(&drv_attr, 0, sizeof(struct qla_drv_attr));
-	drv_attr.ext_attributes |= QLA_IMG_SET_VALID_SUPPORT;
+	if (!IS_QLA29XX(ha))
+		drv_attr.ext_attributes |= QLA_IMG_SET_VALID_SUPPORT;
 
 
 	sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
@@ -2906,6 +3379,13 @@ qla2x00_process_vendor_specific(struct scsi_qla_host *vha, struct bsg_job *bsg_j
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + sizeof(uint32_t)) {
+		ql_log(ql_log_warn, vha, 0x7000,
+		       "BSG request too small for vendor cmd.\n");
+		return -EINVAL;
+	}
+
 	ql_dbg(ql_dbg_edif, vha, 0x911b, "%s FC_BSG_HST_VENDOR cmd[0]=0x%x\n",
 	    __func__, bsg_request->rqst_data.h_vendor.vendor_cmd[0]);
 
@@ -3007,6 +3487,18 @@ qla2x00_process_vendor_specific(struct scsi_qla_host *vha, struct bsg_job *bsg_j
 	case QL_VND_MBX_PASSTHRU:
 		return qla2x00_mailbox_passthru(bsg_job);
 
+	case QL_VND_READ_FLASH_BLOCK:
+		return qla29xx_bsg_flash_block_read(bsg_job);
+
+	case QL_VND_WRITE_FLASH_BLOCK:
+		return qla29xx_bsg_flash_block_write(bsg_job);
+
+	case QL_VND_LOAD_MPI:
+		return qla2900_bsg_load_mpi(bsg_job);
+
+	case QL_VND_DUMP_MPI:
+		return qla2900_bsg_dump_mpi(bsg_job);
+
 	default:
 		return -ENOSYS;
 	}
@@ -3037,8 +3529,11 @@ qla24xx_bsg_request(struct bsg_job *bsg_job)
 	}
 
 	/* Disable port will bring down the chip, allow enable command */
-	if (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
-	    bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS)
+	if (bsg_request->msgcode == FC_BSG_HST_VENDOR &&
+	    bsg_job->request_len >=
+		sizeof(struct fc_bsg_request) + sizeof(uint32_t) &&
+	    (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
+	     bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS))
 		goto skip_chip_chk;
 
 	if (vha->hw->flags.port_isolated) {
@@ -3347,6 +3842,10 @@ static int qla28xx_validate_flash_image(struct bsg_job *bsg_job)
 	if (!IS_QLA28XX(ha) || vha->vp_idx != 0)
 		return -EPERM;
 
+	if (bsg_job->request_len <
+	    sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t))
+		return -EINVAL;
+
 	mutex_lock(&ha->optrom_mutex);
 	rval = qla28xx_do_validate_flash_image(bsg_job, &state);
 	if (rval)
@@ -3365,9 +3864,8 @@ static int qla28xx_validate_flash_image(struct bsg_job *bsg_job)
 	bsg_reply->result = DID_OK << 16;
 	bsg_reply->reply_payload_rcv_len = 0;
 	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
-	if (!rval)
-		bsg_job_done(bsg_job, bsg_reply->result,
-			     bsg_reply->reply_payload_rcv_len);
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
 
 	return QLA_SUCCESS;
 }
diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h
index a920c8e482bc..8a784b8226cc 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.h
+++ b/drivers/scsi/qla2xxx/qla_bsg.h
@@ -40,6 +40,10 @@
 #define QL_VND_MBX_PASSTHRU		0x2B
 #define QL_VND_DPORT_DIAGNOSTICS_V2	0x2C
 #define QL_VND_IMG_SET_VALID	0x30
+#define QL_VND_READ_FLASH_BLOCK		0x33
+#define QL_VND_WRITE_FLASH_BLOCK	0x34
+#define QL_VND_LOAD_MPI			0x35
+#define QL_VND_DUMP_MPI			0x36
 
 /* BSG Vendor specific subcode returns */
 #define EXT_STATUS_OK			0
@@ -83,6 +87,36 @@
 #define ELS_OPCODE_BYTE			0x10
 
 /* BSG Vendor specific definations */
+
+#define QLA_IS_TIM	0x1
+#define QLA_IS_SECURE	0x2
+#define QLA_UPDATE_MBR	0x4
+
+struct qla_block_rw {
+	uint32_t region;
+	uint32_t rw_length;
+	uint32_t options;
+	uint32_t region_offset;
+	uint32_t chunk_length;
+	uint8_t  reserved[44];
+} __packed;
+
+struct qla_load_dump_mpi {
+	uint32_t mpi_address;
+	uint32_t length;
+	uint32_t options;
+#define	QLA_LDM_SECURE_ENABLE	0x1
+#define	QLA_LDM_OTP_PROV	0x2
+#define	QLA_LDM_DEV_CSR		0x4
+#define	QLA_LDM_AUTH_CMD_BIN	0x8
+#define	QLA_LDM_SHADOW_REGS	0x10
+#define	QLA_LDM_CA_CSR		0x20
+#define	QLA_LDM_MLDSA_ALGO	0x40
+#define	QLA_LDM_MLDSA_SIGNATURE	0x80
+
+	uint8_t reserved[52];
+} __packed;
+
 #define A84_ISSUE_WRITE_TYPE_CMD        0
 #define A84_ISSUE_READ_TYPE_CMD         1
 #define A84_CLEANUP_CMD                 2
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index a7e3ec9bba47..196cfa8f8623 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -16,7 +16,7 @@
  * |                              |                    | 0x2127-0x2128  |
  * | Queue Command and IO tracing |       0x3074       | 0x300b         |
  * |                              |                    | 0x3027-0x3028  |
- * |                              |                    | 0x303d-0x3041  |
+ * |                              |                    | 0x303e-0x3041  |
  * |                              |                    | 0x302e,0x3033  |
  * |                              |                    | 0x3036,0x3038  |
  * |                              |                    | 0x303a		|
@@ -89,16 +89,17 @@ qla2xxx_copy_queues(struct qla_hw_data *ha, void *ptr)
 {
 	struct req_que *req = ha->req_q_map[0];
 	struct rsp_que *rsp = ha->rsp_q_map[0];
+	size_t req_entry_size = qla_req_entry_size(ha);
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
+
 	/* Request queue. */
-	memcpy(ptr, req->ring, req->length *
-	    sizeof(request_t));
+	memcpy(ptr, req->ring, req->length * req_entry_size);
 
 	/* Response queue. */
-	ptr += req->length * sizeof(request_t);
-	memcpy(ptr, rsp->ring, rsp->length  *
-	    sizeof(response_t));
+	ptr += req->length * req_entry_size;
+	memcpy(ptr, rsp->ring, rsp->length * rsp_entry_size);
 
-	return ptr + (rsp->length * sizeof(response_t));
+	return ptr + (rsp->length * rsp_entry_size);
 }
 
 int
@@ -171,7 +172,7 @@ qla27xx_dump_mpi_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram,
 
 		if (!test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
 			/* no interrupt, timed out*/
-			return rval;
+			return QLA_FUNCTION_TIMEOUT;
 		}
 		if (rval) {
 			/* error completion status */
@@ -254,7 +255,7 @@ qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, __be32 *ram,
 
 		if (!test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
 			/* no interrupt, timed out*/
-			return rval;
+			return QLA_FUNCTION_TIMEOUT;
 		}
 		if (rval) {
 			/* error completion status */
@@ -606,6 +607,8 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, __be32 **last_chain)
 	struct req_que *req;
 	struct rsp_que *rsp;
 	int que;
+	size_t req_entry_size = qla_req_entry_size(ha);
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
 
 	if (!ha->mqenable)
 		return ptr;
@@ -623,19 +626,19 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, __be32 **last_chain)
 		q->chain_size = htonl(
 		    sizeof(struct qla2xxx_mqueue_chain) +
 		    sizeof(struct qla2xxx_mqueue_header) +
-		    (req->length * sizeof(request_t)));
+		    (req->length * req_entry_size));
 		ptr += sizeof(struct qla2xxx_mqueue_chain);
 
 		/* Add header. */
 		qh = ptr;
 		qh->queue = htonl(TYPE_REQUEST_QUEUE);
 		qh->number = htonl(que);
-		qh->size = htonl(req->length * sizeof(request_t));
+		qh->size = htonl(req->length * req_entry_size);
 		ptr += sizeof(struct qla2xxx_mqueue_header);
 
 		/* Add data. */
-		memcpy(ptr, req->ring, req->length * sizeof(request_t));
-		ptr += req->length * sizeof(request_t);
+		memcpy(ptr, req->ring, req->length * req_entry_size);
+		ptr += req->length * req_entry_size;
 	}
 
 	/* Response queues */
@@ -651,19 +654,19 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, __be32 **last_chain)
 		q->chain_size = htonl(
 		    sizeof(struct qla2xxx_mqueue_chain) +
 		    sizeof(struct qla2xxx_mqueue_header) +
-		    (rsp->length * sizeof(response_t)));
+		    (rsp->length * rsp_entry_size));
 		ptr += sizeof(struct qla2xxx_mqueue_chain);
 
 		/* Add header. */
 		qh = ptr;
 		qh->queue = htonl(TYPE_RESPONSE_QUEUE);
 		qh->number = htonl(que);
-		qh->size = htonl(rsp->length * sizeof(response_t));
+		qh->size = htonl(rsp->length * rsp_entry_size);
 		ptr += sizeof(struct qla2xxx_mqueue_header);
 
 		/* Add data. */
-		memcpy(ptr, rsp->ring, rsp->length * sizeof(response_t));
-		ptr += rsp->length * sizeof(response_t);
+		memcpy(ptr, rsp->ring, rsp->length * rsp_entry_size);
+		ptr += rsp->length * rsp_entry_size;
 	}
 
 	return ptr;
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 5593ad7fad27..97e2a1a9ce3f 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -337,6 +337,7 @@ static inline void wrt_reg_dword(volatile __le32 __iomem *addr, u32 data)
 
 #define MAX_CMDSZ	16		/* SCSI maximum CDB size. */
 #include "qla_fw.h"
+#include "qla_fw29.h"
 
 struct name_list_extended {
 	struct get_name_list_extended *l;
@@ -576,7 +577,7 @@ struct srb_iocb {
 			__le16 comp_status;
 
 			uint8_t modifier;
-			uint8_t vp_index;
+			uint16_t vp_index;
 			uint16_t loop_id;
 		} tmf;
 		struct {
@@ -1278,6 +1279,7 @@ static inline bool qla2xxx_is_valid_mbs(unsigned int mbs)
 #define MBC_LOAD_RISC_RAM		9	/* Load RAM command. */
 #define MBC_DUMP_RISC_RAM		0xa	/* Dump RAM command. */
 #define MBC_SECURE_FLASH_UPDATE		0xa	/* Secure Flash Update(28xx) */
+#define MBC_RD_WR_FLASH			0xa	/* Read/write Dword/block Flash(29xx) */
 #define MBC_LOAD_RISC_RAM_EXTENDED	0xb	/* Load RAM extended. */
 #define MBC_DUMP_RISC_RAM_EXTENDED	0xc	/* Dump RAM extended. */
 #define MBC_WRITE_RAM_WORD_EXTENDED	0xd	/* Write RAM word extended */
@@ -1547,6 +1549,11 @@ typedef struct {
 #define PD_STATE_PORT_LOGOUT			10
 #define PD_STATE_WAIT_PORT_LOGOUT_ACK		11
 
+enum qla29xx_mpi_optrom_op {
+	QLA29XX_MPI_OP_DUMP,
+	QLA29XX_MPI_OP_LOAD,
+};
+
 
 #define QLA_ZIO_MODE_6		(BIT_2 | BIT_1)
 #define QLA_ZIO_DISABLED	0
@@ -1988,6 +1995,18 @@ typedef struct {
 #define RESPONSE_PROCESSED	0xDEADDEAD	/* Signature */
 } response_t;
 
+struct response_ext {
+	uint8_t		entry_type;		/* Entry type. */
+	uint8_t		entry_count;		/* Entry count. */
+	uint8_t		sys_define;		/* System defined. */
+	uint8_t		entry_status;		/* Entry Status. */
+	uint32_t	handle;			/* System defined handle */
+	uint8_t		data[52];
+	uint32_t	signature;
+	uint8_t		reserved[64];
+#define RESPONSE_PROCESSED	0xDEADDEAD	/* Signature */
+};
+
 /*
  * ISP queue - ATIO queue entry definition.
  */
@@ -2066,6 +2085,24 @@ typedef struct {
 	struct dsd64 dsd[2];
 } cmd_a64_entry_t, request_t;
 
+struct request_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+	uint32_t handle;		/* System handle. */
+	target_id_t target;		/* SCSI ID */
+	__le16  lun;			/* SCSI LUN */
+	__le16  control_flags;		/* Control flags. */
+	uint16_t reserved_1;
+	__le16  timeout;		/* Command timeout. */
+	__le16  dseg_count;		/* Data segment count. */
+	uint8_t scsi_cdb[MAX_CMDSZ];	/* SCSI command words. */
+	uint32_t byte_count;		/* Total byte count. */
+	struct dsd64 dsd[2];
+	uint8_t reserved[64];
+};
+
 /*
  * ISP queue - continuation entry structure definition.
  */
@@ -2324,6 +2361,15 @@ typedef struct {
 	uint8_t reserved_2[48];
 } mrk_entry_t;
 
+/* 29xx definitions */
+struct sts_cont_entry_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+	uint8_t data[124];		/* data */
+};
+
 /*
  * ISP queue - Management Server entry structure definition.
  */
@@ -2482,6 +2528,14 @@ struct imm_ntfy_from_isp {
 #define REQUEST_ENTRY_SIZE	(sizeof(request_t))
 
 
+/*
+ * 29xx (qla29xx) uses 128-byte ring entries for both request and response
+ * queues.  These macros give the size of an extended IOCB slot and are
+ * used when allocating from / zeroing the 29xx request ring via ring_ext_ptr.
+ */
+#define RESPONSE_ENTRY_SIZE_EXT (sizeof(struct response_ext))
+#define REQUEST_ENTRY_SIZE_EXT  (sizeof(struct request_ext))
+
 
 /*
  * Switch info gathering structure.
@@ -2591,6 +2645,8 @@ typedef struct fc_port {
 	struct list_head list;
 	struct scsi_qla_host *vha;
 	struct list_head unsol_ctx_head;
+	/* Serializes unsol_ctx_head against ISR, DPC and NVMe transport. */
+	spinlock_t unsol_ctx_lock;
 
 	unsigned int conf_compl_supported:1;
 	unsigned int deleted:2;
@@ -3503,6 +3559,13 @@ struct isp_operations {
 	int (*write_optrom)(struct scsi_qla_host *, void *, uint32_t,
 		uint32_t);
 
+	void *(*read_optrom_region)(struct scsi_qla_host *vha,
+		uint16_t reg_code, uint16_t opts, void *buf,
+		uint32_t offset, uint32_t length);
+	int (*write_optrom_region)(struct scsi_qla_host *vha,
+		uint16_t reg_code, uint16_t opts, void *buf,
+		uint32_t offset, uint32_t length);
+
 	int (*get_flash_version) (struct scsi_qla_host *, void *);
 	int (*start_scsi) (srb_t *);
 	int (*start_scsi_mq) (srb_t *);
@@ -3528,7 +3591,6 @@ struct isp_operations {
 #define QLA_MIDX_DEFAULT	0
 #define QLA_MIDX_RSP_Q		1
 #define QLA_PCI_MSIX_CONTROL	0xa2
-#define QLA_83XX_PCI_MSIX_CONTROL	0x92
 
 struct scsi_qla_host;
 
@@ -3737,6 +3799,14 @@ struct rsp_que {
 	dma_addr_t  dma;
 	response_t *ring;
 	response_t *ring_ptr;
+	/*
+	 * 29xx extended IOCB ring (128-byte entries) aliases of ring/ring_ptr.
+	 * Allocated when IS_QLA29XX(ha); set up at queue-init time to point at
+	 * the same DMA memory as 'ring' but typed for the 29xx stride.  24xx
+	 * code paths walk via ring_ptr; 29xx paths walk via ring_ext_ptr.
+	 */
+	struct response_ext *ring_ext;
+	struct response_ext *ring_ext_ptr;
 	__le32	__iomem *rsp_q_in;	/* FWI2-capable only. */
 	__le32	__iomem *rsp_q_out;
 	uint16_t  ring_index;
@@ -3764,6 +3834,14 @@ struct req_que {
 	dma_addr_t  dma;
 	request_t *ring;
 	request_t *ring_ptr;
+	/*
+	 * 29xx extended IOCB ring (128-byte entries) aliases of ring/ring_ptr.
+	 * Allocated when IS_QLA29XX(ha); set up at queue-init time to point at
+	 * the same DMA memory as 'ring' but typed for the 29xx stride.  24xx
+	 * code paths must not run on 29xx HW, and vice-versa.
+	 */
+	struct request_ext *ring_ext;
+	struct request_ext *ring_ext_ptr;
 	__le32	__iomem *req_q_in;	/* FWI2-capable only. */
 	__le32	__iomem *req_q_out;
 	uint16_t  ring_index;
@@ -4100,6 +4178,7 @@ struct qla_hw_data {
 #define SRB_MIN_REQ     128
 	mempool_t       *srb_mempool;
 	u8 port_name[WWN_SIZE];
+	u16 mbregs[32];
 
 	volatile struct {
 		uint32_t	mbox_int		:1;
@@ -4170,7 +4249,10 @@ struct qla_hw_data {
 		uint32_t	eeh_flush:2;
 #define EEH_FLUSH_RDY  1
 #define EEH_FLUSH_DONE 2
+		uint32_t	t262_fail:1;
+		uint32_t	t272_fail:1;
 		uint32_t	secure_mcu:1;
+		uint32_t	valid_flt:1;
 	} flags;
 
 	uint16_t max_exchg;
@@ -4256,6 +4338,7 @@ struct qla_hw_data {
 #define PORT_SPEED_16GB 0x05
 #define PORT_SPEED_32GB 0x06
 #define PORT_SPEED_64GB 0x07
+#define PORT_SPEED_128GB 0x08
 #define PORT_SPEED_10GB	0x13
 	uint16_t	link_data_rate;         /* F/W operating speed */
 	uint16_t	set_data_rate;		/* Set by user */
@@ -4287,6 +4370,10 @@ struct qla_hw_data {
 #define PCI_DEVICE_ID_QLOGIC_ISP2089	0x2089
 #define PCI_DEVICE_ID_QLOGIC_ISP2281	0x2281
 #define PCI_DEVICE_ID_QLOGIC_ISP2289	0x2289
+#define PCI_DEVICE_ID_QLOGIC_ISP2099	0x2099
+#define PCI_DEVICE_ID_QLOGIC_ISP2299	0x2299
+#define PCI_DEVICE_ID_QLOGIC_ISP2091	0x2091
+#define PCI_DEVICE_ID_QLOGIC_ISP2291	0x2291
 
 	uint32_t	isp_type;
 #define DT_ISP2100                      BIT_0
@@ -4316,7 +4403,11 @@ struct qla_hw_data {
 #define DT_ISP2089			BIT_24
 #define DT_ISP2281			BIT_25
 #define DT_ISP2289			BIT_26
-#define DT_ISP_LAST			(DT_ISP2289 << 1)
+#define DT_ISP2299			BIT_27
+#define DT_ISP2099			BIT_28
+#define DT_ISP2091			BIT_29
+#define DT_ISP2291			BIT_30
+#define DT_ISP_LAST			((uint32_t)DT_ISP2291 << 1)
 
 	uint32_t	device_type;
 #define DT_T10_PI                       BIT_25
@@ -4353,6 +4444,10 @@ struct qla_hw_data {
 #define IS_QLA2261(ha)	(DT_MASK(ha) & DT_ISP2261)
 #define IS_QLA2081(ha)	(DT_MASK(ha) & DT_ISP2081)
 #define IS_QLA2281(ha)	(DT_MASK(ha) & DT_ISP2281)
+#define IS_QLA2299(ha)	(DT_MASK(ha) & DT_ISP2299)
+#define IS_QLA2099(ha)	(DT_MASK(ha) & DT_ISP2099)
+#define IS_QLA2091(ha)	(DT_MASK(ha) & DT_ISP2091)
+#define IS_QLA2291(ha)	(DT_MASK(ha) & DT_ISP2291)
 
 #define IS_QLA23XX(ha)  (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \
 			IS_QLA6312(ha) || IS_QLA6322(ha))
@@ -4363,6 +4458,9 @@ struct qla_hw_data {
 #define IS_QLA84XX(ha)  (IS_QLA8432(ha))
 #define IS_QLA27XX(ha)  (IS_QLA2071(ha) || IS_QLA2271(ha) || IS_QLA2261(ha))
 #define IS_QLA28XX(ha)	(IS_QLA2081(ha) || IS_QLA2281(ha))
+#define IS_QLA29XX(ha)	(IS_QLA2099(ha) || IS_QLA2299(ha) || \
+				IS_QLA2091(ha) || IS_QLA2291(ha))
+
 #define IS_QLA24XX_TYPE(ha)     (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \
 				IS_QLA84XX(ha))
 #define IS_CNA_CAPABLE(ha)	(IS_QLA81XX(ha) || IS_QLA82XX(ha) || \
@@ -4372,9 +4470,10 @@ struct qla_hw_data {
 				IS_QLA25XX(ha) || IS_QLA81XX(ha) || \
 				IS_QLA82XX(ha) || IS_QLA83XX(ha) || \
 				IS_QLA8044(ha) || IS_QLA27XX(ha) || \
-				IS_QLA28XX(ha))
+				IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_MSIX_NACK_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha) || \
-				IS_QLA27XX(ha) || IS_QLA28XX(ha))
+				IS_QLA27XX(ha) || IS_QLA28XX(ha) || \
+				IS_QLA29XX(ha))
 #define IS_NOPOLLING_TYPE(ha)	(IS_QLA81XX(ha) && (ha)->flags.msix_enabled)
 #define IS_FAC_REQUIRED(ha)	(IS_QLA81XX(ha) || IS_QLA83XX(ha) || \
 				IS_QLA27XX(ha) || IS_QLA28XX(ha))
@@ -4390,9 +4489,9 @@ struct qla_hw_data {
 #define HAS_EXTENDED_IDS(ha)    ((ha)->device_type & DT_EXTENDED_IDS)
 #define IS_CT6_SUPPORTED(ha)	((ha)->device_type & DT_CT6_SUPPORTED)
 #define IS_MQUE_CAPABLE(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
-				 IS_QLA28XX(ha))
+				 IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_BIDI_CAPABLE(ha) \
-    (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+    (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 /* Bit 21 of fw_attributes decides the MCTP capabilities */
 #define IS_MCTP_CAPABLE(ha)	(IS_QLA2031(ha) && \
 				((ha)->fw_attributes_ext[0] & BIT_0))
@@ -4408,18 +4507,19 @@ struct qla_hw_data {
 	(QLA_NVME_IOS(_sp) && QLA_ABTS_FW_ENABLED(_sp->fcport->vha->hw))
 
 #define IS_PI_UNINIT_CAPABLE(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
-					 IS_QLA28XX(ha))
+					 IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_PI_IPGUARD_CAPABLE(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
-					 IS_QLA28XX(ha))
+					 IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_PI_DIFB_DIX0_CAPABLE(ha)	(0)
 #define IS_PI_SPLIT_DET_CAPABLE_HBA(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
-					IS_QLA28XX(ha))
+					IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_PI_SPLIT_DET_CAPABLE(ha)	(IS_PI_SPLIT_DET_CAPABLE_HBA(ha) && \
     (((ha)->fw_attributes_h << 16 | (ha)->fw_attributes) & BIT_22))
 #define IS_ATIO_MSIX_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
 				IS_QLA28XX(ha))
 #define IS_TGT_MODE_CAPABLE(ha)	(ha->tgt.atio_q_length)
-#define IS_SHADOW_REG_CAPABLE(ha)  (IS_QLA27XX(ha) || IS_QLA28XX(ha))
+#define IS_SHADOW_REG_CAPABLE(ha)  (IS_QLA27XX(ha) || IS_QLA28XX(ha) || \
+				IS_QLA29XX(ha))
 #define IS_DPORT_CAPABLE(ha)  (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
 				IS_QLA28XX(ha))
 #define IS_FAWWN_CAPABLE(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
@@ -4452,6 +4552,7 @@ struct qla_hw_data {
 	uint16_t	vpd_size;
 	uint16_t	vpd_base;
 	void		*vpd;
+	struct qla_flash_memo_block *fiv;
 
 	uint16_t	loop_reset_delay;
 	uint8_t		retry_count;
@@ -4483,6 +4584,8 @@ struct qla_hw_data {
 
 	struct qla_flt_header *flt;
 	dma_addr_t	flt_dma;
+	struct qla_flash_layout *flt_data;
+	uint32_t	fw_dump_tmplt_len;
 
 #define XGMAC_DATA_SIZE	4096
 	void		*xgmac_data;
@@ -4707,6 +4810,9 @@ struct qla_hw_data {
 	uint32_t	fdt_protect_sec_cmd;
 	uint32_t	fdt_wrt_sts_reg_cmd;
 
+#define QLA_SEGMENT_LENGTH      0x25000
+	uint32_t        flt_segment_length;
+
 	struct {
 		uint32_t	flt_region_flt;
 		uint32_t	flt_region_fdt;
@@ -4900,6 +5006,7 @@ struct active_regions {
 #define QLA_SET_DATA_RATE_LR	2 /* Set speed and initiate LR */
 
 #define QLA_DEFAULT_PAYLOAD_SIZE	64
+#define QLA_MAX_IOCB_SIZE		128
 /*
  * This item might be allocated with a size > sizeof(struct purex_item).
  * The "size" variable gives the size of the payload (which
@@ -4914,7 +5021,7 @@ struct purex_item {
 	atomic_t in_use;
 	uint16_t size;
 	struct {
-		uint8_t iocb[64];
+		u8 iocb[QLA_MAX_IOCB_SIZE];
 	} iocb;
 };
 
@@ -5333,6 +5440,14 @@ static inline bool qla_vha_mark_busy(scsi_qla_host_t *vha)
 /*
  * Flash support definitions
  */
+#define check_and_set_mbc_bits(bopt, dopt, bit_to_check, bit_to_set) {	\
+	if (bopt & bit_to_check)			\
+		dopt |= bit_to_set;			\
+}
+
+#define SET_FW_BIT(__opts, bit) ((__opts) |= (bit))
+#define CLEAR_FW_BIT(__opts, bit) ((__opts) &= ~(bit))
+
 #define OPTROM_SIZE_2300	0x20000
 #define OPTROM_SIZE_2322	0x100000
 #define OPTROM_SIZE_24XX	0x100000
@@ -5474,9 +5589,9 @@ struct sff_8247_a0 {
 /* BPM -- Buffer Plus Management support. */
 #define IS_BPM_CAPABLE(ha) \
 	(IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA83XX(ha) || \
-	 IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	 IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_BPM_RANGE_CAPABLE(ha) \
-	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 #define IS_BPM_ENABLED(vha) \
 	(ql2xautodetectsfp && !vha->vp_idx && IS_BPM_CAPABLE(vha->hw))
 
@@ -5597,4 +5712,9 @@ struct ql_vnd_tgt_stats_resp {
 	(!_fcport || IS_SESSION_DELETED(_fcport) || atomic_read(&_fcport->state) != FCS_ONLINE || \
 	!_fcport->vha->hw->flags.fw_started)
 
+#define is_flash_read(_opt)	\
+	(!(_opt & BIT_9) && !(_opt & BIT_6))
+
+#define is_flash_write(_opt)	\
+	(!(_opt & BIT_9) && (_opt & BIT_6))
 #endif
diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
index 43970caca7b3..5d08bdbcf70a 100644
--- a/drivers/scsi/qla2xxx/qla_dfs.c
+++ b/drivers/scsi/qla2xxx/qla_dfs.c
@@ -497,7 +497,7 @@ qla2x00_dfs_fce_write(struct file *file, const char __user *buffer,
 	unsigned long enable;
 
 	if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) {
+	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) {
 		ql_dbg(ql_dbg_user, vha, 0xd034,
 		       "this adapter does not support FCE.");
 		return -EINVAL;
@@ -510,7 +510,9 @@ qla2x00_dfs_fce_write(struct file *file, const char __user *buffer,
 		return PTR_ERR(buf);
 	}
 
-	enable = kstrtoul(buf, 0, 0);
+	rc = kstrtoul(buf, 0, &enable);
+	if (rc)
+		goto out_free;
 	rc = count;
 
 	mutex_lock(&ha->fce_mutex);
@@ -698,7 +700,7 @@ qla2x00_dfs_setup(scsi_qla_host_t *vha)
 	struct qla_hw_data *ha = vha->hw;
 
 	if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		goto out;
 
 	if (qla2x00_dfs_root)
diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c
index eccedb38a515..bfa520f936a2 100644
--- a/drivers/scsi/qla2xxx/qla_edif.c
+++ b/drivers/scsi/qla2xxx/qla_edif.c
@@ -2534,7 +2534,7 @@ qla24xx_sa_replace_iocb(srb_t *sp, struct sa_update_28xx *sa_update_iocb)
 
 void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp)
 {
-	struct purex_entry_24xx *p = *pkt;
+	struct qla_hw_data *ha = vha->hw;
 	struct enode		*ptr;
 	int		sid;
 	u16 totlen;
@@ -2544,26 +2544,55 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp)
 	struct fc_port *fcport;
 	struct qla_els_pt_arg a;
 	be_id_t beid;
+	__le16 nport_handle;
+	__le32 rx_xchg_addr;
+	__le16 ox_id;
+	__le16 frame_size, status_flags, trunc_frame_size;
+	uint8_t s_id[3], d_id[3];
+	uint8_t vp_idx;
+	struct purex_entry_24xx *p = *pkt;
 
 	memset(&a, 0, sizeof(a));
 
+	/*
+	 * purex_entry_24xx_ext (29xx) overlays purex_entry_24xx for every
+	 * field touched here -- nport_handle, rx_xchg_addr, ox_id, frame_size,
+	 * status_flags, trunc_frame_size, s_id[3], d_id[3] -- with only
+	 * vp_idx differing in width (u8 at offset 6 vs __le16 at offsets 6-7,
+	 * with reserved2 at offset 7 in the 24xx layout). So all reads but
+	 * vp_idx go through a single struct purex_entry_24xx * view.
+	 */
+	nport_handle = p->nport_handle;
+	rx_xchg_addr = p->rx_xchg_addr;
+	ox_id = p->ox_id;
+	frame_size = p->frame_size;
+	status_flags = p->status_flags;
+	trunc_frame_size = p->trunc_frame_size;
+	memcpy(s_id, p->s_id, sizeof(s_id));
+	memcpy(d_id, p->d_id, sizeof(d_id));
+	if (IS_QLA29XX(ha))
+		vp_idx = le16_to_cpu(((struct purex_entry_24xx_ext *)
+				      *pkt)->vp_idx);
+	else
+		vp_idx = p->vp_idx;
+
 	a.els_opcode = ELS_AUTH_ELS;
-	a.nport_handle = p->nport_handle;
-	a.rx_xchg_address = p->rx_xchg_addr;
-	a.did.b.domain = p->s_id[2];
-	a.did.b.area   = p->s_id[1];
-	a.did.b.al_pa  = p->s_id[0];
+	a.nport_handle = nport_handle;
+	a.rx_xchg_address = rx_xchg_addr;
+	a.did.b.domain = s_id[2];
+	a.did.b.area   = s_id[1];
+	a.did.b.al_pa  = s_id[0];
 	a.tx_byte_count = a.tx_len = sizeof(struct fc_els_ls_rjt);
-	a.tx_addr = vha->hw->elsrej.cdma;
+	a.tx_addr = ha->elsrej.cdma;
 	a.vp_idx = vha->vp_idx;
 	a.control_flags = EPD_ELS_RJT;
-	a.ox_id = le16_to_cpu(p->ox_id);
+	a.ox_id = le16_to_cpu(ox_id);
 
-	sid = p->s_id[0] | (p->s_id[1] << 8) | (p->s_id[2] << 16);
+	sid = s_id[0] | (s_id[1] << 8) | (s_id[2] << 16);
 
-	totlen = (le16_to_cpu(p->frame_size) & 0x0fff) - PURX_ELS_HEADER_SIZE;
-	if (le16_to_cpu(p->status_flags) & 0x8000) {
-		totlen = le16_to_cpu(p->trunc_frame_size);
+	totlen = (le16_to_cpu(frame_size) & 0x0fff) - PURX_ELS_HEADER_SIZE;
+	if (le16_to_cpu(status_flags) & 0x8000) {
+		totlen = le16_to_cpu(trunc_frame_size);
 		qla_els_reject_iocb(vha, (*rsp)->qpair, &a);
 		__qla_consume_iocb(vha, pkt, rsp);
 		return;
@@ -2600,12 +2629,12 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp)
 	purex = &ptr->u.purexinfo;
 	purex->pur_info.pur_sid = a.did;
 	purex->pur_info.pur_bytes_rcvd = totlen;
-	purex->pur_info.pur_rx_xchg_address = le32_to_cpu(p->rx_xchg_addr);
-	purex->pur_info.pur_nphdl = le16_to_cpu(p->nport_handle);
-	purex->pur_info.pur_did.b.domain =  p->d_id[2];
-	purex->pur_info.pur_did.b.area =  p->d_id[1];
-	purex->pur_info.pur_did.b.al_pa =  p->d_id[0];
-	purex->pur_info.vp_idx = p->vp_idx;
+	purex->pur_info.pur_rx_xchg_address = le32_to_cpu(rx_xchg_addr);
+	purex->pur_info.pur_nphdl = le16_to_cpu(nport_handle);
+	purex->pur_info.pur_did.b.domain =  d_id[2];
+	purex->pur_info.pur_did.b.area =  d_id[1];
+	purex->pur_info.pur_did.b.al_pa =  d_id[0];
+	purex->pur_info.vp_idx = vp_idx;
 
 	a.sid = purex->pur_info.pur_did;
 
@@ -2989,6 +3018,21 @@ qla28xx_start_scsi_edif(srb_t *sp)
 	struct req_que *req = sp->qpair->req;
 	spinlock_t *lock = sp->qpair->qp_lock_ptr;
 
+	/*
+	 * EDIF on 29xx is not supported by this driver yet.  The EDIF fast
+	 * path builds 64-byte cmd_type_6 IOCBs and advances req->ring_ptr
+	 * with a 64-byte stride, which would corrupt the 128-byte extended
+	 * request ring that 29xx hardware uses.  A proper 29xx EDIF port
+	 * requires a cmd_type_6_ext-shaped submission path; until that is
+	 * implemented, refuse the command rather than risk ring corruption.
+	 */
+	if (IS_QLA29XX(ha)) {
+		ql_log(ql_log_warn, vha, 0x13ae,
+		    "EDIF is not supported on 29xx hardware; failing cmd sp=%p.\n",
+		    sp);
+		return QLA_FUNCTION_FAILED;
+	}
+
 	/* Setup device pointers. */
 	cmd = GET_CMD_SP(sp);
 
@@ -3129,7 +3173,8 @@ qla28xx_start_scsi_edif(srb_t *sp)
 			 * Five DSDs are available in the Continuation
 			 * Type 1 IOCB.
 			 */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(vha, req);
+			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+			    ha, req);
 			cur_dsd = cont_pkt->dsd;
 			avail_dsds = 5;
 		}
@@ -3485,12 +3530,18 @@ static void __chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
 }
 
 void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
-		srb_t *sp, struct sts_entry_24xx *sts24)
+		srb_t *sp, void *pkt)
 {
+	struct sts_entry_24xx *sts24 = pkt;
+	struct sts_entry_24xx_ext *stsext = pkt;
+	struct qla_hw_data *ha = vha->hw;
 	fc_port_t *fcport = sp->fcport;
-	/* sa_index used by this iocb */
 	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
 	uint32_t handle;
+	uint16_t sa_index;
+
+	if (!cmd)
+		return;
 
 	handle = (uint32_t)LSW(sts24->handle);
 
@@ -3498,8 +3549,12 @@ void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
 	if (cmd->sc_data_direction != DMA_FROM_DEVICE)
 		return;
 
-	return __chk_edif_rx_sa_delete_pending(vha, fcport, handle,
-	   le16_to_cpu(sts24->edif_sa_index));
+	if (IS_QLA29XX(ha))
+		sa_index = le16_to_cpu(stsext->read_sa_index);
+	else
+		sa_index = le16_to_cpu(sts24->edif_sa_index);
+
+	return __chk_edif_rx_sa_delete_pending(vha, fcport, handle, sa_index);
 }
 
 void qlt_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, fc_port_t *fcport,
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index f307beed9d29..98bc4a57b59b 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -1442,6 +1442,10 @@ struct vp_ctrl_entry_24xx {
 	uint8_t reserved_5[24];
 };
 
+/* vp_idx_map is a 128-bit (16-byte) bitmap selecting target VPs. */
+#define VP_CTRL_IDX_MAP_BITS \
+	(sizeof_field(struct vp_ctrl_entry_24xx, vp_idx_map) * 8)
+
 /*
  * Modify Virtual Port Configuration IOCB
  */
@@ -1538,7 +1542,7 @@ struct vp_rpt_id_entry_24xx {
 #define TOPO_N2N   0x4
 #define TOPO_F     0x6
 
-			uint16_t fip_flags;
+			__le16 fip_flags;
 			uint8_t rsv2[12];
 
 			uint8_t ls_rjt_vendor;
@@ -1548,13 +1552,13 @@ struct vp_rpt_id_entry_24xx {
 
 			uint8_t port_name[8];
 			uint8_t node_name[8];
-			uint16_t bbcr;
+			__le16 bbcr;
 			uint8_t reserved_5[6];
 		} f1;
 		struct _f2 { /* format 2: N2N direct connect */
 			uint8_t vpstat1_subcode;
 			uint8_t flags;
-			uint16_t fip_flags;
+			__le16 fip_flags;
 			uint8_t rsv2[12];
 
 			uint8_t ls_rjt_vendor;
@@ -1564,7 +1568,7 @@ struct vp_rpt_id_entry_24xx {
 
 			uint8_t port_name[8];
 			uint8_t node_name[8];
-			uint16_t bbcr;
+			__le16 bbcr;
 			uint8_t reserved_5[2];
 			uint8_t remote_nport_id[4];
 		} f2;
@@ -1676,6 +1680,8 @@ struct qla_flt_location {
 #define FLT_REG_VPD_SEC_27XX_2	0xD8
 #define FLT_REG_VPD_SEC_27XX_3	0xDA
 #define FLT_REG_NVME_PARAMS_27XX	0x21
+#define FLT_REG_FMB_PRI		0xDF
+#define FLT_REG_FMB_SEC		0x124
 
 /* 28xx */
 #define FLT_REG_AUX_IMG_PRI_28XX	0x125
@@ -1695,6 +1701,10 @@ struct qla_flt_location {
 #define FLT_REG_NVME_PARAMS_PRI_28XX	0x14E
 #define FLT_REG_NVME_PARAMS_SEC_28XX	0x179
 
+/* 29xx */
+#define FLT_REG_MINI_FLT		0x201
+#define FLT_REG_FW_DUMP_TMPLT		0x1A0
+
 struct qla_flt_region {
 	__le16	code;
 	uint8_t attribute;
@@ -1716,6 +1726,56 @@ struct qla_flt_header {
 #define FLT_MAX_REGIONS		0xFF
 #define FLT_REGIONS_SIZE	(FLT_REGION_SIZE * FLT_MAX_REGIONS)
 
+/* 29xx */
+#define FLT_HDR_VERSION		0x2
+
+struct qla_flt_region_header {
+	__le32	signature;
+	__le32	version;
+	__le32	length;
+	__le32	checksum;
+	__le16	region_count;
+	__le16	region_size;
+	__le32	segment_size;
+	__le32	res3;
+	__le32	res4;
+	__le32	res5;
+	__le32	res6;
+	__le32	res7;
+	__le32	res8;
+	__le32	res9;
+	__le32	res10;
+	__le32	res11;
+	__le32	res12;
+};
+
+struct qla_flt_region_data {
+	__le16	region_code;
+	__le16	reserved;
+	__le32	attribute;
+	__le32	image_length;
+	__le32	mbi_offset;
+	__le32	version;
+	__le32	card_type;
+	__le32	chip_revision;
+	__le32	res4;
+	__le32	res5;
+	__le32	res6;
+	__le32	res7;
+	__le32	res8;
+	__le32	res9;
+	__le32	res10;
+	__le32	res11;
+	__le32	res12;
+};
+
+struct qla_flash_layout {
+	struct qla_flt_region_header flt_header;
+	struct qla_flt_region_data region[];
+};
+
+#define FLT_DATA_MAX_REGIONS	0xFF
+
 /* Flash NPIV Configuration Table ********************************************/
 
 struct qla_npiv_header {
@@ -2056,7 +2116,7 @@ struct nvram_81xx {
 	 * BIT 7    = SCM Disabled if BIT is set (1)
 	 * BIT 8-15 = Unused
 	 */
-	uint16_t enhanced_features;
+	__le16	enhanced_features;
 
 	uint16_t reserved_24[4];
 
@@ -2284,4 +2344,78 @@ struct qla_fcp_prio_cfg {
 
 #define NVRAM_DUAL_FCP_NVME_FLAG_OFFSET	0x196
 
+struct qla_fmb_version {
+	uint8_t major;
+	uint8_t minor;
+	uint8_t sub;
+	uint8_t build;
+};
+
+struct qla_fmb_upd_time {
+	__le16   year;
+	uint8_t  month;
+	uint8_t  day;
+
+	uint8_t  hour;
+	uint8_t  minute;
+	uint8_t  second;
+	uint8_t  reserved;
+};
+
+struct qla_flash_memo_block {
+	__le32   signature;	/* "FMBS" */
+#define QLFC_FMB_SIG	cpu_to_le32(0x53424D46)
+	__le32   length;
+	__le32   version;
+#define QLFC_FMB_VERSION 3
+	__le32   checksum;
+	struct qla_fmb_version ffv_ver;
+	struct qla_fmb_version mbi_ver;
+	struct {
+		__le16   year;
+		uint8_t  month;
+		uint8_t  day;
+		uint8_t  reserve[4];
+	} bld_time;
+	uint8_t tool_id[4];
+	struct qla_fmb_upd_time upd_time;
+	struct qla_fmb_version  tool_version;
+};
+
+#define TIM_DEST_ADDR	0xffffffff
+#define CHUNK_SIZE	0x10000
+
+#define TIM	0
+#define ARR1	1
+#define ARR2	2
+#define ARR3	3
+#define ARR4	4
+
+#define LD_FL_HEADER_SIGNATURE	0x46434F50
+#define LD_FL_HEADER_VERSION	0x01
+#define LD_FL_HEADER_SIZE	(0x14 * 4)
+
+struct fcop_header {
+	uint32_t signature;
+	uint32_t header_length;
+	uint32_t header_version;
+	uint32_t segment_size;
+	uint32_t tim_length;
+	uint32_t fc_major_version;
+	uint32_t fc_minor_version;
+	uint32_t fc_subminor_version;
+	uint32_t array1_length;
+	uint32_t array1_destination_addr;
+	uint32_t array2_length;
+	uint32_t array2_destination_addr;
+	uint32_t array3_length;
+	uint32_t array4_length;
+	uint32_t attribute;
+	uint32_t extended_attribute;
+	uint32_t reserved0;
+	uint32_t reserved1;
+	uint32_t reserved2;
+	uint32_t image_checksum;
+};
+
 #endif
diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h
new file mode 100644
index 000000000000..6382a054310a
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_fw29.h
@@ -0,0 +1,830 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c)  2026- Marvell.
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#ifndef __QLA_FW29_H
+#define __QLA_FW29_H
+
+#include "qla_fw.h"
+
+/* Control Flags 2 common for cmd6 and 7 */
+#define CF2_VMID_ENABLE			BIT_0
+#define CF2_CSCTL_PRIORITY_TAG		BIT_1
+#define CF2_NO_TRNF_READY_ENABLE	BIT_2
+#define CF2_RX_ID_ENABLE		BIT_3
+
+/*
+ * vp_index layout for 29xx extended command IOCBs
+ * (cmd_type_6_ext, cmd_type_7_ext, cmd_type_crc_2_ext, ...):
+ *   bits [8:0]   - VP index (9 bits)
+ *   bits [15:9]  - reserved, must be zero
+ * Access on a host-endian value via le16_to_cpu(vp_index) & CMD_EXT_VP_INDEX_MASK.
+ */
+#define CMD_EXT_VP_INDEX_MASK		0x01ff
+
+/*
+ * Combined vp_index/sof_type field layout (used by ELS and ABTS ext IOCBs):
+ *   bits [8:0]   - VP index (9 bits)
+ *   bits [11:9]  - reserved
+ *   bits [15:12] - SOF type (4 bits)
+ */
+#define EXT_VP_SOF_VP_INDEX_MASK	0x01ff
+#define EXT_VP_SOF_SOF_TYPE_SHIFT	12
+#define EXT_VP_SOF_SOF_TYPE_MASK	0xf000
+
+static inline u16 qla_ext_get_vp_index(__le16 vp_sof)
+{
+	return le16_to_cpu(vp_sof) & EXT_VP_SOF_VP_INDEX_MASK;
+}
+
+static inline u16 qla_ext_get_sof_type(__le16 vp_sof)
+{
+	return (le16_to_cpu(vp_sof) >> EXT_VP_SOF_SOF_TYPE_SHIFT) & 0xf;
+}
+
+static inline __le16 qla_ext_build_vp_sof(u16 vp_idx, u16 sof_type)
+{
+	return cpu_to_le16((vp_idx & EXT_VP_SOF_VP_INDEX_MASK) |
+			   ((sof_type & 0xf) << EXT_VP_SOF_SOF_TYPE_SHIFT));
+}
+
+/*
+ * Combined vp_idx/vp_status field layout (vp_rpt_id_entry_24xx_ext):
+ *   bits [8:0]   - VP index (9 bits)
+ *   bits [15:9]  - VP status (7 bits)
+ */
+#define EXT_VP_STATUS_VP_INDEX_MASK	0x01ff
+#define EXT_VP_STATUS_VP_STATUS_SHIFT	9
+#define EXT_VP_STATUS_VP_STATUS_MASK	0xfe00
+
+/*
+ * ISP queue - command entry structure definition.
+ */
+#define NUM_CMD67_DSDS	4
+struct cmd_type_6_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	nport_handle;		/* N_PORT handle. */
+	__le16	timeout;		/* Command timeout. */
+
+	__le16	dseg_count;		/* Data segment count. */
+
+	__le16	fcp_rsp_dsd_len;	/* FCP_RSP DSD length. */
+
+	struct scsi_lun lun;		/* FCP LUN (BE). */
+
+	__le16	control_flags;		/* Control flags. */
+
+	__le16	fcp_cmnd_dseg_len;	/* Data segment length. */
+					/* Data segment address. */
+	__le64	 fcp_cmnd_dseg_address __packed;
+					/* Data segment address. */
+	__le64	 fcp_rsp_dseg_address __packed;
+
+	__le32	byte_count;		/* Total byte count. */
+	__le16	control_flags_2;		/* Control flags 2. */
+
+	__le16	vp_index;		/* VP Index 9bits*/
+	__le32	fburstlen_rxid;		/* First Burst length/RX ID */
+	__le16 io_tag;			/* I/O Tag */
+	uint8_t vl_n_fctl;		/* VL (7-4) | RSVD (3-2) | F_CTL [17] (1) | RSVD (0) */
+	uint8_t prtag_csctl;		/* Priority Tag or CS_CTL */
+	__le32	src_vm_id;		/* Source VM ID */
+	uint8_t reserved_2[16];		/* Reserved */
+	struct dsd64 dsd[NUM_CMD67_DSDS];		/* Data Segment Descriptors */
+};
+
+struct cmd_type_7_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+	uint32_t handle;		/* System handle. */
+	__le16	nport_handle;		/* N_PORT handle. */
+	__le16	timeout;		/* Command timeout. */
+
+	__le16	dseg_count;		/* Data segment count. */
+	uint16_t reserved_1;
+
+	struct scsi_lun lun;		/* FCP LUN (BE). */
+
+	__le16	task_mgmt_flags;	/* Task management flags. */
+
+	uint8_t task;
+	uint8_t crn;
+	uint8_t fcp_cdb[MAX_CMDSZ];	/* SCSI command words. */
+	__le32	byte_count;		/* Total byte count. */
+	__le16	ctrl_flags_2;		/* Control flags 2 */
+	__le16	vp_index;		/* VP Index 9bits*/
+	__le32	rx_id;			/* Receive Exchange ID */
+	__le16	io_tag;			/* I/O Tag */
+	uint8_t vl_n_fctl;		/* VL (7-4) | RSVD (3-2) | F_CTL [17] (1) | RSVD (0) */
+	uint8_t reserved_3[21];		/* Reserved */
+	struct dsd64 dsd[NUM_CMD67_DSDS];	/* Data Segment Descriptors */
+};
+
+/*
+ * Inline data-DSD capacity of the 29xx cmd_type_crc_2_ext IOCB.  Unlike
+ * cmd_type_6_ext / cmd_type_7_ext (which carry NUM_CMD67_DSDS inline DSDs),
+ * CRC_2 places the bulk of its DSDs in the separate CRC-context DMA; only
+ * a single data_dsd is carried inline in both the u.nobundling and
+ * u.bundling variants.  Use this constant wherever the IOCB-reservation
+ * calculator needs the CRC_2 ext inline capacity so it stays in sync with
+ * the firmware-facing layout below.
+ */
+#define NUM_CRC2_EXT_INLINE_DSDS	1
+
+struct cmd_type_crc_2_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	nport_handle;		/* N_PORT handle. */
+	__le16	timeout;		/* Command timeout. */
+
+	__le16	dseg_count;		/* Data segment count. */
+	__le16	fcp_rsp_dseg_len;	/* FCP_RSP DSD length. */
+
+	struct scsi_lun lun;		/* FCP LUN (BE). */
+
+	__le16	control_flags_1;		/* Control flags. */
+	__le16	fcp_cmnd_dseg_len;	/* Data segment length. */
+
+	__le64	 fcp_cmnd_dseg_address __packed;
+					/* Data segment address. */
+	__le64	 fcp_rsp_dseg_address __packed;
+
+	__le32	byte_count;		/* Total byte count. */
+
+	__le16	control_flags_2;		/* Control flags - 2 */
+	__le16	vp_index;		/* VP Index (bits [8:0]); bits [15:9] reserved.
+					 * See CMD_EXT_VP_INDEX_MASK.
+					 */
+
+	uint32_t reserved_1;
+
+	__le16	 iocb_tag; /* Unused */
+	__le16 vl_prio; /* Bit 1 - F_CTL, Bits 4-7 VL, rest are rsvd */
+
+	uint32_t reserved_2; /* 3C-3F offset */
+
+	__le32 ref_tag;
+	uint8_t ref_tag_mask[4];	/* Validation/Replacement Mask*/
+
+	__le16 app_tag;
+	uint8_t app_tag_mask[2];	/* Validation/Replacement Mask*/
+
+	__le16 blk_size;		/* Data size in bytes */
+	__le16 prot_opts;		/* Requested Data Protection Mode */
+
+	__le32 tot_byte_count;		/* Total byte count/ total data
+					 * transfer count
+					 */
+	union {
+		struct {
+			uint32_t	reserved_1; /* offset 54 */
+			uint16_t	reserved_2;
+			__le16		guard_seed; /* offset 5A */
+			struct dsd64	data_dsd[1];
+			uint32_t	reserved_5[2];
+			uint32_t	reserved_6;
+		} nobundling;
+		struct {
+			__le32	dif_byte_count;	/* Total DIF byte
+						 * count
+						 */
+			__le16	dseg_count;	/* Data segment count */
+			__le16 guard_seed;      /* Initial Guard Seed */
+			struct dsd64	data_dsd[1];
+			struct dsd64	dif_dsd;
+		} bundling;
+	} u;
+	uint8_t reserved_3[12];			/* MUST be set to 0. */
+};
+
+/*
+ * ISP queue - status entry structure definition.
+ */
+struct sts_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	comp_status;		/* Completion status. */
+	__le16	ox_id;			/* OX_ID used by the firmware. */
+
+	__le32	residual_len;		/* FW calc residual transfer length. */
+
+	union {
+		__le16 reserved_1;
+		__le16 nvme_rsp_pyld_len;
+	} u1;
+
+	__le16	state_flags;		/* State flags. */
+
+	__le16 read_sa_index;
+	__le16 wr_sa_index;
+	uint8_t	reserved_2[8];
+	uint8_t act_dif[8];
+	uint8_t exp_dif[8];
+	union {
+		struct {
+			__le32	rsp_data_len_dma;	/* FCP response data length  */
+			uint8_t reserved_3[76];
+		};
+		struct {
+			uint8_t nvme_ersp_data[32];
+			uint8_t reserved_4[48];
+		};
+		struct {
+			__le32	bid_rd_rsp_residual_count;	/* BID read rsp residual cnt */
+			__le16	retry_delay_timer;	/* Retry delay timer. */
+			__le16	scsi_status;		/* SCSI status. */
+			__le32	rsp_residual_count;	/* FCP RSP residual count. */
+			__le32	sense_len;		/* FCP SENSE length. */
+			__le32	rsp_data_len_ndma;	/* FCP response data length  */
+			uint8_t	data[60];	/* FCP rsp/sense information */
+		};
+	} u2;
+
+	/*
+	 * If DIF Error is set in comp_status, these additional fields are
+	 * defined:
+	 *
+	 * !!! NOTE: Firmware sends expected/actual DIF data in big endian
+	 * format; but all of the "data" field gets swab32-d in the beginning
+	 * of qla2900_status_entry().
+	 *
+	 * &data[10] : uint8_t report_runt_bg[2];	- computed guard
+	 * &data[12] : uint8_t actual_dif[8];		- DIF Data received
+	 * &data[20] : uint8_t expected_dif[8];		- DIF Data computed
+	 */
+};
+
+/*
+ * ISP queue - marker entry structure definition.
+ */
+struct mrk_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t handle_count;		/* Handle count. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	nport_handle;		/* N_PORT handle. */
+
+	uint8_t modifier;		/* Modifier (7-0). */
+	uint8_t reserved_1;
+
+	__le16	vp_index;	/* VP Index. 9bits*/
+	uint16_t reserved_3;
+
+	uint8_t lun[8];			/* FCP LUN (BE). */
+	uint8_t reserved_4[104];
+};
+
+/*
+ * ISP queue - CT Pass-Through entry structure definition.
+ */
+#define NUM_CT_DSDS	5
+struct ct_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System Defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	comp_status;		/* Completion status. */
+
+	__le16	nport_handle;		/* N_PORT handle. */
+
+	__le16	cmd_dsd_count;
+
+	__le16	vp_index;		/* vp index 9 bits*/
+
+	__le16	timeout;		/* Command timeout. */
+	uint16_t reserved_2;
+
+	__le16	rsp_dsd_count;
+
+	uint8_t reserved_3[10];
+	uint8_t reserved_4[28];		/* Reserved. */
+
+	__le32	rsp_byte_count;
+	__le32	cmd_byte_count;
+	struct dsd64 dsd[NUM_CT_DSDS];	/* Data Segment Descriptors */
+};
+
+/*
+ * 29xx extended Link Service pass-through request IOCB (128 bytes).
+ *
+ * Same wire purpose as the 64-byte struct pt_ls4_request used on 24xx-class
+ * adapters, but laid out for the 128-byte 29xx request ring:
+ *   - vp_index widened to __le16 (bits [8:0] meaningful, see
+ *     CMD_EXT_VP_INDEX_MASK).
+ *   - reserved area expanded to 32 bytes between exchange_address and
+ *     rx_byte_count.
+ *   - inline DSD capacity grown from 2 to 5.
+ * Header through 'tx_dseg_count' (offset 14) and the control_flags /
+ * exchange_address fields keep the same offsets as struct pt_ls4_request,
+ * so common code can populate them via either type once IS_QLA29XX(ha) is
+ * branched for the layout-divergent fields.
+ */
+#define NUM_PT_LS4_EXT_DSDS	5
+struct pt_ls4_request_ext {
+	uint8_t entry_type;
+	uint8_t entry_count;
+	uint8_t sys_define;
+	uint8_t entry_status;
+	uint32_t handle;
+	__le16	status;
+	__le16	nport_handle;
+	__le16	tx_dseg_count;
+	__le16	vp_index;	/* VP Index 9 bits; see CMD_EXT_VP_INDEX_MASK */
+	__le16	timeout;
+	__le16	control_flags;	/* CF_LS4_* (see struct pt_ls4_request) */
+	__le16	rx_dseg_count;
+	__le16	rsvd2;
+	__le32	exchange_address;
+	uint8_t rsvd3[32];
+	__le32	rx_byte_count;
+	__le32	tx_byte_count;
+	struct dsd64 dsd[NUM_PT_LS4_EXT_DSDS];
+};
+
+/*
+ * ISP queue - PUREX IOCB entry structure definition
+ */
+struct purex_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	__le16	reserved1;
+	__le16	vp_idx;			/* VP index 9 bits*/
+
+	__le16	status_flags;
+	__le16	nport_handle;
+
+	__le16	frame_size;
+	__le16	trunc_frame_size;
+
+	__le32	rx_xchg_addr;
+
+	uint8_t d_id[3];
+	uint8_t r_ctl;
+
+	uint8_t s_id[3];
+	uint8_t cs_ctl;
+
+	uint8_t f_ctl[3];
+	uint8_t type;
+
+	__le16	seq_cnt;
+	uint8_t df_ctl;
+	uint8_t seq_id;
+
+	__le16	rx_id;
+	__le16	ox_id;
+	__le32	param;
+
+	uint8_t els_frame_payload[84];
+};
+
+/*
+ * ISP queue - ELS Pass-Through entry structure definition.
+ * ELS_EXT_EST_SOFI*: 4-bit sof_type for extended IOCBs (qla_fw.h EST_SOFI*
+ * is for els_entry_24xx byte layout).
+ */
+#define ELS_EXT_EST_SOFI3	(1 << 1)
+#define ELS_EXT_EST_SOFI2	(3 << 3)
+
+struct els_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System Defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	comp_status;		/* response only */
+	__le16	nport_handle;
+
+	__le16	tx_dsd_count;
+
+	__le16	vp_index_sof;		/* bits [8:0]=VP index, [15:12]=SOF type */
+
+	__le32	rx_xchg_address;	/* Receive exchange address. */
+	__le16	rx_dsd_count;
+
+	uint8_t opcode;
+	uint8_t reserved_2;
+
+	uint8_t d_id[3];
+	uint8_t s_id[3];
+
+	__le16	control_flags;		/* Control flags. */
+
+	union {
+		struct {
+			__le32	 rx_byte_count;
+			__le32	 tx_byte_count;
+
+			__le64	 tx_address __packed;	/* DSD 0 address. */
+			__le32	 tx_len;		/* DSD 0 length. */
+
+			__le64	 rx_address __packed;	/* DSD 1 address. */
+			__le32	 rx_len;		/* DSD 1 length. */
+		};
+		struct {
+			__le32	total_byte_count;
+			__le32	error_subcode_1;
+			__le32	error_subcode_2;
+			__le32	error_subcode_3;
+			uint8_t reserved_3[16];
+		};
+	};
+	uint8_t reserved_4[64];
+};
+
+struct els_sts_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System Defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	__le32	handle;		/* System handle. */
+
+	__le16	comp_status;
+
+	__le16	nport_handle;		/* N_PORT handle. */
+
+	__le16	reserved_1;
+
+	__le16	vp_index_sof;		/* bits [8:0]=VP index, [15:12]=SOF type */
+
+	__le32	rx_xchg_address;	/* Receive exchange address. */
+	__le16	reserved_2;
+
+	uint8_t opcode;
+	uint8_t reserved_3;
+
+	uint8_t d_id[3];
+	uint8_t s_id[3];
+
+	__le16	control_flags;		/* Control flags. */
+	__le32	total_byte_count;
+	__le32	error_subcode_1;
+	__le32	error_subcode_2;
+	__le32	error_subcode_3;
+
+	uint8_t	reserved_4[80];
+};
+
+struct logio_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	comp_status;		/* Completion status. */
+
+	__le16	nport_handle;		/* N_PORT handle. */
+
+	__le16	control_flags;		/* Control flags. */
+
+	__le16	vp_index;		/* VP Index 9bits*/
+
+	uint8_t port_id[3];		/* PortID of destination port. */
+
+	uint8_t rsp_size;		/* Response size in 32bit words. */
+
+	__le32	io_parameter[11];	/* General I/O parameters. */
+	uint8_t reserved_2[64];		/* Reserved*/
+};
+
+struct tsk_mgmt_entry_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t handle_count;		/* Handle count. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	nport_handle;		/* N_PORT handle. */
+
+	__le16	reserved_1;
+
+	__le16	delay;			/* Activity delay in seconds. */
+
+	__le16	timeout;		/* Command timeout. */
+
+	struct scsi_lun lun;		/* FCP LUN (BE). */
+
+	__le32	control_flags;		/* Control Flags. */
+
+	__le16	vp_index;	/* VP Index 9bits */
+
+	uint8_t reserved_3[98];
+};
+
+struct abort_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t handle_count;		/* Handle count. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	union {
+		__le16 nport_handle;            /* N_PORT handle. */
+		__le16 comp_status;             /* Completion status. */
+	};
+
+	__le16	options;		/* Options. */
+
+	uint32_t handle_to_abort;	/* System handle to abort. */
+
+	__le16	req_que_no;
+
+	__le16	vp_index;		/* VP Index 9bits*/
+	u8	reserved_2[4];
+	union {
+		struct {
+			__le16 abts_rty_cnt;
+			__le16 rsp_timeout;
+		} drv;
+		struct {
+			u8	ba_rjt_vendorUnique;
+			u8	ba_rjt_reasonCodeExpl;
+			u8	ba_rjt_reasonCode;
+			u8	reserved_3;
+		} fw;
+	};
+	u8	reserved_4[100];
+};
+
+struct abts_entry_24xx_ext {
+	uint8_t entry_type;
+	uint8_t entry_count;
+	uint8_t handle_count;
+	uint8_t entry_status;
+
+	__le32	handle;		/* type 0x55 only */
+
+	__le16	comp_status;		/* type 0x55 only */
+	__le16	nport_handle;		/* type 0x54 only */
+
+	__le16	control_flags;		/* type 0x55 only */
+	__le16	vp_idx_sof;		/* bits [8:0]=VP index, [15:12]=SOF type */
+
+	__le32	rx_xch_addr;
+
+	uint8_t d_id[3];
+	uint8_t r_ctl;
+
+	uint8_t s_id[3];
+	uint8_t cs_ctl;
+
+	uint8_t f_ctl[3];
+	uint8_t type;
+
+	__le16	seq_cnt;
+	uint8_t df_ctl;
+	uint8_t seq_id;
+
+	__le16	rx_id;
+	__le16	ox_id;
+
+	__le32	param;
+
+	union {
+		struct {
+			__le32	subcode3;
+			__le32	rsvd;
+			__le32	subcode1;
+			__le32	subcode2;
+		} error;
+		struct {
+			__le16	rsrvd1;
+			uint8_t last_seq_id;
+			uint8_t seq_id_valid;
+			__le16	aborted_rx_id;
+			__le16	aborted_ox_id;
+			__le16	high_seq_cnt;
+			__le16	low_seq_cnt;
+		} ba_acc;
+		struct {
+			uint8_t vendor_unique;
+			uint8_t explanation;
+			uint8_t reason;
+		} ba_rjt;
+	} payload;
+
+	__le32	rx_xch_addr_to_abort;
+	uint8_t reserved_2[64];
+} __packed;
+/*
+ * Virtual Port Control IOCB
+ */
+struct vp_ctrl_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	vp_idx_failed;
+
+	__le16	comp_status;		/* Completion status. */
+
+	__le16	command;
+
+	__le16	vp_count;
+
+	uint8_t vp_idx_map[16];
+	__le16	flags;
+	__le16	id;
+	uint16_t reserved_4;
+	__le16	hopct;
+	uint8_t reserved_5[88];
+};
+
+/*
+ * Modify Virtual Port Configuration IOCB
+ */
+struct vp_config_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t handle_count;
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+
+	__le16	flags;
+
+	__le16	comp_status;		/* Completion status. */
+
+	uint8_t command;
+
+	uint8_t vp_count;
+
+	uint8_t vp_index1;
+	uint8_t vp_index2;
+
+	uint8_t options_idx1;
+	uint8_t hard_address_idx1;
+	uint16_t reserved_vp1;
+	uint8_t port_name_idx1[WWN_SIZE];
+	uint8_t node_name_idx1[WWN_SIZE];
+
+	uint8_t options_idx2;
+	uint8_t hard_address_idx2;
+	uint16_t reserved_vp2;
+	uint8_t port_name_idx2[WWN_SIZE];
+	uint8_t node_name_idx2[WWN_SIZE];
+	__le16	id;
+	uint16_t reserved_4;
+	__le16	hopct;
+	uint8_t reserved_5[66];
+};
+
+struct vp_rpt_id_entry_24xx_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+	__le32 resv1;
+	uint8_t vp_acquired;
+	uint8_t vp_setup;
+	__le16	vp_idx_status;		/* bits [8:0]=VP index, [15:9]=VP status */
+
+	uint8_t port_id[3];
+	uint8_t format;
+	union {
+		struct vp_rpt_id_ext_f1 {
+			/* format 1 fabric */
+			uint8_t vpstat1_subcode; /* vp_status=1 subcode */
+			uint8_t flags;
+
+			__le16 fip_flags;
+			uint8_t rsv2[12];
+
+			uint8_t ls_rjt_vendor;
+			uint8_t ls_rjt_explanation;
+			uint8_t ls_rjt_reason;
+			uint8_t rsv3;
+			__le16	rsv8;
+			__le16	flogi_acc_payload_size;	/* bits [8:0] meaningful */
+			uint8_t port_name[8];
+			uint8_t node_name[8];
+			__le16 bbcr;
+			uint8_t reserved_5[6];
+		} f1;
+		struct vp_rpt_id_ext_f2 { /* format 2: N2N direct connect */
+			uint8_t vpstat1_subcode;
+			uint8_t flags;
+			__le16 fip_flags;
+			uint8_t rsv2[12];
+
+			uint8_t ls_rjt_vendor;
+			uint8_t ls_rjt_explanation;
+			uint8_t ls_rjt_reason;
+			uint8_t rsv3[5];
+
+			uint8_t port_name[8];
+			uint8_t node_name[8];
+			__le16 bbcr;
+			uint8_t reserved_5[2];
+			uint8_t remote_nport_id[4];
+		} f2;
+	} u;
+	uint8_t reserved_end[64];
+};
+
+/*
+ * ISP queue - 64-Bit addressing, continuation entry structure definition
+ * for the 29xx extended (128-byte) IOCB ring.  Mirrors cont_a64_entry_t
+ * but carries 10 DSDs per entry instead of 5.
+ */
+#define NUM_CONT1_DSDS	10
+struct cont_a64_entry_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+	uint32_t reserved;
+	struct dsd64 dsd[NUM_CONT1_DSDS];
+};
+
+/*
+ * 29xx extended Command Type FC-NVMe IOCB (128 bytes).
+ *
+ * The header layout up through 'byte_count' (offset 48) is identical to the
+ * 64-byte struct cmd_nvme used by 24xx-class adapters, so common code can
+ * populate those fields via either type.  Fields beyond 'byte_count' diverge:
+ * 29xx adds control_flags_2/vp_index/first_burst_rx_id/io_tag/..., drops
+ * port_id[3]+vp_index(byte), and carries NUM_NVME_DSDS inline DSDs.
+ */
+#define NUM_NVME_DSDS	4
+struct cmd_nvme_ext {
+	uint8_t entry_type;		/* Entry type. */
+	uint8_t entry_count;		/* Entry count. */
+	uint8_t sys_define;		/* System defined. */
+	uint8_t entry_status;		/* Entry Status. */
+
+	uint32_t handle;		/* System handle. */
+	__le16	nport_handle;		/* N_PORT handle. */
+	__le16	timeout;		/* Command timeout. */
+
+	__le16	dseg_count;		/* Data segment count. */
+	__le16	nvme_rsp_dsd_len;	/* NVMe RSP DSD length */
+
+	uint64_t rsvd;
+
+	__le16	control_flags;		/* Control Flags (see struct cmd_nvme) */
+	__le16	nvme_cmnd_dseg_len;			/* Data segment length. */
+
+	__le64	 nvme_cmnd_dseg_address __packed;	/* Data segment address. */
+
+	__le64	 nvme_rsp_dseg_address __packed;	/* Data segment address. */
+
+	__le32	byte_count;		/* Total byte count. */
+
+	__le16	control_flags_2;
+	/*
+	 * vp_index layout matches the other 29xx extended IOCBs: only bits
+	 * [8:0] are meaningful (see CMD_EXT_VP_INDEX_MASK).
+	 */
+	__le16	vp_index;
+	__le32	first_burst_rx_id;
+	__le16	io_tag;
+	uint8_t vl_n_fctl;	/* VL(7:4) | RSVD(3:2) | F_CTL[17](1) | RSVD(0) */
+	uint8_t prtag_csctl;	/* Priority Tag or CS_CTL */
+	__le32	src_vm_id;	/* Source VM ID */
+	uint8_t reserved_2[16];
+
+	struct dsd64 nvme_dsd[NUM_NVME_DSDS];
+};
+
+#endif
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 9e328c235e39..cb48b9e15a44 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -38,6 +38,7 @@ extern void qla24xx_update_fw_options(scsi_qla_host_t *);
 extern int qla2x00_load_risc(struct scsi_qla_host *, uint32_t *);
 extern int qla24xx_load_risc(scsi_qla_host_t *, uint32_t *);
 extern int qla81xx_load_risc(scsi_qla_host_t *, uint32_t *);
+extern int qla29xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr);
 
 extern int qla2x00_perform_loop_resync(scsi_qla_host_t *);
 extern int qla2x00_loop_resync(scsi_qla_host_t *);
@@ -135,7 +136,7 @@ void qla_edif_sadb_release(struct qla_hw_data *ha);
 int qla_edif_sadb_build_free_pool(struct qla_hw_data *ha);
 void qla_edif_sadb_release_free_pool(struct qla_hw_data *ha);
 void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
-		srb_t *sp, struct sts_entry_24xx *sts24);
+		srb_t *sp, void *pkt);
 void qlt_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, fc_port_t *fcport,
 		struct ctio7_from_24xx *ctio);
 void qla2x00_release_all_sadb(struct scsi_qla_host *vha, struct fc_port *fcport);
@@ -265,8 +266,7 @@ extern int qla24xx_modify_vp_config(scsi_qla_host_t *);
 extern int qla2x00_send_change_request(scsi_qla_host_t *, uint16_t, uint16_t);
 extern void qla2x00_vp_stop_timer(scsi_qla_host_t *);
 extern int qla24xx_configure_vhba (scsi_qla_host_t *);
-extern void qla24xx_report_id_acquisition(scsi_qla_host_t *,
-    struct vp_rpt_id_entry_24xx *);
+extern void qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt);
 extern void qla2x00_do_dpc_all_vps(scsi_qla_host_t *);
 extern int qla24xx_vport_create_req_sanity_check(struct fc_vport *);
 extern scsi_qla_host_t *qla24xx_create_vhost(struct fc_vport *);
@@ -294,9 +294,11 @@ void qla_adjust_buf(struct scsi_qla_host *);
  * Global Function Prototypes in qla_iocb.c source file.
  */
 void qla_els_pt_iocb(struct scsi_qla_host *vha,
-	struct els_entry_24xx *pkt, struct qla_els_pt_arg *a);
+	void *pkt, struct qla_els_pt_arg *a);
 cont_a64_entry_t *qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha,
-		struct req_que *que);
+		struct qla_hw_data *ha, struct req_que *que);
+struct cont_a64_entry_ext *qla2900_prep_cont_type1_iocb(scsi_qla_host_t *vha,
+		struct req_que *req);
 extern uint16_t qla2x00_calc_iocbs_32(uint16_t);
 extern uint16_t qla2x00_calc_iocbs_64(uint16_t);
 extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t);
@@ -336,7 +338,7 @@ void qla2x00_els_dcmd2_iocb_timeout(void *data);
  * Global Function Prototypes in qla_mbx.c source file.
  */
 extern int
-qla2x00_load_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t);
+qla2x00_load_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t, int);
 
 extern int
 qla2x00_dump_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t);
@@ -580,6 +582,12 @@ extern int qla2xxx_read_remote_register(scsi_qla_host_t *, uint32_t,
 extern int qla2xxx_write_remote_register(scsi_qla_host_t *, uint32_t,
     uint32_t);
 void qla_no_op_mb(struct scsi_qla_host *vha);
+extern int qla29xx_flash_block_read(scsi_qla_host_t *vha, dma_addr_t req_dma,
+				    uint32_t flash_addr, uint32_t flash_size,
+				    uint16_t reg_code, uint16_t opt);
+extern int qla29xx_flash_block_write(scsi_qla_host_t *vha, dma_addr_t req_dma,
+				     uint32_t flash_addr, uint32_t flash_size,
+				     uint16_t reg_code, uint16_t opt);
 
 /*
  * Global Function Prototypes in qla_isr.c source file.
@@ -682,7 +690,19 @@ struct purex_item *qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha,
 	void **pkt, struct rsp_que **rsp, bool is_purls, bool byte_order);
 int qla_mailbox_passthru(scsi_qla_host_t *vha, uint16_t *mbx_in,
 			 uint16_t *mbx_out);
-
+void *qla29xx_read_optrom_data(struct scsi_qla_host *vha,
+				       uint16_t reg_code, uint16_t opts,
+				       void *buf, uint32_t offset,
+				       uint32_t length);
+extern int qla29xx_write_optrom_data(struct scsi_qla_host *vha,
+				     uint16_t reg_code, uint16_t opts,
+				     void *buf, uint32_t offset,
+				     uint32_t length);
+extern int qla29xx_mpi_optrom_data(struct scsi_qla_host *vha, uint16_t opts,
+				   void *buf, uint32_t offset, uint32_t length,
+				   enum qla29xx_mpi_optrom_op op);
+extern int qla29xx_load_dump_mpi(scsi_qla_host_t *vha, uint16_t opt,
+				 uint32_t mpi_addr, uint32_t dlen, dma_addr_t req_dma);
 /*
  * Global Function Prototypes in qla_dbg.c source file.
  */
@@ -968,10 +988,8 @@ extern void qla24xx_process_purex_list(struct purex_list *);
 extern void qla2x00_dfs_create_rport(scsi_qla_host_t *vha, struct fc_port *fp);
 extern void qla2x00_dfs_remove_rport(scsi_qla_host_t *vha, struct fc_port *fp);
 extern void qla_wait_nvme_release_cmd_kref(srb_t *sp);
-extern void qla_nvme_abort_set_option
-		(struct abort_entry_24xx *abt, srb_t *sp);
-extern void qla_nvme_abort_process_comp_status
-		(struct abort_entry_24xx *abt, srb_t *sp);
+extern void qla_nvme_abort_set_option(void *pkt, srb_t *sp);
+extern void qla_nvme_abort_process_comp_status(void *pkt, srb_t *sp);
 struct scsi_qla_host *qla_find_host_by_vp_idx(struct scsi_qla_host *vha,
 	uint16_t vp_idx);
 
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 880cd73feaca..20b1aef455c4 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -68,30 +68,62 @@ void *
 qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg)
 {
 	struct qla_hw_data *ha = vha->hw;
-	struct ct_entry_24xx *ct_pkt;
 
-	ct_pkt = (struct ct_entry_24xx *)arg->iocb;
-	memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+	if (IS_QLA29XX(ha)) {
+		struct ct_entry_24xx_ext *ct_pkt;
 
-	ct_pkt->entry_type = CT_IOCB_TYPE;
-	ct_pkt->entry_count = 1;
-	ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle);
-	ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
-	ct_pkt->cmd_dsd_count = cpu_to_le16(1);
-	ct_pkt->rsp_dsd_count = cpu_to_le16(1);
-	ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size);
-	ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size);
+		ct_pkt = (struct ct_entry_24xx_ext *)arg->iocb;
+		memset(ct_pkt, 0, sizeof(struct ct_entry_24xx_ext));
 
-	put_unaligned_le64(arg->req_dma, &ct_pkt->dsd[0].address);
-	ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
+		ct_pkt->entry_type = CT_IOCB_TYPE;
+		ct_pkt->entry_count = 1;
+		ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle);
+		ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
+		ct_pkt->cmd_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size);
+		ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size);
 
-	put_unaligned_le64(arg->rsp_dma, &ct_pkt->dsd[1].address);
-	ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
-	ct_pkt->vp_index = vha->vp_idx;
+		put_unaligned_le64(arg->req_dma,
+				   &ct_pkt->dsd[0].address);
+		ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
 
-	vha->qla_stats.control_requests++;
+		put_unaligned_le64(arg->rsp_dma,
+				   &ct_pkt->dsd[1].address);
+		ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
+		ct_pkt->vp_index = cpu_to_le16(vha->vp_idx);
+
+		vha->qla_stats.control_requests++;
+
+		return ct_pkt;
+	} else {
+		struct ct_entry_24xx *ct_pkt;
+
+		ct_pkt = (struct ct_entry_24xx *)arg->iocb;
+		memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+
+		ct_pkt->entry_type = CT_IOCB_TYPE;
+		ct_pkt->entry_count = 1;
+		ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle);
+		ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
+		ct_pkt->cmd_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size);
+		ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size);
+
+		put_unaligned_le64(arg->req_dma,
+				   &ct_pkt->dsd[0].address);
+		ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
+
+		put_unaligned_le64(arg->rsp_dma,
+				   &ct_pkt->dsd[1].address);
+		ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
+		ct_pkt->vp_index = vha->vp_idx;
 
-	return (ct_pkt);
+		vha->qla_stats.control_requests++;
+
+		return ct_pkt;
+	}
 }
 
 /**
@@ -132,7 +164,10 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
 		    routine, ms_pkt->entry_status, vha->d_id.b.domain,
 		    vha->d_id.b.area, vha->d_id.b.al_pa);
 	} else {
-		if (IS_FWI2_CAPABLE(ha))
+		if (IS_QLA29XX(ha))
+			comp_status = le16_to_cpu(
+			    ((struct ct_entry_24xx_ext *)ms_pkt)->comp_status);
+		else if (IS_FWI2_CAPABLE(ha))
 			comp_status = le16_to_cpu(
 			    ((struct ct_entry_24xx *)ms_pkt)->comp_status);
 		else
@@ -157,8 +192,8 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
 			break;
 		case CS_PORT_LOGGED_OUT:
 			if (IS_FWI2_CAPABLE(ha)) {
-				if (le16_to_cpu(ms_pkt->loop_id.extended) ==
-				    NPH_SNS)
+				if (le16_to_cpu(((struct ct_entry_24xx *)
+				    ms_pkt)->nport_handle) == NPH_SNS)
 					lid_is_sns = true;
 			} else {
 				if (le16_to_cpu(ms_pkt->loop_id.extended) ==
@@ -1437,42 +1472,85 @@ void *
 qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size,
     uint32_t rsp_size)
 {
-	struct ct_entry_24xx *ct_pkt;
 	struct qla_hw_data *ha = vha->hw;
 
-	ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
-	memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+	if (IS_QLA29XX(ha)) {
+		struct ct_entry_24xx_ext *ct_pkt;
+
+		ct_pkt = (struct ct_entry_24xx_ext *)ha->ms_iocb;
+		memset(ct_pkt, 0, sizeof(struct ct_entry_24xx_ext));
+
+		ct_pkt->entry_type = CT_IOCB_TYPE;
+		ct_pkt->entry_count = 1;
+		ct_pkt->nport_handle =
+		    cpu_to_le16(vha->mgmt_svr_loop_id);
+		ct_pkt->timeout =
+		    cpu_to_le16(ha->r_a_tov / 10 * 2);
+		ct_pkt->cmd_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size);
+		ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
 
-	ct_pkt->entry_type = CT_IOCB_TYPE;
-	ct_pkt->entry_count = 1;
-	ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id);
-	ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
-	ct_pkt->cmd_dsd_count = cpu_to_le16(1);
-	ct_pkt->rsp_dsd_count = cpu_to_le16(1);
-	ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size);
-	ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
+		put_unaligned_le64(ha->ct_sns_dma,
+				   &ct_pkt->dsd[0].address);
+		ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
 
-	put_unaligned_le64(ha->ct_sns_dma, &ct_pkt->dsd[0].address);
-	ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
+		put_unaligned_le64(ha->ct_sns_dma,
+				   &ct_pkt->dsd[1].address);
+		ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
+		ct_pkt->vp_index = cpu_to_le16(vha->vp_idx);
 
-	put_unaligned_le64(ha->ct_sns_dma, &ct_pkt->dsd[1].address);
-	ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
-	ct_pkt->vp_index = vha->vp_idx;
+		return ct_pkt;
+	} else {
+		struct ct_entry_24xx *ct_pkt;
+
+		ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
+		memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+
+		ct_pkt->entry_type = CT_IOCB_TYPE;
+		ct_pkt->entry_count = 1;
+		ct_pkt->nport_handle =
+		    cpu_to_le16(vha->mgmt_svr_loop_id);
+		ct_pkt->timeout =
+		    cpu_to_le16(ha->r_a_tov / 10 * 2);
+		ct_pkt->cmd_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_dsd_count = cpu_to_le16(1);
+		ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size);
+		ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
+
+		put_unaligned_le64(ha->ct_sns_dma,
+				   &ct_pkt->dsd[0].address);
+		ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
+
+		put_unaligned_le64(ha->ct_sns_dma,
+				   &ct_pkt->dsd[1].address);
+		ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count;
+		ct_pkt->vp_index = vha->vp_idx;
 
-	return ct_pkt;
+		return ct_pkt;
+	}
 }
 
 static void
 qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size)
 {
 	struct qla_hw_data *ha = vha->hw;
-	ms_iocb_entry_t *ms_pkt = ha->ms_iocb;
-	struct ct_entry_24xx *ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
 
-	if (IS_FWI2_CAPABLE(ha)) {
+	if (IS_QLA29XX(ha)) {
+		struct ct_entry_24xx_ext *ct_pkt =
+		    (struct ct_entry_24xx_ext *)ha->ms_iocb;
+
+		ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
+		ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
+	} else if (IS_FWI2_CAPABLE(ha)) {
+		struct ct_entry_24xx *ct_pkt =
+		    (struct ct_entry_24xx *)ha->ms_iocb;
+
 		ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
 		ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count;
 	} else {
+		ms_iocb_entry_t *ms_pkt = ha->ms_iocb;
+
 		ms_pkt->req_bytecount = cpu_to_le32(req_size);
 		ms_pkt->req_dsd.length = ms_pkt->req_bytecount;
 	}
@@ -1508,12 +1586,18 @@ qla25xx_fdmi_port_speed_capability(struct qla_hw_data *ha)
 
 	if (IS_CNA_CAPABLE(ha))
 		return FDMI_PORT_SPEED_10GB;
-	if (IS_QLA28XX(ha) || IS_QLA27XX(ha)) {
-		if (ha->max_supported_speed == 2) {
+	if (IS_QLA28XX(ha) || IS_QLA27XX(ha) || IS_QLA29XX(ha)) {
+		if (ha->max_supported_speed == 3) {
+			if (ha->min_supported_speed <= 7)
+				speeds |= FDMI_PORT_SPEED_128GB;
+		}
+		if (ha->max_supported_speed == 3 ||
+		    ha->max_supported_speed == 2) {
 			if (ha->min_supported_speed <= 6)
 				speeds |= FDMI_PORT_SPEED_64GB;
 		}
-		if (ha->max_supported_speed == 2 ||
+		if (ha->max_supported_speed == 3 ||
+		    ha->max_supported_speed == 2 ||
 		    ha->max_supported_speed == 1) {
 			if (ha->min_supported_speed <= 5)
 				speeds |= FDMI_PORT_SPEED_32GB;
@@ -1577,6 +1661,8 @@ qla25xx_fdmi_port_speed_currently(struct qla_hw_data *ha)
 		return FDMI_PORT_SPEED_32GB;
 	case PORT_SPEED_64GB:
 		return FDMI_PORT_SPEED_64GB;
+	case PORT_SPEED_128GB:
+		return FDMI_PORT_SPEED_128GB;
 	default:
 		return FDMI_PORT_SPEED_UNKNOWN;
 	}
@@ -2620,6 +2706,8 @@ qla2x00_port_speed_capability(uint16_t speed)
 		return PORT_SPEED_32GB;
 	case BIT_7:
 		return PORT_SPEED_64GB;
+	case BIT_6:
+		return PORT_SPEED_128GB;
 	default:
 		return PORT_SPEED_UNKNOWN;
 	}
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index e746c9274cde..2b9a9c672ec6 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -228,7 +228,7 @@ qla2x00_async_iocb_timeout(void *data)
 	srb_t *sp = data;
 	fc_port_t *fcport = sp->fcport;
 	struct srb_iocb *lio = &sp->u.iocb_cmd;
-	int rc, h;
+	int rc, h, found;
 	unsigned long flags;
 
 	if (fcport) {
@@ -251,6 +251,7 @@ qla2x00_async_iocb_timeout(void *data)
 			lio->u.logio.data[1] =
 				lio->u.logio.flags & SRB_LOGIN_RETRIED ?
 				QLA_LOGIO_LOGIN_RETRIED : 0;
+			found = 0;
 			spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags);
 			for (h = 1; h < sp->qpair->req->num_outstanding_cmds;
 			    h++) {
@@ -258,11 +259,19 @@ qla2x00_async_iocb_timeout(void *data)
 				    sp) {
 					sp->qpair->req->outstanding_cmds[h] =
 					    NULL;
+					found = 1;
 					break;
 				}
 			}
 			spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags);
-			sp->done(sp, QLA_FUNCTION_TIMEOUT);
+			/*
+			 * Only complete the command if this path removed it
+			 * from outstanding_cmds.  Otherwise the ISR already
+			 * completed it and a second sp->done() would race the
+			 * submitter's freeing of the on-stack completion.
+			 */
+			if (found)
+				sp->done(sp, QLA_FUNCTION_TIMEOUT);
 		}
 		break;
 	case SRB_LOGOUT_CMD:
@@ -275,6 +284,7 @@ qla2x00_async_iocb_timeout(void *data)
 	default:
 		rc = qla24xx_async_abort_cmd(sp, false);
 		if (rc) {
+			found = 0;
 			spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags);
 			for (h = 1; h < sp->qpair->req->num_outstanding_cmds;
 			    h++) {
@@ -282,11 +292,19 @@ qla2x00_async_iocb_timeout(void *data)
 				    sp) {
 					sp->qpair->req->outstanding_cmds[h] =
 					    NULL;
+					found = 1;
 					break;
 				}
 			}
 			spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags);
-			sp->done(sp, QLA_FUNCTION_TIMEOUT);
+			/*
+			 * Only complete the command if this path removed it
+			 * from outstanding_cmds.  Otherwise the ISR already
+			 * completed it and a second sp->done() would race the
+			 * submitter's freeing of the on-stack completion.
+			 */
+			if (found)
+				sp->done(sp, QLA_FUNCTION_TIMEOUT);
 		}
 		break;
 	}
@@ -2773,7 +2791,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha)
 	ha->isp_ops->reset_chip(vha);
 
 	/* Check for secure flash support */
-	if (IS_QLA28XX(ha)) {
+	if (IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		if (rd_reg_word(&reg->mailbox12) & BIT_0)
 			ha->flags.secure_adapter = 1;
 		ql_log(ql_log_info, vha, 0xffff, "Secure Adapter: %s\n",
@@ -3275,47 +3293,37 @@ qla81xx_reset_mpi(scsi_qla_host_t *vha)
 	return qla81xx_write_mpi_register(vha, mb);
 }
 
-static int
-qla_chk_risc_recovery(scsi_qla_host_t *vha)
+/* save MB regs at start of day for fw dump */
+static void
+qla_save_mbregs(scsi_qla_host_t *vha)
 {
 	struct qla_hw_data *ha = vha->hw;
 	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
 	__le16 __iomem *mbptr = &reg->mailbox0;
 	int i;
-	u16 mb[32];
-	int rc = QLA_SUCCESS;
-
-	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
-		return rc;
+	u16 *mb = ha->mbregs;
 
-	/* this check is only valid after RISC reset */
-	mb[0] = rd_reg_word(mbptr);
-	mbptr++;
-	if (mb[0] == 0xf) {
-		rc = QLA_FUNCTION_FAILED;
+	if ((!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) ||
+	    vha->flags.init_done)
+		return;
 
-		for (i = 1; i < 32; i++) {
-			mb[i] = rd_reg_word(mbptr);
-			mbptr++;
-		}
-
-		ql_log(ql_log_warn, vha, 0x1015,
-		       "RISC reset failed. mb[0-7] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
-		       mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], mb[6], mb[7]);
-		ql_log(ql_log_warn, vha, 0x1015,
-		       "RISC reset failed. mb[8-15] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
-		       mb[8], mb[9], mb[10], mb[11], mb[12], mb[13], mb[14],
-		       mb[15]);
-		ql_log(ql_log_warn, vha, 0x1015,
-		       "RISC reset failed. mb[16-23] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
-		       mb[16], mb[17], mb[18], mb[19], mb[20], mb[21], mb[22],
-		       mb[23]);
-		ql_log(ql_log_warn, vha, 0x1015,
-		       "RISC reset failed. mb[24-31] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
-		       mb[24], mb[25], mb[26], mb[27], mb[28], mb[29], mb[30],
-		       mb[31]);
+	for (i = 0; i < 32; i++) {
+		mb[i] = rd_reg_word(mbptr);
+		mbptr++;
 	}
-	return rc;
+
+	ql_log(ql_log_info, vha, 0x1015,
+	    "mb[0-7] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
+	    mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], mb[6], mb[7]);
+	ql_log(ql_log_info, vha, 0x1015,
+	    "mb[8-15] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
+	    mb[8], mb[9], mb[10], mb[11], mb[12], mb[13], mb[14], mb[15]);
+	ql_log(ql_log_info, vha, 0x1015,
+	    "mb[16-23] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
+	    mb[16], mb[17], mb[18], mb[19], mb[20], mb[21], mb[22], mb[23]);
+	ql_log(ql_log_info, vha, 0x1015,
+	    "mb[24-31] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
+	    mb[24], mb[25], mb[26], mb[27], mb[28], mb[29], mb[30], mb[31]);
 }
 
 /**
@@ -3334,7 +3342,6 @@ qla24xx_reset_risc(scsi_qla_host_t *vha)
 	uint16_t wd;
 	static int abts_cnt; /* ISP abort retry counts */
 	int rval = QLA_SUCCESS;
-	int print = 1;
 
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 
@@ -3431,9 +3438,6 @@ qla24xx_reset_risc(scsi_qla_host_t *vha)
 		barrier();
 		if (cnt) {
 			mdelay(1);
-			if (print && qla_chk_risc_recovery(vha))
-				print = 0;
-
 			wd = rd_reg_word(&reg->mailbox0);
 		} else {
 			rval = QLA_FUNCTION_TIMEOUT;
@@ -3453,6 +3457,8 @@ qla24xx_reset_risc(scsi_qla_host_t *vha)
 
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
+	qla_save_mbregs(vha);
+
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x015f,
 	    "Driver in %s mode\n",
 	    IS_NOPOLLING_TYPE(ha) ? "Interrupt" : "Polling");
@@ -3733,7 +3739,7 @@ int qla2x00_alloc_fce_trace(scsi_qla_host_t *vha)
 		return -EINVAL;
 
 	if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return -EINVAL;
 
 	if (ha->fce) {
@@ -3764,11 +3770,27 @@ int qla2x00_alloc_fce_trace(scsi_qla_host_t *vha)
 
 void qla2x00_free_fce_trace(struct qla_hw_data *ha)
 {
-	if (!ha->fce)
+	void *fce;
+	dma_addr_t fce_dma;
+	unsigned long flags;
+
+	/*
+	 * Unpublish ha->fce under hardware_lock so a firmware dump in
+	 * progress (which reads ha->fce under the same lock) cannot race
+	 * with the buffer being freed.
+	 */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	if (!ha->fce) {
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
 		return;
-	dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, ha->fce_dma);
+	}
+	fce = ha->fce;
+	fce_dma = ha->fce_dma;
 	ha->fce = NULL;
 	ha->fce_dma = 0;
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	dma_free_coherent(&ha->pdev->dev, FCE_SIZE, fce, fce_dma);
 }
 
 static void
@@ -3813,16 +3835,11 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 	struct qla_hw_data *ha = vha->hw;
 	struct req_que *req = ha->req_q_map[0];
 	struct rsp_que *rsp = ha->rsp_q_map[0];
-	struct qla2xxx_fw_dump *fw_dump;
-
-	if (ha->fw_dump) {
-		ql_dbg(ql_dbg_init, vha, 0x00bd,
-		    "Firmware dump already allocated.\n");
-		return;
-	}
+	struct qla2xxx_fw_dump *fw_dump, *prev_fw_dump;
+	void *prev_mpi_fw_dump;
+	size_t req_entry_size = qla_req_entry_size(ha);
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
 
-	ha->fw_dumped = 0;
-	ha->fw_dump_cap_flags = 0;
 	dump_size = fixed_size = mem_size = eft_size = fce_size = mq_size = 0;
 	req_q_size = rsp_q_size = 0;
 
@@ -3852,9 +3869,9 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 			 * Resizing must be done at end-of-dump processing.
 			 */
 			mq_size += (ha->max_req_queues - 1) *
-			    (req->length * sizeof(request_t));
+			    (req->length * req_entry_size);
 			mq_size += (ha->max_rsp_queues - 1) *
-			    (rsp->length * sizeof(response_t));
+			    (rsp->length * rsp_entry_size);
 		}
 		if (ha->tgt.atio_ring)
 			mq_size += ha->tgt.atio_q_length * sizeof(request_t);
@@ -3868,7 +3885,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 			eft_size = EFT_SIZE;
 	}
 
-	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		struct fwdt *fwdt = ha->fwdt;
 		uint j;
 
@@ -3890,8 +3907,8 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 		/* Add space for spare MPI fw dump. */
 		dump_size += ha->fwdt[1].dump_size;
 	} else {
-		req_q_size = req->length * sizeof(request_t);
-		rsp_q_size = rsp->length * sizeof(response_t);
+		req_q_size = req->length * req_entry_size;
+		rsp_q_size = rsp->length * rsp_entry_size;
 		dump_size = offsetof(struct qla2xxx_fw_dump, isp);
 		dump_size += fixed_size + mem_size + req_q_size + rsp_q_size
 			+ eft_size;
@@ -3905,13 +3922,11 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 				ha->exlogin_size;
 	}
 
-	if (!ha->fw_dump_len || dump_size > ha->fw_dump_alloc_len) {
-
-		ql_dbg(ql_dbg_init, vha, 0x00c5,
-		    "%s dump_size %d fw_dump_len %d fw_dump_alloc_len %d\n",
-		    __func__, dump_size, ha->fw_dump_len,
-		    ha->fw_dump_alloc_len);
+	ql_dbg(ql_dbg_init, vha, 0x00c5,
+	    "%s dump_size %d fw_dump_len %d fw_dump_alloc_len %d\n",
+	    __func__, dump_size, ha->fw_dump_len, ha->fw_dump_alloc_len);
 
+	if (!ha->fw_dump_len || dump_size > ha->fw_dump_alloc_len) {
 		fw_dump = vmalloc(dump_size);
 		if (!fw_dump) {
 			ql_log(ql_log_warn, vha, 0x00c4,
@@ -3919,9 +3934,26 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 			    dump_size / 1024);
 		} else {
 			mutex_lock(&ha->optrom_mutex);
-			if (ha->fw_dumped) {
-				memcpy(fw_dump, ha->fw_dump, ha->fw_dump_len);
-				vfree(ha->fw_dump);
+
+			if (ha->fw_dumped || ha->mpi_fw_dumped) {
+				prev_fw_dump = ha->fw_dump;
+
+				if (ha->fw_dumped)
+					memcpy(fw_dump, prev_fw_dump,
+					    ha->fw_dump_len);
+
+				if (IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+				    IS_QLA29XX(ha)) {
+					prev_mpi_fw_dump = ha->mpi_fw_dump;
+					ha->mpi_fw_dump = (char *)fw_dump +
+						ha->fwdt[0].dump_size;
+
+					if (ha->mpi_fw_dumped)
+						memcpy(ha->mpi_fw_dump,
+						    prev_mpi_fw_dump,
+						    ha->mpi_fw_dump_len);
+				}
+				vfree(prev_fw_dump);
 				ha->fw_dump = fw_dump;
 				ha->fw_dump_alloc_len =  dump_size;
 				ql_dbg(ql_dbg_init, vha, 0x00c5,
@@ -3937,9 +3969,10 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
 				    "Allocated (%d KB) for firmware dump.\n",
 				    dump_size / 1024);
 
-				if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+				if (IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+				    IS_QLA29XX(ha)) {
 					ha->mpi_fw_dump = (char *)fw_dump +
-						ha->fwdt[1].dump_size;
+						ha->fwdt[0].dump_size;
 					mutex_unlock(&ha->optrom_mutex);
 					return;
 				}
@@ -4194,11 +4227,11 @@ qla24xx_detect_sfp(scsi_qla_host_t *vha)
 	used_nvram = 0;
 	ha->flags.lr_detected = 0;
 	if (IS_BPM_RANGE_CAPABLE(ha) &&
-	    (nv->enhanced_features & NEF_LR_DIST_ENABLE)) {
+	    (le16_to_cpu(nv->enhanced_features) & NEF_LR_DIST_ENABLE)) {
 		used_nvram = 1;
 		ha->flags.lr_detected = 1;
 		ha->lr_distance =
-		    (nv->enhanced_features >> LR_DIST_NV_POS)
+		    (le16_to_cpu(nv->enhanced_features) >> LR_DIST_NV_POS)
 		     & LR_DIST_NV_MASK;
 	}
 
@@ -4336,6 +4369,16 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
 
 		rval = qla2x00_verify_checksum(vha, srisc_address);
 		if (rval == QLA_SUCCESS) {
+			/*
+			 * Alloc a guestimate dump buffer to capture any failure
+			 * during early phase of driver load.
+			 */
+			if (ql2xallocfwdump &&
+			    (IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+			     IS_QLA29XX(ha)) &&
+			    !vha->flags.init_done)
+				qla2x00_alloc_fw_dump(vha);
+
 			/* Start firmware execution. */
 			ql_dbg(ql_dbg_init, vha, 0x00ca,
 			    "Starting firmware.\n");
@@ -4387,6 +4430,19 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
 					    MIN_MULTI_ID_FABRIC))
 						ha->max_npiv_vports =
 						    MIN_MULTI_ID_FABRIC - 1;
+
+					/*
+					 * The VP_CTRL IOCB selects target VPs
+					 * through the fixed vp_idx_map bitmap,
+					 * so a vp_index beyond it can be enabled
+					 * via VP_CONFIG but never disabled via
+					 * VP_CTRL, leaking the VP.  Cap the count
+					 * to the bitmap capacity.
+					 */
+					if (ha->max_npiv_vports >=
+					    VP_CTRL_IDX_MAP_BITS)
+						ha->max_npiv_vports =
+						    VP_CTRL_IDX_MAP_BITS - 1;
 				}
 				qlt_config_nvram_with_fw_version(vha);
 				qla2x00_get_resource_cnts(vha);
@@ -4478,15 +4534,33 @@ void
 qla2x00_init_response_q_entries(struct rsp_que *rsp)
 {
 	uint16_t cnt;
-	response_t *pkt;
 
 	rsp->ring_ptr = rsp->ring;
 	rsp->ring_index    = 0;
 	rsp->status_srb = NULL;
-	pkt = rsp->ring_ptr;
-	for (cnt = 0; cnt < rsp->length; cnt++) {
-		pkt->signature = RESPONSE_PROCESSED;
-		pkt++;
+
+	if (rsp->hw && IS_QLA29XX(rsp->hw)) {
+		/*
+		 * 29xx uses a 128-byte response-ring stride.  The signature
+		 * field offset matches response_t, but the entry pitch is
+		 * sizeof(struct response_ext); walk via ring_ext_ptr / struct response_ext.
+		 */
+		struct response_ext *pkt;
+
+		rsp->ring_ext_ptr = rsp->ring_ext;
+		pkt = rsp->ring_ext_ptr;
+		for (cnt = 0; cnt < rsp->length; cnt++) {
+			pkt->signature = RESPONSE_PROCESSED;
+			pkt++;
+		}
+	} else {
+		response_t *pkt;
+
+		pkt = rsp->ring_ptr;
+		for (cnt = 0; cnt < rsp->length; cnt++) {
+			pkt->signature = RESPONSE_PROCESSED;
+			pkt++;
+		}
 	}
 }
 
@@ -4772,7 +4846,14 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
 		ql_dbg(ql_dbg_init, vha, 0x00fd,
 		    "Speed set by user : %s Gbps \n",
 		    qla2x00_get_link_speed_str(ha, ha->set_data_rate));
-		icb->firmware_options_3 = cpu_to_le32(ha->set_data_rate << 13);
+		/*
+		 * The ICB data-rate field is 3 bits (bits 13-15); rates above
+		 * 64G do not fit and would overflow into bit 16 (75 ohm
+		 * termination select). Such rates are forced via MBC_DATA_RATE.
+		 */
+		if (ha->set_data_rate <= PORT_SPEED_64GB)
+			icb->firmware_options_3 =
+			    cpu_to_le32(ha->set_data_rate << 13);
 	}
 
 	/* PCI posting */
@@ -4807,7 +4888,12 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
 		req = ha->req_q_map[que];
 		if (!req || !test_bit(que, ha->req_qid_map))
 			continue;
-		req->out_ptr = (uint16_t *)(req->ring + req->length);
+		if (IS_QLA29XX(ha))
+			req->out_ptr =
+			    (uint16_t *)(req->ring_ext + req->length);
+		else
+			req->out_ptr =
+			    (uint16_t *)(req->ring + req->length);
 		*req->out_ptr = 0;
 		for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++)
 			req->outstanding_cmds[cnt] = NULL;
@@ -4818,13 +4904,19 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
 		req->ring_ptr  = req->ring;
 		req->ring_index    = 0;
 		req->cnt      = req->length;
+		if (IS_QLA29XX(ha))
+			req->ring_ext_ptr = req->ring_ext;
 	}
 
 	for (que = 0; que < ha->max_rsp_queues; que++) {
 		rsp = ha->rsp_q_map[que];
 		if (!rsp || !test_bit(que, ha->rsp_qid_map))
 			continue;
-		rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length);
+		if (IS_QLA29XX(ha))
+			rsp->in_ptr =
+			    (uint16_t *)(rsp->ring_ext + rsp->length);
+		else
+			rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length);
 		*rsp->in_ptr = 0;
 		/* Initialize response queue entries */
 		if (IS_QLAFX00(ha))
@@ -4896,6 +4988,8 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
 		ql_dbg(ql_dbg_init, vha, 0x00d3,
 		    "Init Firmware -- success.\n");
 		vha->u_ql2xexchoffld = vha->u_ql2xiniexchg = 0;
+		vha->hw->flags.t262_fail = 0;
+		vha->hw->flags.t272_fail = 0;
 	}
 
 	return (rval);
@@ -5616,6 +5710,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
 	INIT_LIST_HEAD(&fcport->gnl_entry);
 	INIT_LIST_HEAD(&fcport->list);
 	INIT_LIST_HEAD(&fcport->unsol_ctx_head);
+	spin_lock_init(&fcport->unsol_ctx_lock);
 
 	INIT_LIST_HEAD(&fcport->sess_cmd_list);
 	spin_lock_init(&fcport->sess_cmd_lock);
@@ -8669,7 +8764,8 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr,
 			for (i = 0; i < dlen; i++)
 				dcode[i] = swab32(dcode[i]);
 
-			rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen);
+			rval = qla2x00_load_ram(vha, req->dma, risc_addr,
+			    dlen, 0);
 			if (rval) {
 				ql_log(ql_log_fatal, vha, 0x008f,
 				    "-> Failed load firmware fragment %u.\n",
@@ -8839,7 +8935,7 @@ qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
 				wcode[i] = swab16((__force u32)fwcode[i]);
 
 			rval = qla2x00_load_ram(vha, req->dma, risc_addr,
-			    wlen);
+			    wlen, 0);
 			if (rval) {
 				ql_log(ql_log_fatal, vha, 0x008a,
 				    "Failed to load segment %d of firmware.\n",
@@ -8929,7 +9025,8 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr)
 			for (i = 0; i < dlen; i++)
 				dcode[i] = swab32((__force u32)fwcode[i]);
 
-			rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen);
+			rval = qla2x00_load_ram(vha, req->dma, risc_addr,
+			    dlen, 0);
 			if (rval) {
 				ql_log(ql_log_fatal, vha, 0x0098,
 				    "-> Failed load firmware fragment %u.\n",
@@ -9128,6 +9225,539 @@ qla81xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
 	return rval;
 }
 
+static int qla29xx_validate_firmware_image(scsi_qla_host_t *vha,
+					   __be32 *dword)
+{
+	if (be32_to_cpu(dword[0]) != LD_FL_HEADER_SIGNATURE) {
+		ql_dbg(ql_dbg_init, vha, 0x0093,
+		       "Firmware image Signature does not match (0x%x).\n",
+		       be32_to_cpu(dword[0]));
+		return QLA_FUNCTION_FAILED;
+	}
+	if (be32_to_cpu(dword[2]) != LD_FL_HEADER_VERSION) {
+		ql_dbg(ql_dbg_init, vha, 0x0093,
+		       "Firmware image Version does not match (0x%x).\n",
+		       be32_to_cpu(dword[2]));
+		return QLA_FUNCTION_FAILED;
+	}
+	return QLA_SUCCESS;
+}
+
+static int qla29xx_process_rd_image(struct scsi_qla_host *vha,
+				    struct fcop_header *header,
+				    __be32 *fwcode, int section,
+				    uint32_t risc_addr, uint32_t section_size)
+{
+	int rval = QLA_SUCCESS;
+	uint32_t *dcode = NULL;
+	struct qla_hw_data *ha = vha->hw;
+	struct req_que *req = ha->req_q_map[0];
+	int num_segments = 0, segment = 0;
+	int chunks_per_segment = 0, chunk = 0;
+	int is_first_chunk = 0;
+	int is_last_chunk = 0;
+	int is_first_segment = 0;
+	int is_last_segment = 0;
+	int opt = 0;
+	uint32_t size = 0;
+	uint32_t size_remainder = 0;
+	uint32_t seg_size_remainder = 0;
+	int i = 0;
+
+	dcode = (uint32_t *)req->ring;
+
+	if (section == TIM) {
+		if (section_size > req->length * qla_req_entry_size(ha)) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			    "TIM section too large (0x%x bytes, ring 0x%lx bytes).\n",
+			    section_size,
+			    req->length * qla_req_entry_size(ha));
+			return QLA_FUNCTION_FAILED;
+		}
+
+		opt = BIT_15 | BIT_2 | BIT_1 | BIT_0;
+		for (i = 0; i < (section_size >> 2); i++)
+			dcode[i] = swab32((__force u32)fwcode[i]);
+
+		ql_dbg(ql_dbg_init, vha, 0x0098,
+		       "TIM : process_rd_image [opt 0x%x]\n", opt);
+		rval = qla2x00_load_ram(vha, req->dma, risc_addr,
+					section_size >> 2, opt);
+		if (rval) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "-> Failed load TIM\n");
+			return QLA_FUNCTION_FAILED;
+		}
+	} else {
+		size_remainder = section_size;
+		num_segments += (section_size % header->segment_size == 0) ?
+			(section_size / header->segment_size) :
+			(section_size / header->segment_size) + 1;
+		chunks_per_segment =
+			(header->segment_size % CHUNK_SIZE == 0) ?
+			(header->segment_size / CHUNK_SIZE) :
+			(header->segment_size / CHUNK_SIZE) + 1;
+
+		ql_dbg(ql_dbg_init, vha, 0x0098,
+		       "num seg 0x%x chunk per seg 0x%x\n",
+		       num_segments, chunks_per_segment);
+
+		for (segment = 0; segment < num_segments; segment++) {
+			for (chunk = 0; chunk < chunks_per_segment; chunk++) {
+				is_first_chunk = (chunk == 0);
+				if (chunk == 0) {
+					if (size_remainder >=
+					    header->segment_size)
+						seg_size_remainder =
+							header->segment_size;
+					else
+						seg_size_remainder =
+							size_remainder;
+				}
+
+				is_first_segment =
+					(section == ARR1 && segment == 0);
+				is_last_segment =
+					(section == ARR2 &&
+					 (segment == (num_segments - 1)));
+				is_last_chunk =
+					(chunk == (chunks_per_segment - 1) ||
+					 size_remainder <= CHUNK_SIZE);
+
+				if (seg_size_remainder < CHUNK_SIZE)
+					size = seg_size_remainder % CHUNK_SIZE;
+				else
+					size = CHUNK_SIZE;
+
+				ql_dbg(ql_dbg_init, vha, 0x0098,
+				       "[%d]chunk 0x%x segment 0x%x first_segment 0x%x first_chunk 0x%x last_segment 0x%x last_chunk 0x%x\n",
+				       __LINE__, chunk, segment,
+				       is_first_segment, is_first_chunk,
+				       is_last_segment, is_last_chunk);
+
+				opt = BIT_2;
+				if (is_first_chunk)
+					opt |= BIT_0;
+				if (is_last_chunk)
+					opt |= BIT_1;
+				if (is_first_segment)
+					opt |= BIT_3;
+				if (is_last_segment)
+					opt |= BIT_4;
+
+				for (i = 0; i < (size >> 2); i++)
+					dcode[i] =
+					    swab32((__force u32)fwcode[i]);
+
+				ql_dbg(ql_dbg_init, vha, 0x0098,
+				       "ARR[0x%x] : opt 0x%x\n", size, opt);
+				rval = qla2x00_load_ram(vha, req->dma,
+							risc_addr,
+							size >> 2, opt);
+				if (rval) {
+					ql_log(ql_log_fatal, vha, 0x0098,
+					       "-> Failed load ram\n");
+					return QLA_FUNCTION_FAILED;
+				}
+
+				fwcode += size >> 2;
+				size_remainder -= size;
+				seg_size_remainder -= size;
+				if (size_remainder == 0)
+					break;
+			}
+			risc_addr += header->segment_size >> 2;
+		}
+	}
+	return rval;
+}
+
+static int
+qla29xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr)
+{
+	int rval;
+	struct fcop_header *header;
+	uint32_t *dcode = NULL;
+	struct fw_blob *blob;
+	__be32 *fwcode, *array1_addr, *array3_addr;
+	uint templates;
+	uint32_t array_size, risc_attr = 0;
+	ulong i;
+	uint j;
+	ulong dlen, array3_offset;
+	size_t max_dwords;
+	struct qla_hw_data *ha = vha->hw;
+	struct fwdt *fwdt = ha->fwdt;
+
+	header = vmalloc(sizeof(*header));
+	if (!header)
+		return QLA_FUNCTION_FAILED;
+
+	ql_dbg(ql_dbg_init, vha, 0x0090,
+	       "-> FW: Loading via request-firmware.\n");
+
+	blob = qla2x00_request_firmware(vha);
+	if (!blob) {
+		ql_log(ql_log_warn, vha, 0x0092,
+		       "-> Firmware file not found.\n");
+		vfree(header);
+		return QLA_FUNCTION_FAILED;
+	}
+
+	fwcode = (__force __be32 *)blob->fw->data;
+
+	if (blob->fw->size < sizeof(*header)) {
+		ql_log(ql_log_fatal, vha, 0x0093,
+		       "Firmware image too small (%zu bytes, need %zu).\n",
+		       blob->fw->size, sizeof(*header));
+		vfree(header);
+		return QLA_FUNCTION_FAILED;
+	}
+
+	rval = qla29xx_validate_firmware_image(vha, fwcode);
+	if (rval != QLA_SUCCESS) {
+		ql_log(ql_log_fatal, vha, 0x0093,
+		       "Unable to verify integrity of firmware image\n");
+		vfree(header);
+		return QLA_FUNCTION_FAILED;
+	}
+
+	/* byte-swap big-endian firmware header to CPU endian */
+	for (i = 0; i < sizeof(*header) / sizeof(__be32); i++)
+		((uint32_t *)header)[i] = be32_to_cpu(fwcode[i]);
+
+	/*
+	 * Section lengths are dword counts from the firmware image. Reject
+	 * values that would overflow the u32 fields on conversion to bytes
+	 * (<< 2) or that exceed the image, before using them below.
+	 */
+	max_dwords = blob->fw->size >> 2;
+	if (header->header_length > max_dwords ||
+	    header->tim_length > max_dwords ||
+	    header->array1_length > max_dwords ||
+	    header->array2_length > max_dwords ||
+	    header->array3_length > max_dwords ||
+	    header->array4_length > max_dwords) {
+		ql_log(ql_log_fatal, vha, 0x0098,
+		       "FW image has invalid section length.\n");
+		goto img_corrupt;
+	}
+
+	header->header_length <<= 2;
+	header->tim_length <<= 2;
+	header->array1_length <<= 2;
+	header->array2_length <<= 2;
+	header->array3_length <<= 2;
+	header->array4_length <<= 2;
+
+	if (!header->segment_size) {
+		ql_log(ql_log_fatal, vha, 0x0098,
+		       "FW image has zero segment_size.\n");
+		goto img_corrupt;
+	}
+
+	{
+		size_t fw_size = blob->fw->size;
+		size_t offset;
+
+		/* TIM */
+		offset = header->header_length;
+		if (offset + header->tim_length > fw_size) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "FW image too small for TIM (%#zx > %#zx).\n",
+			       offset + header->tim_length, fw_size);
+			goto img_corrupt;
+		}
+		rval = qla29xx_process_rd_image(vha, header,
+			(__be32 *)((char *)fwcode + offset),
+			TIM, TIM_DEST_ADDR, header->tim_length);
+		if (rval) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "-> Failed load TIM\n");
+			goto img_corrupt;
+		}
+
+		/* Array 1 */
+		offset += header->tim_length;
+		if (offset + header->array1_length > fw_size) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "FW image too small for Arr1 (%#zx > %#zx).\n",
+			       offset + header->array1_length, fw_size);
+			goto img_corrupt;
+		}
+		rval = qla29xx_process_rd_image(vha, header,
+			(__be32 *)((char *)fwcode + offset),
+			ARR1, header->array1_destination_addr,
+			header->array1_length);
+		if (rval) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "-> Failed load Arr1\n");
+			goto img_corrupt;
+		}
+
+		/* Array 2 */
+		offset += header->array1_length;
+		if (offset + header->array2_length > fw_size) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "FW image too small for Arr2 (%#zx > %#zx).\n",
+			       offset + header->array2_length, fw_size);
+			goto img_corrupt;
+		}
+		rval = qla29xx_process_rd_image(vha, header,
+			(__be32 *)((char *)fwcode + offset),
+			ARR2, header->array2_destination_addr,
+			header->array2_length);
+		if (rval) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "-> Failed load Arr2\n");
+			goto img_corrupt;
+		}
+
+		/* Array 3: FW_DUMP template */
+		if (offset + 10 * sizeof(__be32) > fw_size) {
+			ql_log(ql_log_fatal, vha, 0x0098,
+			       "FW image too small for risc_attr.\n");
+			goto img_corrupt;
+		}
+		array1_addr = (__be32 *)((char *)fwcode +
+				header->header_length +
+				header->tim_length);
+		risc_attr = be32_to_cpu(array1_addr[9]);
+	}
+	templates = (risc_attr & BIT_9) ? 2 : 1;
+	ql_dbg(ql_dbg_init, vha, 0x0160,
+	       "-> templates = %u\n", templates);
+
+	array3_offset = header->header_length + header->tim_length +
+		header->array1_length + header->array2_length;
+	if (array3_offset > blob->fw->size) {
+		ql_log(ql_log_fatal, vha, 0x0098,
+		       "FW image too small for Arr3 (%#lx > %#zx).\n",
+		       array3_offset, blob->fw->size);
+		goto img_corrupt;
+	}
+
+	array3_addr = (__be32 *)(((char *)fwcode) + array3_offset);
+	for (j = 0; j < templates; j++, fwdt++) {
+		vfree(fwdt->template);
+		fwdt->template = NULL;
+		fwdt->length = 0;
+
+		if ((char *)&array3_addr[7] >
+		    (char *)blob->fw->data + blob->fw->size) {
+			ql_log(ql_log_warn, vha, 0x0169,
+			       "-> fwdt%u template header exceeds firmware blob.\n",
+			       j);
+			goto failed;
+		}
+
+		array_size = be32_to_cpu(array3_addr[2]);
+		ql_dbg(ql_dbg_init, vha, 0x0161,
+		       "-> fwdt%u template array at %p (%#x dwords)\n",
+		       j, array3_addr, array_size);
+
+		if (!array_size || !~array_size || array_size <= 8) {
+			ql_dbg(ql_dbg_init, vha, 0x0162,
+			       "-> fwdt%u failed to read array\n", j);
+			goto failed;
+		}
+
+		array3_addr += 7;
+		array_size -= 8;
+
+		if ((char *)&array3_addr[array_size] >
+		    (char *)blob->fw->data + blob->fw->size) {
+			ql_log(ql_log_warn, vha, 0x0163,
+			    "-> fwdt%u template array exceeds firmware blob.\n",
+			    j);
+			goto failed;
+		}
+
+		ql_dbg(ql_dbg_init, vha, 0x0163,
+		       "-> fwdt%u template allocate template %#x words...\n",
+		       j, array_size);
+		fwdt->template = vmalloc_array(array_size, sizeof(*dcode));
+		if (!fwdt->template) {
+			ql_log(ql_log_warn, vha, 0x0164,
+			       "-> fwdt%u failed allocate template.\n", j);
+			goto failed;
+		}
+
+		dcode = fwdt->template;
+		for (i = 0; i < array_size; i++)
+			dcode[i] = (__force u32)array3_addr[i];
+
+		if (!qla27xx_fwdt_template_valid(dcode)) {
+			ql_log(ql_log_warn, vha, 0x0165,
+			       "-> fwdt%u failed template validate\n", j);
+			goto failed;
+		}
+
+		dlen = qla27xx_fwdt_template_size(dcode);
+		ql_dbg(ql_dbg_init, vha, 0x0166,
+		       "-> fwdt%u template size %#lx bytes (%#lx words)\n",
+		       j, dlen, dlen / sizeof(*dcode));
+		if (dlen > array_size * sizeof(*dcode)) {
+			ql_log(ql_log_warn, vha, 0x0167,
+			       "-> fwdt%u template exceeds array (%-lu bytes)\n",
+			       j, dlen - array_size * sizeof(*dcode));
+			goto failed;
+		}
+
+		fwdt->length = dlen;
+		ql_dbg(ql_dbg_init, vha, 0x0168,
+		       "-> fwdt%u loaded template ok\n", j);
+
+		array3_addr += array_size + 1;
+	}
+	vfree(header);
+	return QLA_SUCCESS;
+
+failed:
+	vfree(fwdt->template);
+	fwdt->template = NULL;
+	fwdt->length = 0;
+img_corrupt:
+	vfree(header);
+	return QLA_FUNCTION_FAILED;
+}
+
+static int
+qla29xx_load_fw_template(scsi_qla_host_t *vha)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct fwdt *fwdt = ha->fwdt;
+	uint templates = 1;
+	void *fw_dump_tmplt;
+	uint32_t *dcode;
+	void *buf = NULL;
+	uint32_t len;
+	uint32_t template_size;
+	uint j = 0;
+	int rval = 0;
+
+	ql_dbg(ql_dbg_init, vha, 0x01bf,
+	       "-> templates = %u fw_dump_tmplt_len = 0x%x\n",
+	       templates, ha->fw_dump_tmplt_len);
+
+	if (!ha->fw_dump_tmplt_len) {
+		ql_dbg(ql_dbg_init, vha, 0x01bf,
+		       "no fw dump template available\n");
+		return QLA_SUCCESS;
+	}
+
+	fw_dump_tmplt = kzalloc(ha->fw_dump_tmplt_len, GFP_KERNEL);
+	if (!fw_dump_tmplt) {
+		ql_log(ql_log_info, vha, 0x0013,
+		       "Unable to allocate memory\n");
+		return QLA_FUNCTION_FAILED;
+	}
+
+	buf = qla29xx_read_optrom_data(vha, FLT_REG_FW_DUMP_TMPLT, 0,
+				       fw_dump_tmplt, 0,
+				       ha->fw_dump_tmplt_len);
+	if (!buf) {
+		ql_log(ql_log_info, vha, 0x0013,
+		       "Unable to read fw dump temp info.\n");
+		goto free_fw_dump;
+	}
+
+	ql_dump_buffer(ql_dbg_init, vha, 0x006b, fw_dump_tmplt,
+		       min_t(uint32_t, ha->fw_dump_tmplt_len, 1024));
+
+	for (j = 0; j < templates; j++, fwdt++) {
+		vfree(fwdt->template);
+		fwdt->template = NULL;
+		fwdt->length = 0;
+
+		template_size = le32_to_cpu(((__le32 *)fw_dump_tmplt)[2]);
+
+		ql_dbg(ql_dbg_init, vha, 0x0161,
+		       "-> fwdt%u template array at %p (0x%x bytes)\n",
+		       j, fw_dump_tmplt, template_size);
+
+		if (!template_size || !~template_size ||
+		    template_size > ha->fw_dump_tmplt_len) {
+			ql_dbg(ql_dbg_init, vha, 0x0162,
+			       "-> fwdt%u failed to read array\n", j);
+			goto failed;
+		}
+
+		fwdt->template = vmalloc(template_size);
+		if (!fwdt->template) {
+			ql_log(ql_log_warn, vha, 0x0164,
+			       "-> fwdt%u failed allocate template.\n", j);
+			goto failed;
+		}
+
+		dcode = fwdt->template;
+		memcpy((char *)dcode, (char *)fw_dump_tmplt, template_size);
+
+		if (!qla27xx_fwdt_template_valid(dcode)) {
+			ql_log(ql_log_warn, vha, 0x0165,
+			       "-> fwdt%u failed template validate (rval %x)\n",
+			       j, rval);
+			goto failed;
+		}
+
+		len = qla27xx_fwdt_template_size(dcode);
+		if (len > template_size) {
+			ql_log(ql_log_warn, vha, 0x0167,
+			       "-> fwdt%u template exceeds array (%-u bytes)\n",
+			       j, len - template_size);
+			goto failed;
+		}
+
+		fwdt->length = len;
+	}
+
+	kfree(fw_dump_tmplt);
+	return QLA_SUCCESS;
+
+failed:
+	if (fwdt->template) {
+		vfree(fwdt->template);
+		fwdt->template = NULL;
+		fwdt->length = 0;
+	}
+free_fw_dump:
+	kfree(fw_dump_tmplt);
+	return QLA_FUNCTION_FAILED;
+}
+
+int
+qla29xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
+{
+	int rval;
+
+	*srisc_addr = 0x100000;
+
+	if (ql2xfwloadbin == 2) {
+		rval = qla29xx_load_risc_blob(vha, srisc_addr);
+		if (rval != QLA_SUCCESS) {
+			ql_log(ql_log_fatal, vha, 0x019e,
+			       "Failed to load firmware from file.\n");
+			return QLA_FUNCTION_FAILED;
+		}
+	} else {
+		rval = qla28xx_load_flash_firmware(vha);
+		if (rval != QLA_SUCCESS) {
+			ql_log(ql_log_fatal, vha, 0x019e,
+			       "Failed to load flash firmware.\n");
+			return QLA_FUNCTION_FAILED;
+		}
+
+		rval = qla29xx_load_fw_template(vha);
+		if (rval != QLA_SUCCESS) {
+			ql_log(ql_log_warn, vha, 0x01bd,
+			       "failed to read firmware template\n");
+			return QLA_FUNCTION_FAILED;
+		}
+	}
+
+	return rval;
+}
+
 void
 qla2x00_try_to_stop_firmware(scsi_qla_host_t *vha)
 {
@@ -9306,35 +9936,98 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
 	if (IS_P3P_TYPE(ha) || IS_QLA8031(ha))
 		ha->vpd_size = FA_VPD_SIZE_82XX;
 
-	if (IS_QLA28XX(ha) || IS_QLA27XX(ha))
-		qla28xx_get_aux_images(vha, &active_regions);
-
 	/* Get VPD data into cache */
 	ha->vpd = ha->nvram + VPD_OFFSET;
 
-	faddr = ha->flt_region_vpd;
-	if (IS_QLA28XX(ha)) {
-		if (active_regions.aux.vpd_nvram == QLA27XX_SECONDARY_IMAGE)
-			faddr = ha->flt_region_vpd_sec;
+	if (IS_QLA29XX(ha)) {
+		uint16_t fw_options = 0, r_code;
+		uint32_t vpd_r[] = {FLT_REG_VPD_0, FLT_REG_VPD_1,
+				    FLT_REG_VPD_2, FLT_REG_VPD_3};
+		uint32_t nvram_r[] = {FLT_REG_NVRAM_0, FLT_REG_NVRAM_1,
+				      FLT_REG_NVRAM_2, FLT_REG_NVRAM_3};
+		void *buf;
+
+		BUILD_BUG_ON((VPD_OFFSET + FA_NVRAM_VPD_SIZE +
+			      sizeof(struct qla_flash_memo_block)) >
+			     MAX_NVRAM_SIZE);
+
+		ha->fiv = (struct qla_flash_memo_block *)
+			((char *)ha->vpd + ha->vpd_size);
+
+		buf = qla29xx_read_optrom_data(vha, FLT_REG_FMB_PRI,
+					       fw_options, ha->fiv, 0,
+					       sizeof(struct qla_flash_memo_block));
+		if (!buf) {
+			ql_log(ql_log_info, vha, 0x01be,
+			    "Unable to read Flash Image Version.\n");
+		} else if (ha->fiv->signature != QLFC_FMB_SIG) {
+			ql_log(ql_log_warn, vha, 0x01bf,
+			    "Invalid FMB signature %#x, expected %#x.\n",
+			    le32_to_cpu(ha->fiv->signature),
+			    le32_to_cpu(QLFC_FMB_SIG));
+			ha->fiv = NULL;
+		} else {
+			ql_log(ql_log_info, vha, 0x0024,
+			    "Flash Image Version %u.%02u.%02u\n",
+			    ha->fiv->mbi_ver.major,
+			    ha->fiv->mbi_ver.minor,
+			    ha->fiv->mbi_ver.sub);
+		}
+
+		if (ha->port_no >= ARRAY_SIZE(vpd_r)) {
+			ql_log(ql_log_warn, vha, 0x002e,
+			    "Invalid port number %u, skipping VPD/NVRAM read.\n",
+			    ha->port_no);
+			goto out_29xx;
+		}
+
+		r_code = vpd_r[ha->port_no];
+		buf = qla29xx_read_optrom_data(vha, r_code, fw_options,
+					       ha->vpd, 0, ha->vpd_size);
+		if (!buf)
+			ql_log(ql_log_info, vha, 0x002d,
+			    "Unable to read VPD info.\n");
+
+		r_code = nvram_r[ha->port_no];
+		buf = qla29xx_read_optrom_data(vha, r_code, fw_options,
+					       ha->nvram, 0, ha->nvram_size);
+		if (!buf)
+			ql_log(ql_log_info, vha, 0x0013,
+			    "Unable to read nvram config info.\n");
+	} else {
+		if (IS_QLA28XX(ha) || IS_QLA27XX(ha))
+			qla28xx_get_aux_images(vha, &active_regions);
+
+		faddr = ha->flt_region_vpd;
+		if (IS_QLA28XX(ha)) {
+			if (active_regions.aux.vpd_nvram ==
+			    QLA27XX_SECONDARY_IMAGE)
+				faddr = ha->flt_region_vpd_sec;
+			ql_dbg(ql_dbg_init, vha, 0x0110,
+			    "Loading %s nvram image.\n",
+			    active_regions.aux.vpd_nvram ==
+			    QLA27XX_PRIMARY_IMAGE ?
+			    "primary" : "secondary");
+		}
+		ha->isp_ops->read_optrom(vha, ha->vpd, faddr << 2,
+					 ha->vpd_size);
+
+		/* Get NVRAM data into cache and calculate checksum. */
+		faddr = ha->flt_region_nvram;
+		if (IS_QLA28XX(ha)) {
+			if (active_regions.aux.vpd_nvram ==
+			    QLA27XX_SECONDARY_IMAGE)
+				faddr = ha->flt_region_nvram_sec;
+		}
 		ql_dbg(ql_dbg_init, vha, 0x0110,
 		    "Loading %s nvram image.\n",
 		    active_regions.aux.vpd_nvram == QLA27XX_PRIMARY_IMAGE ?
 		    "primary" : "secondary");
+		ha->isp_ops->read_optrom(vha, ha->nvram, faddr << 2,
+					 ha->nvram_size);
 	}
-	ha->isp_ops->read_optrom(vha, ha->vpd, faddr << 2, ha->vpd_size);
-
-	/* Get NVRAM data into cache and calculate checksum. */
-	faddr = ha->flt_region_nvram;
-	if (IS_QLA28XX(ha)) {
-		if (active_regions.aux.vpd_nvram == QLA27XX_SECONDARY_IMAGE)
-			faddr = ha->flt_region_nvram_sec;
-	}
-	ql_dbg(ql_dbg_init, vha, 0x0110,
-	    "Loading %s nvram image.\n",
-	    active_regions.aux.vpd_nvram == QLA27XX_PRIMARY_IMAGE ?
-	    "primary" : "secondary");
-	ha->isp_ops->read_optrom(vha, ha->nvram, faddr << 2, ha->nvram_size);
 
+out_29xx:
 	dptr = (__force __le32 *)nv;
 	for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++, dptr++)
 		chksum += le32_to_cpu(*dptr);
@@ -9386,7 +10079,10 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
 		nv->login_timeout = cpu_to_le16(0);
 		nv->firmware_options_1 =
 		    cpu_to_le32(BIT_14|BIT_13|BIT_2|BIT_1);
-		nv->firmware_options_2 = cpu_to_le32(2 << 4);
+		if (IS_QLA29XX(ha))
+			nv->firmware_options_2 = cpu_to_le32(BIT_4);
+		else
+			nv->firmware_options_2 = cpu_to_le32(BIT_5);
 		nv->firmware_options_2 |= cpu_to_le32(BIT_12);
 		nv->firmware_options_3 = cpu_to_le32(2 << 13);
 		nv->host_p = cpu_to_le32(BIT_11|BIT_10);
@@ -9468,9 +10164,13 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
 		icb->node_name[0] &= 0xF0;
 	}
 
-	if (IS_QLA28XX(ha) || IS_QLA27XX(ha)) {
-		if ((nv->enhanced_features & BIT_7) == 0)
+	/* SCM Enabled in NVRAM */
+	if (IS_QLA29XX(ha) || IS_QLA28XX(ha) || IS_QLA27XX(ha)) {
+		if ((le16_to_cpu(nv->enhanced_features) & BIT_7) == 0) {
+			ql_log(ql_log_info, vha, 0x0062,
+			       "USCM enabled in NVRAM\n");
 			ha->flags.scm_supported_a = 1;
+		}
 	}
 
 	/* Set host adapter parameters. */
@@ -9546,7 +10246,8 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
 
 	/* if not running MSI-X we need handshaking on interrupts */
 	if (!vha->hw->flags.msix_enabled &&
-	    (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)))
+	    (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+	     IS_QLA29XX(ha)))
 		icb->firmware_options_2 |= cpu_to_le32(BIT_22);
 
 	/* Enable ZIO. */
@@ -10026,11 +10727,28 @@ int qla2xxx_delete_qpair(struct scsi_qla_host *vha, struct qla_qpair *qpair)
 {
 	int ret = QLA_FUNCTION_FAILED;
 	struct qla_hw_data *ha = qpair->hw;
+	struct rsp_que *rsp = qpair->rsp;
 
 	qpair->delete_in_progress = 1;
 
 	qla_free_buf_pool(qpair);
 
+	/*
+	 * The response-queue interrupt schedules qla_do_work(), which
+	 * dereferences qpair->rsp->req.  Release the interrupt and flush
+	 * any pending work before the request queue is freed below so a
+	 * late completion cannot touch the freed request queue.  The
+	 * firmware queue-delete order (request then response) is kept.
+	 */
+	if (rsp && rsp->msix && rsp->msix->have_irq) {
+		free_irq(rsp->msix->vector, rsp->msix->handle);
+		rsp->msix->have_irq = 0;
+		rsp->msix->in_use = 0;
+		rsp->msix->handle = NULL;
+	}
+	if (rsp && ha->wq)
+		cancel_work_sync(&qpair->q_work);
+
 	ret = qla25xx_delete_req_que(vha, qpair->req);
 	if (ret != QLA_SUCCESS)
 		goto fail;
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index 47fbd830ff16..90da4bad8e6e 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -54,6 +54,103 @@ qla2x00_debounce_register(volatile __le16 __iomem *addr)
 	return (first);
 }
 
+/**
+ * qla29xx_calc_iocbs() - Determine number of Command-Type and Continuation
+ * IOCBs to allocate for the 29xx extended (128-byte) IOCB ring.
+ * @vha: HA context
+ * @dsds: number of data segment descriptors needed
+ * @iocb_dsds: number of DSDs embedded in the first (command) IOCB.  The
+ *	remaining DSDs ride on Continuation Type 1 Ext IOCBs which hold
+ *	NUM_CONT1_DSDS (10) each.
+ *
+ * Returns the total number of IOCB entries needed to carry @dsds.
+ */
+static inline uint16_t
+qla29xx_calc_iocbs(scsi_qla_host_t *vha, uint16_t dsds, uint8_t iocb_dsds)
+{
+	uint16_t iocbs = 1;
+
+	if (dsds > iocb_dsds) {
+		iocbs += (dsds - iocb_dsds) / NUM_CONT1_DSDS;
+		if ((dsds - iocb_dsds) % NUM_CONT1_DSDS)
+			iocbs++;
+	}
+	return iocbs;
+}
+
+/**
+ * qla_req_entry_size() - request-ring entry stride.
+ * @ha: HBA pointer
+ *
+ * Returns sizeof(struct request_ext) (128) on 29xx, sizeof(request_t) (64)
+ * everywhere else.
+ */
+static inline size_t
+qla_req_entry_size(struct qla_hw_data *ha)
+{
+	return IS_QLA29XX(ha) ? sizeof(struct request_ext) : sizeof(request_t);
+}
+
+/**
+ * qla_rsp_entry_size() - response-ring entry stride.
+ * @ha: HBA pointer
+ *
+ * Counterpart of qla_req_entry_size() for the response ring.
+ */
+static inline size_t
+qla_rsp_entry_size(struct qla_hw_data *ha)
+{
+	return IS_QLA29XX(ha) ? sizeof(struct response_ext) : sizeof(response_t);
+}
+
+/**
+ * qla_sts_cont_data_size() - status-continuation IOCB data payload size.
+ * @ha: HBA pointer
+ *
+ * sts_cont_entry_t and struct sts_cont_entry_ext share the same header and
+ * data offset; only the trailing data[] size differs (60 vs 124 bytes).
+ * Returns that size so callers need not branch on the adapter type.
+ */
+static inline u32
+qla_sts_cont_data_size(struct qla_hw_data *ha)
+{
+	return IS_QLA29XX(ha) ?
+		sizeof_field(struct sts_cont_entry_ext, data) :
+		sizeof_field(sts_cont_entry_t, data);
+}
+
+/**
+ * qla_logio_set_vp_index() - write vp_index into a login/logout IOCB.
+ * @ha: HBA pointer
+ * @pkt: logio IOCB (logio_entry_24xx or logio_entry_24xx_ext)
+ * @vp_idx: virtual port index
+ *
+ * vp_index widens from u8 (logio_entry_24xx) to __le16
+ * (logio_entry_24xx_ext) on 29xx; write the field at the right width.
+ */
+static inline void
+qla_logio_set_vp_index(struct qla_hw_data *ha, void *pkt, u16 vp_idx)
+{
+	if (IS_QLA29XX(ha))
+		((struct logio_entry_24xx_ext *)pkt)->vp_index =
+			cpu_to_le16(vp_idx);
+	else
+		((struct logio_entry_24xx *)pkt)->vp_index = vp_idx;
+}
+
+static inline u8
+qla_calc_queue_count(u16 msix_count)
+{
+	/*
+	 * Request/response queues are bounded by the MSI-X vector count less
+	 * the mailbox vector.  These counters are u8, so a board advertising
+	 * e.g. 257 vectors would truncate msix_count - 1 (256) to 0 and hand
+	 * kzalloc_objs() a zero count (ZERO_SIZE_PTR), faulting on the first
+	 * ha->req_q_map[0] store.  Clamp into [1, QLA_MAX_QUEUES - 1].
+	 */
+	return clamp_t(u16, msix_count - 1, 1, QLA_MAX_QUEUES - 1);
+}
+
 static inline void
 qla2x00_poll(struct rsp_que *rsp)
 {
@@ -358,17 +455,132 @@ static inline void
 qla_83xx_start_iocbs(struct qla_qpair *qpair)
 {
 	struct req_que *req = qpair->req;
+	struct qla_hw_data *ha = qpair->vha->hw;
 
+	/*
+	 * 29xx uses the 128-byte-strided extended request ring; advance the
+	 * matching ring_ext_ptr so the next IOCB allocator sees the correct
+	 * slot.  All other 83xx-family generations (83xx/27xx/28xx) keep the
+	 * 64-byte ring_ptr.
+	 */
 	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else
-		req->ring_ptr++;
+	if (IS_QLA29XX(ha)) {
+		if (req->ring_index == req->length) {
+			req->ring_index = 0;
+			req->ring_ext_ptr = req->ring_ext;
+		} else {
+			req->ring_ext_ptr++;
+		}
+	} else {
+		if (req->ring_index == req->length) {
+			req->ring_index = 0;
+			req->ring_ptr = req->ring;
+		} else {
+			req->ring_ptr++;
+		}
+	}
 
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 }
 
+/**
+ * qla_rsp_ring_advance() - Advance the response queue consumer pointer
+ * to the next IOCB slot, handling both 24xx (64-byte) and 29xx (128-byte)
+ * ring strides.
+ *
+ * On 29xx, ring_ext_ptr is the authoritative slot pointer (correct 128-byte
+ * pitch) and ring_ptr is kept in sync as a response_t view of the same slot
+ * so existing 24xx-shaped reads (rsp->ring_ptr->signature,
+ * (struct sts_entry_24xx *)rsp->ring_ptr, etc.) keep working unchanged; the
+ * first 64 bytes of struct response_ext are layout-compatible with response_t.
+ */
+static inline void
+qla_rsp_ring_advance(struct rsp_que *rsp)
+{
+	rsp->ring_index++;
+	if (rsp->ring_index == rsp->length) {
+		rsp->ring_index = 0;
+		rsp->ring_ptr = rsp->ring;
+		if (rsp->hw && IS_QLA29XX(rsp->hw))
+			rsp->ring_ext_ptr = rsp->ring_ext;
+	} else if (rsp->hw && IS_QLA29XX(rsp->hw)) {
+		rsp->ring_ext_ptr++;
+		rsp->ring_ptr = (response_t *)rsp->ring_ext_ptr;
+	} else {
+		rsp->ring_ptr++;
+	}
+}
+
+/**
+ * qla_req_ring_slot() - return the current request-ring producer slot.
+ * @ha: HBA pointer
+ * @req: request queue
+ *
+ * On 29xx the firmware-visible ring uses 128-byte-strided entries
+ * referenced by ring_ext_ptr; on earlier adapters the 64-byte ring
+ * referenced by ring_ptr is used.  The returned pointer is
+ * layout-compatible with request_t for common header writes; callers
+ * needing 29xx-specific fields should cast to struct request_ext.
+ */
+static inline void *
+qla_req_ring_slot(struct qla_hw_data *ha, struct req_que *req)
+{
+	return IS_QLA29XX(ha) ? (void *)req->ring_ext_ptr
+			      : (void *)req->ring_ptr;
+}
+
+/**
+ * qla_req_ring_advance() - advance request-ring producer pointer.
+ * @ha: HBA pointer
+ * @req: request queue
+ *
+ * Mirrors qla_rsp_ring_advance().  Does NOT publish the new producer
+ * index to firmware; callers that need to do so should follow with a
+ * wrt_reg_dword or qla_83xx_start_iocbs().
+ */
+static inline void
+qla_req_ring_advance(struct qla_hw_data *ha, struct req_que *req)
+{
+	req->ring_index++;
+	if (IS_QLA29XX(ha)) {
+		if (req->ring_index == req->length) {
+			req->ring_index = 0;
+			req->ring_ext_ptr = req->ring_ext;
+		} else {
+			req->ring_ext_ptr++;
+		}
+	} else {
+		if (req->ring_index == req->length) {
+			req->ring_index = 0;
+			req->ring_ptr = req->ring;
+		} else {
+			req->ring_ptr++;
+		}
+	}
+}
+
+/**
+ * qla_rsp_ring_rewind_to() - Restore the response queue consumer pointer
+ * to a previously-observed slot (used when we need to defer processing an
+ * IOCB whose continuation entries have not yet arrived).
+ * @rsp: response queue
+ * @pkt: 64-byte view of the slot to rewind to (captured from a prior read
+ *       of rsp->ring_ptr)
+ * @idx: matching ring_index value (also captured before the advance)
+ *
+ * On 29xx, pkt was originally obtained as (response_t *)rsp->ring_ext_ptr,
+ * so casting back to struct response_ext * recovers the 128-byte-stride slot
+ * pointer.
+ */
+static inline void
+qla_rsp_ring_rewind_to(struct rsp_que *rsp, response_t *pkt, uint16_t idx)
+{
+	rsp->ring_ptr = pkt;
+	rsp->ring_index = idx;
+	if (rsp->hw && IS_QLA29XX(rsp->hw))
+		rsp->ring_ext_ptr = (struct response_ext *)pkt;
+}
+
 static inline int
 qla2xxx_get_fc4_priority(struct scsi_qla_host *vha)
 {
@@ -638,3 +850,76 @@ static inline bool val_is_in_range(u32 val, u32 start, u32 end)
 	else
 		return false;
 }
+
+/*
+ * Common fields extracted from FWI2 status IOCBs.  Populated once so
+ * callers avoid duplicated IS_QLA29XX() branches for every field access.
+ */
+struct qla_sts_fwi2 {
+	u8  *data;
+	u32 data_sz;
+	u16 scsi_status;
+	u16 sts_qual;
+	u32 sense_len;
+	u32 rsp_data_len;
+	u32 rsp_residual_count;
+};
+
+static inline void
+qla_sts_fwi2_extract(struct qla_hw_data *ha, void *pkt,
+		      struct qla_sts_fwi2 *sf)
+{
+	if (IS_QLA29XX(ha)) {
+		struct sts_entry_24xx_ext *s = pkt;
+
+		sf->scsi_status = le16_to_cpu(s->u2.scsi_status);
+		sf->sts_qual = le16_to_cpu(s->u2.retry_delay_timer);
+		sf->sense_len = le32_to_cpu(s->u2.sense_len);
+		sf->rsp_data_len = le32_to_cpu(s->u2.rsp_data_len_ndma);
+		sf->rsp_residual_count = le32_to_cpu(s->u2.rsp_residual_count);
+		sf->data = s->u2.data;
+		sf->data_sz = sizeof(s->u2.data);
+		host_to_fcp_swap(s->u2.data, sizeof(s->u2.data));
+		host_to_fcp_swap(s->act_dif, sizeof(s->act_dif));
+		host_to_fcp_swap(s->exp_dif, sizeof(s->exp_dif));
+	} else {
+		struct sts_entry_24xx *s = pkt;
+
+		sf->scsi_status = le16_to_cpu(s->scsi_status);
+		sf->sts_qual = le16_to_cpu(s->status_qualifier);
+		sf->sense_len = le32_to_cpu(s->sense_len);
+		sf->rsp_data_len = le32_to_cpu(s->rsp_data_len);
+		sf->rsp_residual_count = le32_to_cpu(s->rsp_residual_count);
+		sf->data = s->data;
+		sf->data_sz = sizeof(s->data);
+		host_to_fcp_swap(s->data, sizeof(s->data));
+	}
+}
+
+/*
+ * qla_els_set_vp_sof() - write the vp_index / sof_type pair into an ELS
+ *    pass-through IOCB (els_entry_24xx{,_ext}).
+ *
+ * Both layouts have the same 16-bit slot at offset 14, but it is encoded
+ * differently:
+ *   - 24xx: separate u8 vp_index + u8 sof_type with EST_SOFI3 (1 << 4)
+ *   - 29xx: __le16 vp_index_sof with bits [8:0]=VP index, [15:12]=SOF type
+ *           and ELS_EXT_EST_SOFI3
+ * so this is the single point in the driver that knows about that
+ * encoding split.
+ */
+static inline void
+qla_els_set_vp_sof(struct scsi_qla_host *vha, void *pkt, u16 vp_idx)
+{
+	if (IS_QLA29XX(vha->hw)) {
+		struct els_entry_24xx_ext *ext = pkt;
+
+		ext->vp_index_sof =
+		    qla_ext_build_vp_sof(vp_idx, ELS_EXT_EST_SOFI3);
+	} else {
+		struct els_entry_24xx *e = pkt;
+
+		e->vp_index = vp_idx;
+		e->sof_type = EST_SOFI3;
+	}
+}
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index dbe3cd4e274c..88bae1166a24 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -12,6 +12,19 @@
 #include <scsi/scsi_tcq.h>
 
 static int qla_start_scsi_type6(srb_t *sp);
+
+/*
+ * 29xx extended IOCB builders, implemented later in this file.  Forward
+ * declared here because the unified 24xx/29xx fast paths dispatch to them
+ * via IS_QLA29XX() branches.
+ */
+static void qla29xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7_ext *cmd_pkt,
+	uint16_t tot_dsds, struct req_que *req);
+static void qla29xx_build_scsi_type_6_iocbs(srb_t *sp,
+	struct cmd_type_6_ext *cmd_pkt, uint16_t tot_dsds);
+static int qla29xx_build_scsi_crc_2_iocbs(srb_t *sp,
+	struct cmd_type_crc_2_ext *cmd_pkt, uint16_t tot_dsds,
+	uint16_t tot_prot_dsds, uint16_t fw_prot_opts);
 /**
  * qla2x00_get_cmd_direction() - Determine control_flag data direction.
  * @sp: SCSI command
@@ -115,25 +128,21 @@ qla2x00_prep_cont_type0_iocb(struct scsi_qla_host *vha)
 /**
  * qla2x00_prep_cont_type1_iocb() - Initialize a Continuation Type 1 IOCB.
  * @vha: HA context
+ * @ha: pointer to adapter hardware data
  * @req: request queue
  *
  * Returns a pointer to the continuation type 1 IOCB packet.
  */
 cont_a64_entry_t *
-qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req)
+qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct qla_hw_data *ha,
+			      struct req_que *req)
 {
 	cont_a64_entry_t *cont_pkt;
 
 	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else {
-		req->ring_ptr++;
-	}
+	qla_req_ring_advance(ha, req);
 
-	cont_pkt = (cont_a64_entry_t *)req->ring_ptr;
+	cont_pkt = (cont_a64_entry_t *)qla_req_ring_slot(ha, req);
 
 	/* Load packet defaults. */
 	put_unaligned_le32(IS_QLAFX00(vha->hw) ? CONTINUE_A64_TYPE_FX00 :
@@ -284,7 +293,8 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
 			 * Five DSDs are available in the Continuation
 			 * Type 1 IOCB.
 			 */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req);
+			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+			    vha->hw, vha->req);
 			cur_dsd = cont_pkt->dsd;
 			avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
 		}
@@ -464,16 +474,16 @@ qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req)
 	if (IS_P3P_TYPE(ha)) {
 		qla82xx_start_iocbs(vha);
 	} else {
-		/* Adjust ring index. */
-		req->ring_index++;
-		if (req->ring_index == req->length) {
-			req->ring_index = 0;
-			req->ring_ptr = req->ring;
-		} else
-			req->ring_ptr++;
+		/*
+		 * Adjust ring index.  29xx uses a 128-byte-strided ring, so
+		 * advance the extended pointer; all other generations advance
+		 * the 64-byte ring_ptr.
+		 */
+		qla_req_ring_advance(ha, req);
 
 		/* Set chip new ring index. */
-		if (ha->mqenable || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		if (ha->mqenable || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+		    IS_QLA29XX(ha)) {
 			wrt_reg_dword(req->req_q_in, req->ring_index);
 		} else if (IS_QLA83XX(ha)) {
 			wrt_reg_dword(req->req_q_in, req->ring_index);
@@ -509,36 +519,46 @@ static int
 __qla2x00_marker(struct scsi_qla_host *vha, struct qla_qpair *qpair,
     uint16_t loop_id, uint64_t lun, uint8_t type)
 {
-	mrk_entry_t *mrk;
-	struct mrk_entry_24xx *mrk24 = NULL;
-	struct req_que *req = qpair->req;
 	struct qla_hw_data *ha = vha->hw;
+	struct mrk_entry_24xx *mrk24;
+	struct req_que *req = qpair->req;
 	scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
 
-	mrk = (mrk_entry_t *)__qla2x00_alloc_iocbs(qpair, NULL);
-	if (mrk == NULL) {
+	/*
+	 * FWI2-capable adapters use the 24xx marker layout; 29xx uses the
+	 * extended marker IOCB (128 bytes) whose first 64 bytes overlay
+	 * mrk_entry_24xx through 'lun', so the common header is written
+	 * through a struct mrk_entry_24xx * view and only vp_index (u8 vs
+	 * __le16) needs a stride-aware branch.  Pre-FWI2 adapters
+	 * (ISP21xx/22xx/23xx) use the legacy mrk_entry_t layout with a
+	 * 2-byte target ID and a 16-bit LUN.
+	 */
+	mrk24 = __qla2x00_alloc_iocbs(qpair, NULL);
+	if (!mrk24) {
 		ql_log(ql_log_warn, base_vha, 0x3026,
 		    "Failed to allocate Marker IOCB.\n");
-
-		return (QLA_FUNCTION_FAILED);
+		return QLA_FUNCTION_FAILED;
 	}
 
-	mrk24 = (struct mrk_entry_24xx *)mrk;
-
-	mrk->entry_type = MARKER_TYPE;
-	mrk->modifier = type;
+	mrk24->entry_type = MARKER_TYPE;
+	mrk24->modifier = type;
 	if (type != MK_SYNC_ALL) {
 		if (IS_FWI2_CAPABLE(ha)) {
 			mrk24->nport_handle = cpu_to_le16(loop_id);
 			int_to_scsilun(lun, (struct scsi_lun *)&mrk24->lun);
 			host_to_fcp_swap(mrk24->lun, sizeof(mrk24->lun));
-			mrk24->vp_index = vha->vp_idx;
+			if (IS_QLA29XX(ha))
+				((struct mrk_entry_24xx_ext *)mrk24)->vp_index =
+				    cpu_to_le16(vha->vp_idx);
+			else
+				mrk24->vp_index = vha->vp_idx;
 		} else {
+			mrk_entry_t *mrk = (mrk_entry_t *)mrk24;
+
 			SET_TARGET_ID(ha, mrk->target, loop_id);
 			mrk->lun = cpu_to_le16((uint16_t)lun);
 		}
 	}
-
 	if (IS_FWI2_CAPABLE(ha))
 		mrk24->handle = QLA_SKIP_HANDLE;
 
@@ -747,7 +767,8 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
 			 * Five DSDs are available in the Continuation
 			 * Type 1 IOCB.
 			 */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(vha, req);
+			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+			    vha->hw, req);
 			cur_dsd = cont_pkt->dsd;
 			avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
 		}
@@ -1620,46 +1641,84 @@ qla24xx_start_scsi(srb_t *sp)
 	cmd->host_scribble = (unsigned char *)(unsigned long)handle;
 	req->cnt -= req_cnt;
 
-	cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
-	cmd_pkt->handle = make_handle(req->id, handle);
+	if (IS_QLA29XX(ha)) {
+		/*
+		 * cmd_type_7_ext diverges from cmd_type_7 past offset 48
+		 * (port_id[3]+vp_index vs ctrl_flags_2+__le16 vp_index),
+		 * so the packet-field writes can't be shared via a common
+		 * pointer type.
+		 */
+		struct cmd_type_7_ext *cmd_pkt_ext =
+			(struct cmd_type_7_ext *)req->ring_ext_ptr;
 
-	/* Zero out remaining portion of packet. */
-	/*    tagged queuing modifier -- default is TSK_SIMPLE (0). */
-	clr_ptr = (uint32_t *)cmd_pkt + 2;
-	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
-	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
+		cmd_pkt_ext->handle = make_handle(req->id, handle);
 
-	/* Set NPORT-ID and LUN number*/
-	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
-	cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
-	cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
-	cmd_pkt->vp_index = sp->vha->vp_idx;
+		/* Zero out remaining portion of packet. */
+		/*    tagged queuing modifier -- default is TSK_SIMPLE (0). */
+		clr_ptr = (uint32_t *)cmd_pkt_ext + 2;
+		memset(clr_ptr, 0, REQUEST_ENTRY_SIZE_EXT - 8);
+		cmd_pkt_ext->dseg_count = cpu_to_le16(tot_dsds);
 
-	int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
-	host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
+		/* Set NPORT-ID and LUN number. */
+		cmd_pkt_ext->nport_handle =
+			cpu_to_le16(sp->fcport->loop_id);
+		cmd_pkt_ext->vp_index =
+			cpu_to_le16(sp->fcport->vha->vp_idx);
 
-	cmd_pkt->task = TSK_SIMPLE;
+		int_to_scsilun(cmd->device->lun, &cmd_pkt_ext->lun);
+		host_to_fcp_swap((uint8_t *)&cmd_pkt_ext->lun, sizeof(cmd_pkt_ext->lun));
 
-	/* Load SCSI command packet. */
-	memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len);
-	host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb));
+		cmd_pkt_ext->task = TSK_SIMPLE;
 
-	cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+		/* Load SCSI command packet. */
+		memcpy(cmd_pkt_ext->fcp_cdb, cmd->cmnd, cmd->cmd_len);
+		host_to_fcp_swap(cmd_pkt_ext->fcp_cdb, sizeof(cmd_pkt_ext->fcp_cdb));
 
-	/* Build IOCB segments */
-	qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds, req);
+		cmd_pkt_ext->byte_count =
+			cpu_to_le32((uint32_t)scsi_bufflen(cmd));
 
-	/* Set total data segment count. */
-	cmd_pkt->entry_count = (uint8_t)req_cnt;
+		/* Build IOCB segments */
+		qla29xx_build_scsi_iocbs(sp, cmd_pkt_ext, tot_dsds, req);
+
+		/* Set total data segment count. */
+		cmd_pkt_ext->entry_count = (uint8_t)req_cnt;
+	} else {
+		cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
+		cmd_pkt->handle = make_handle(req->id, handle);
+
+		/* Zero out remaining portion of packet. */
+		/*    tagged queuing modifier -- default is TSK_SIMPLE (0). */
+		clr_ptr = (uint32_t *)cmd_pkt + 2;
+		memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+		cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
+
+		/* Set NPORT-ID and LUN number*/
+		cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+		cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+		cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+		cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+		cmd_pkt->vp_index = sp->vha->vp_idx;
+
+		int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
+		host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
+
+		cmd_pkt->task = TSK_SIMPLE;
+
+		/* Load SCSI command packet. */
+		memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len);
+		host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb));
+
+		cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+
+		/* Build IOCB segments */
+		qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds, req);
+
+		/* Set total data segment count. */
+		cmd_pkt->entry_count = (uint8_t)req_cnt;
+	}
 	wmb();
 	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else
-		req->ring_ptr++;
+	qla_req_ring_advance(ha, req);
 
 	sp->qpair->cmd_cnt++;
 	sp->flags |= SRB_DMA_VALID;
@@ -1798,7 +1857,20 @@ qla24xx_dif_start_scsi(srb_t *sp)
 
 	sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
 	sp->iores.exch_cnt = 1;
-	sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+	/*
+	 * Unlike cmd_type_6_ext / cmd_type_7_ext (which carry NUM_CMD67_DSDS
+	 * inline DSDs), cmd_type_crc_2_ext carries only a single inline
+	 * data_dsd; the remaining DSDs live in the separate CRC-context DMA.
+	 * Size the firmware IOCB-pool reservation against the CRC_2 ext inline
+	 * capacity (NUM_CRC2_EXT_INLINE_DSDS) so it mirrors the 24xx path
+	 * (qla24xx_calc_iocbs assumes 1 inline DSD for cmd_type_crc_2) and
+	 * doesn't under-count continuations expected by firmware.
+	 */
+	if (IS_QLA29XX(ha))
+		sp->iores.iocb_cnt = qla29xx_calc_iocbs(vha, tot_dsds,
+							NUM_CRC2_EXT_INLINE_DSDS);
+	else
+		sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
 	if (qla_get_fw_resources(sp->qpair, &sp->iores))
 		goto queuing_error;
 
@@ -1829,17 +1901,24 @@ qla24xx_dif_start_scsi(srb_t *sp)
 	req->cnt -= req_cnt;
 
 	/* Fill-in common area */
-	cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ptr;
+	cmd_pkt = (struct cmd_type_crc_2 *)qla_req_ring_slot(ha, req);
 	cmd_pkt->handle = make_handle(req->id, handle);
 
 	clr_ptr = (uint32_t *)cmd_pkt + 2;
-	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+	memset(clr_ptr, 0, qla_req_entry_size(ha) - 8);
 
 	/* Set NPORT-ID and LUN number*/
 	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
-	cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
-	cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+	if (!IS_QLA29XX(ha)) {
+		cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+		cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+		cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+	}
+	/*
+	 * vp_index: 24xx uses a uint8_t field at offset 51 (in port_id
+	 * group); 29xx replaces that region with control_flags_2/vp_index as
+	 * __le16s starting at offset 48.
+	 */
 
 	int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
 	host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
@@ -1848,10 +1927,22 @@ qla24xx_dif_start_scsi(srb_t *sp)
 	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
 
 	/* Build IOCB segments and adjust for data protection segments */
-	if (qla24xx_build_scsi_crc_2_iocbs(sp, (struct cmd_type_crc_2 *)
-	    req->ring_ptr, tot_dsds, tot_prot_dsds, fw_prot_opts) !=
-		QLA_SUCCESS)
-		goto queuing_error;
+	if (IS_QLA29XX(ha)) {
+		struct cmd_type_crc_2_ext *cmd_pkt_ext =
+			(struct cmd_type_crc_2_ext *)cmd_pkt;
+
+		cmd_pkt_ext->vp_index = cpu_to_le16(sp->vha->vp_idx);
+
+		if (qla29xx_build_scsi_crc_2_iocbs(sp, cmd_pkt_ext, tot_dsds,
+						   tot_prot_dsds, fw_prot_opts) !=
+		    QLA_SUCCESS)
+			goto queuing_error;
+	} else {
+		if (qla24xx_build_scsi_crc_2_iocbs(sp, cmd_pkt, tot_dsds,
+						   tot_prot_dsds, fw_prot_opts) !=
+		    QLA_SUCCESS)
+			goto queuing_error;
+	}
 
 	cmd_pkt->entry_count = (uint8_t)req_cnt;
 	/* Specify response queue number where completion should happen */
@@ -1860,12 +1951,7 @@ qla24xx_dif_start_scsi(srb_t *sp)
 	wmb();
 
 	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else
-		req->ring_ptr++;
+	qla_req_ring_advance(ha, req);
 
 	sp->qpair->cmd_cnt++;
 	/* Set chip new ring index. */
@@ -1954,7 +2040,17 @@ qla2xxx_start_scsi_mq(srb_t *sp)
 		nseg = 0;
 
 	tot_dsds = nseg;
-	req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+	/*
+	 * 29xx posts into the 128-byte extended IOCB ring (cmd_type_7_ext,
+	 * NUM_CMD67_DSDS inline DSDs) via ring_ext_ptr; all other FWI2-capable
+	 * generations use the 64-byte cmd_type_7 / ring_ptr path.  The common
+	 * skeleton (marker/handle/sg-map/resource-check/ring advance) is
+	 * shared; only the cmd_pkt field-writes and ring stride differ.
+	 */
+	if (IS_QLA29XX(ha))
+		req_cnt = qla29xx_calc_iocbs(vha, tot_dsds, NUM_CMD67_DSDS);
+	else
+		req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
 
 	sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
 	sp->iores.exch_cnt = 1;
@@ -1987,46 +2083,84 @@ qla2xxx_start_scsi_mq(srb_t *sp)
 	cmd->host_scribble = (unsigned char *)(unsigned long)handle;
 	req->cnt -= req_cnt;
 
-	cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
-	cmd_pkt->handle = make_handle(req->id, handle);
+	if (IS_QLA29XX(ha)) {
+		/*
+		 * cmd_type_7_ext diverges from cmd_type_7 past offset 48
+		 * (port_id[3]+vp_index vs ctrl_flags_2+__le16 vp_index),
+		 * so the packet-field writes can't be shared via a common
+		 * pointer type.
+		 */
+		struct cmd_type_7_ext *cmd_pkt_ext =
+			(struct cmd_type_7_ext *)req->ring_ext_ptr;
 
-	/* Zero out remaining portion of packet. */
-	/*    tagged queuing modifier -- default is TSK_SIMPLE (0). */
-	clr_ptr = (uint32_t *)cmd_pkt + 2;
-	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
-	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
+		cmd_pkt_ext->handle = make_handle(req->id, handle);
 
-	/* Set NPORT-ID and LUN number*/
-	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
-	cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
-	cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
-	cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
+		/* Zero out remaining portion of packet. */
+		/*    tagged queuing modifier -- default is TSK_SIMPLE (0). */
+		clr_ptr = (uint32_t *)cmd_pkt_ext + 2;
+		memset(clr_ptr, 0, REQUEST_ENTRY_SIZE_EXT - 8);
+		cmd_pkt_ext->dseg_count = cpu_to_le16(tot_dsds);
 
-	int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
-	host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
+		/* Set NPORT-ID and LUN number. */
+		cmd_pkt_ext->nport_handle =
+			cpu_to_le16(sp->fcport->loop_id);
+		cmd_pkt_ext->vp_index =
+			cpu_to_le16(sp->fcport->vha->vp_idx);
 
-	cmd_pkt->task = TSK_SIMPLE;
+		int_to_scsilun(cmd->device->lun, &cmd_pkt_ext->lun);
+		host_to_fcp_swap((uint8_t *)&cmd_pkt_ext->lun,
+				sizeof(cmd_pkt_ext->lun));
 
-	/* Load SCSI command packet. */
-	memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len);
-	host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb));
+		cmd_pkt_ext->task = TSK_SIMPLE;
 
-	cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+		memcpy(cmd_pkt_ext->fcp_cdb, cmd->cmnd, cmd->cmd_len);
+		host_to_fcp_swap(cmd_pkt_ext->fcp_cdb,
+				sizeof(cmd_pkt_ext->fcp_cdb));
 
-	/* Build IOCB segments */
-	qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds, req);
+		cmd_pkt_ext->byte_count =
+			cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+
+		qla29xx_build_scsi_iocbs(sp, cmd_pkt_ext, tot_dsds, req);
+
+		cmd_pkt_ext->entry_count = (uint8_t)req_cnt;
+	} else {
+		cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
+		cmd_pkt->handle = make_handle(req->id, handle);
+
+		/* Zero out remaining portion of packet. */
+		/*    tagged queuing modifier -- default is TSK_SIMPLE (0). */
+		clr_ptr = (uint32_t *)cmd_pkt + 2;
+		memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+		cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
+
+		/* Set NPORT-ID and LUN number. */
+		cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+		cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+		cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+		cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+		cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
+
+		int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
+		host_to_fcp_swap((uint8_t *)&cmd_pkt->lun,
+				sizeof(cmd_pkt->lun));
+
+		cmd_pkt->task = TSK_SIMPLE;
+
+		memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len);
+		host_to_fcp_swap(cmd_pkt->fcp_cdb,
+				sizeof(cmd_pkt->fcp_cdb));
+
+		cmd_pkt->byte_count =
+			cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+
+		qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds, req);
+
+		cmd_pkt->entry_count = (uint8_t)req_cnt;
+	}
 
-	/* Set total data segment count. */
-	cmd_pkt->entry_count = (uint8_t)req_cnt;
 	wmb();
 	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else
-		req->ring_ptr++;
+	qla_req_ring_advance(ha, req);
 
 	sp->qpair->cmd_cnt++;
 	sp->flags |= SRB_DMA_VALID;
@@ -2034,8 +2168,12 @@ qla2xxx_start_scsi_mq(srb_t *sp)
 	/* Set chip new ring index. */
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 
-	/* Manage unprocessed RIO/ZIO commands in response queue. */
-	if (vha->flags.process_response_queue &&
+	/*
+	 * Manage unprocessed RIO/ZIO commands in response queue. 29xx skips
+	 * this optimization until rsp_que is made 128-byte-stride aware;
+	 * completions still arrive via the regular ISR path.
+	 */
+	if (!IS_QLA29XX(ha) && vha->flags.process_response_queue &&
 	    rsp->ring_ptr->signature != RESPONSE_PROCESSED)
 		qla24xx_process_response_queue(vha, rsp);
 
@@ -2180,7 +2318,20 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp)
 
 	sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
 	sp->iores.exch_cnt = 1;
-	sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+	/*
+	 * Unlike cmd_type_6_ext / cmd_type_7_ext (which carry NUM_CMD67_DSDS
+	 * inline DSDs), cmd_type_crc_2_ext carries only a single inline
+	 * data_dsd; the remaining DSDs live in the separate CRC-context DMA.
+	 * Size the firmware IOCB-pool reservation against the CRC_2 ext inline
+	 * capacity (NUM_CRC2_EXT_INLINE_DSDS) so it mirrors the 24xx path
+	 * (qla24xx_calc_iocbs assumes 1 inline DSD for cmd_type_crc_2) and
+	 * doesn't under-count continuations expected by firmware.
+	 */
+	if (IS_QLA29XX(ha))
+		sp->iores.iocb_cnt = qla29xx_calc_iocbs(vha, tot_dsds,
+						       NUM_CRC2_EXT_INLINE_DSDS);
+	else
+		sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
 	if (qla_get_fw_resources(sp->qpair, &sp->iores))
 		goto queuing_error;
 
@@ -2211,18 +2362,35 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp)
 	cmd->host_scribble = (unsigned char *)(unsigned long)handle;
 	req->cnt -= req_cnt;
 
-	/* Fill-in common area */
-	cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ptr;
+	/*
+	 * cmd_type_crc_2 and cmd_type_crc_2_ext share an identical header
+	 * layout through byte_count (offset 44); the trailing port_id /
+	 * vp_index / CRC-context fields diverge.  The common header writes
+	 * (handle, memset, nport_handle, lun, dseg_count, entry_count,
+	 * timeout) are performed via the 24xx-typed pointer in both paths;
+	 * the divergent tail and builder use hardware-specific handling.
+	 */
+	if (IS_QLA29XX(ha))
+		cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ext_ptr;
+	else
+		cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ptr;
 	cmd_pkt->handle = make_handle(req->id, handle);
 
 	clr_ptr = (uint32_t *)cmd_pkt + 2;
-	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+	memset(clr_ptr, 0, qla_req_entry_size(ha) - 8);
 
 	/* Set NPORT-ID and LUN number*/
 	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
-	cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
-	cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+	if (!IS_QLA29XX(ha)) {
+		cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+		cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+		cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+	}
+	/*
+	 * vp_index: 24xx uses a uint8_t field at offset 51 (in port_id
+	 * group); 29xx replaces that region with control_flags_2/vp_index as
+	 * __le16s starting at offset 48.
+	 */
 
 	int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
 	host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
@@ -2231,29 +2399,41 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp)
 	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
 
 	/* Build IOCB segments and adjust for data protection segments */
-	if (qla24xx_build_scsi_crc_2_iocbs(sp, (struct cmd_type_crc_2 *)
-	    req->ring_ptr, tot_dsds, tot_prot_dsds, fw_prot_opts) !=
-		QLA_SUCCESS)
-		goto queuing_error;
+	if (IS_QLA29XX(ha)) {
+		struct cmd_type_crc_2_ext *cmd_pkt_ext =
+			(struct cmd_type_crc_2_ext *)cmd_pkt;
+
+		cmd_pkt_ext->vp_index = cpu_to_le16(sp->vha->vp_idx);
+
+		if (qla29xx_build_scsi_crc_2_iocbs(sp, cmd_pkt_ext, tot_dsds,
+						   tot_prot_dsds,
+						   fw_prot_opts) !=
+		    QLA_SUCCESS)
+			goto queuing_error;
+	} else {
+		if (qla24xx_build_scsi_crc_2_iocbs(sp, cmd_pkt, tot_dsds,
+						   tot_prot_dsds,
+						   fw_prot_opts) !=
+		    QLA_SUCCESS)
+			goto queuing_error;
+	}
 
 	cmd_pkt->entry_count = (uint8_t)req_cnt;
 	cmd_pkt->timeout = cpu_to_le16(0);
 	wmb();
 
-	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else
-		req->ring_ptr++;
+	/* Adjust ring index.  29xx uses the 128-byte extended ring pointer. */
+	qla_req_ring_advance(ha, req);
 
 	sp->qpair->cmd_cnt++;
 	/* Set chip new ring index. */
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 
-	/* Manage unprocessed RIO/ZIO commands in response queue. */
-	if (vha->flags.process_response_queue &&
+	/*
+	 * Manage unprocessed RIO/ZIO commands in response queue. 29xx skips
+	 * this optimization until rsp_que is made 128-byte-stride aware.
+	 */
+	if (!IS_QLA29XX(ha) && vha->flags.process_response_queue &&
 	    rsp->ring_ptr->signature != RESPONSE_PROCESSED)
 		qla24xx_process_response_queue(vha, rsp);
 
@@ -2286,10 +2466,9 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp)
 	struct req_que *req = qpair->req;
 	device_reg_t *reg = ISP_QUE_REG(ha, req->id);
 	uint32_t handle;
-	request_t *pkt;
 	uint16_t cnt, req_cnt;
+	request_t *pkt = NULL;
 
-	pkt = NULL;
 	req_cnt = 1;
 	handle = 0;
 
@@ -2303,7 +2482,7 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp)
 		if (qpair->use_shadow_reg)
 			cnt = *req->out_ptr;
 		else if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha) ||
-		    IS_QLA28XX(ha))
+		    IS_QLA28XX(ha) || IS_QLA29XX(ha))
 			cnt = rd_reg_dword(&reg->isp25mq.req_q_out);
 		else if (IS_P3P_TYPE(ha))
 			cnt = rd_reg_dword(reg->isp82.req_q_out);
@@ -2343,14 +2522,21 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp)
 		sp->handle = handle;
 	}
 
-	/* Prep packet */
+	/*
+	 * Prep packet.  29xx posts into a 128-byte-strided ring via
+	 * ring_ext_ptr; the first 8 bytes of struct request_ext overlay the
+	 * common request_t header, so entry_count/handle writes are
+	 * layout-compatible once we return the pkt as request_t *.
+	 */
 	req->cnt -= req_cnt;
-	pkt = req->ring_ptr;
-	memset(pkt, 0, REQUEST_ENTRY_SIZE);
+	pkt = qla_req_ring_slot(ha, req);
 	if (IS_QLAFX00(ha)) {
+		memset_io((void __iomem __force *)pkt, 0,
+			  qla_req_entry_size(ha));
 		wrt_reg_byte((u8 __force __iomem *)&pkt->entry_count, req_cnt);
 		wrt_reg_dword((__le32 __force __iomem *)&pkt->handle, handle);
 	} else {
+		memset(pkt, 0, qla_req_entry_size(ha));
 		pkt->entry_count = req_cnt;
 		pkt->handle = handle;
 	}
@@ -2380,8 +2566,9 @@ qla2x00_alloc_iocbs(struct scsi_qla_host *vha, srb_t *sp)
 }
 
 static void
-qla24xx_prli_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+qla24xx_prli_iocb(srb_t *sp, void *pkt)
 {
+	struct logio_entry_24xx *logio = pkt;
 	struct srb_iocb *lio = &sp->u.iocb_cmd;
 
 	logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
@@ -2408,12 +2595,13 @@ qla24xx_prli_iocb(srb_t *sp, struct logio_entry_24xx *logio)
 	logio->port_id[0] = sp->fcport->d_id.b.al_pa;
 	logio->port_id[1] = sp->fcport->d_id.b.area;
 	logio->port_id[2] = sp->fcport->d_id.b.domain;
-	logio->vp_index = sp->vha->vp_idx;
+	qla_logio_set_vp_index(sp->vha->hw, pkt, sp->vha->vp_idx);
 }
 
 static void
-qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+qla24xx_login_iocb(srb_t *sp, void *pkt)
 {
+	struct logio_entry_24xx *logio = pkt;
 	struct srb_iocb *lio = &sp->u.iocb_cmd;
 
 	logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
@@ -2438,7 +2626,7 @@ qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio)
 	logio->port_id[0] = sp->fcport->d_id.b.al_pa;
 	logio->port_id[1] = sp->fcport->d_id.b.area;
 	logio->port_id[2] = sp->fcport->d_id.b.domain;
-	logio->vp_index = sp->vha->vp_idx;
+	qla_logio_set_vp_index(sp->vha->hw, pkt, sp->vha->vp_idx);
 }
 
 static void
@@ -2466,9 +2654,11 @@ qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx)
 }
 
 static void
-qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+qla24xx_logout_iocb(srb_t *sp, void *pkt)
 {
+	struct logio_entry_24xx *logio = pkt;
 	u16 control_flags = LCF_COMMAND_LOGO;
+
 	logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
 
 	if (sp->fcport->explicit_logout) {
@@ -2485,7 +2675,7 @@ qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio)
 	logio->port_id[0] = sp->fcport->d_id.b.al_pa;
 	logio->port_id[1] = sp->fcport->d_id.b.area;
 	logio->port_id[2] = sp->fcport->d_id.b.domain;
-	logio->vp_index = sp->vha->vp_idx;
+	qla_logio_set_vp_index(sp->vha->hw, pkt, sp->vha->vp_idx);
 }
 
 static void
@@ -2507,12 +2697,14 @@ qla2x00_logout_iocb(srb_t *sp, struct mbx_entry *mbx)
 }
 
 static void
-qla24xx_adisc_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+qla24xx_adisc_iocb(srb_t *sp, void *pkt)
 {
+	struct logio_entry_24xx *logio = pkt;
+
 	logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
 	logio->control_flags = cpu_to_le16(LCF_COMMAND_ADISC);
 	logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	logio->vp_index = sp->vha->vp_idx;
+	qla_logio_set_vp_index(sp->vha->hw, pkt, sp->vha->vp_idx);
 }
 
 static void
@@ -2537,7 +2729,7 @@ qla2x00_adisc_iocb(srb_t *sp, struct mbx_entry *mbx)
 }
 
 static void
-qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk)
+qla24xx_tm_iocb(srb_t *sp, void *pkt)
 {
 	uint32_t flags;
 	uint64_t lun;
@@ -2546,26 +2738,39 @@ qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk)
 	struct qla_hw_data *ha = vha->hw;
 	struct srb_iocb *iocb = &sp->u.iocb_cmd;
 	struct req_que *req = sp->qpair->req;
+	struct tsk_mgmt_entry *tsk;
 
 	flags = iocb->u.tmf.flags;
 	lun = iocb->u.tmf.lun;
 
+	/*
+	 * tsk_mgmt_entry_ext overlays tsk_mgmt_entry through control_flags
+	 * (offsets 0-27 are byte-identical), so the common header writes
+	 * go through one struct tsk_mgmt_entry * view.  The ext layout
+	 * has no port_id and uses a wider __le16 vp_index at a different
+	 * offset, so port_id / vp_index assignments diverge per stride.
+	 */
+	tsk = pkt;
 	tsk->entry_type = TSK_MGMT_IOCB_TYPE;
 	tsk->entry_count = 1;
 	tsk->handle = make_handle(req->id, tsk->handle);
 	tsk->nport_handle = cpu_to_le16(fcport->loop_id);
 	tsk->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
 	tsk->control_flags = cpu_to_le32(flags);
-	tsk->port_id[0] = fcport->d_id.b.al_pa;
-	tsk->port_id[1] = fcport->d_id.b.area;
-	tsk->port_id[2] = fcport->d_id.b.domain;
-	tsk->vp_index = fcport->vha->vp_idx;
+	if (IS_QLA29XX(ha)) {
+		((struct tsk_mgmt_entry_ext *)pkt)->vp_index =
+		    cpu_to_le16(fcport->vha->vp_idx);
+	} else {
+		tsk->port_id[0] = fcport->d_id.b.al_pa;
+		tsk->port_id[1] = fcport->d_id.b.area;
+		tsk->port_id[2] = fcport->d_id.b.domain;
+		tsk->vp_index = fcport->vha->vp_idx;
+	}
 
-	if (flags & (TCF_LUN_RESET | TCF_ABORT_TASK_SET|
-	    TCF_CLEAR_TASK_SET|TCF_CLEAR_ACA)) {
+	if (flags & (TCF_LUN_RESET | TCF_ABORT_TASK_SET |
+	    TCF_CLEAR_TASK_SET | TCF_CLEAR_ACA)) {
 		int_to_scsilun(lun, &tsk->lun);
-		host_to_fcp_swap((uint8_t *)&tsk->lun,
-			sizeof(tsk->lun));
+		host_to_fcp_swap((uint8_t *)&tsk->lun, sizeof(tsk->lun));
 	}
 }
 
@@ -2791,8 +2996,9 @@ qla24xx_els_dcmd_iocb(scsi_qla_host_t *vha, int els_opcode,
 }
 
 static void
-qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
+qla24xx_els_logo_iocb(srb_t *sp, void *pkt)
 {
+	struct els_entry_24xx *els_iocb = pkt;
 	scsi_qla_host_t *vha = sp->vha;
 	struct srb_iocb *elsio = &sp->u.iocb_cmd;
 
@@ -2803,11 +3009,11 @@ qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
 	els_iocb->handle = sp->handle;
 	els_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
 	els_iocb->tx_dsd_count = cpu_to_le16(1);
-	els_iocb->vp_index = vha->vp_idx;
-	els_iocb->sof_type = EST_SOFI3;
 	els_iocb->rx_dsd_count = 0;
 	els_iocb->opcode = elsio->u.els_logo.els_cmd;
 
+	qla_els_set_vp_sof(vha, pkt, vha->vp_idx);
+
 	els_iocb->d_id[0] = sp->fcport->d_id.b.al_pa;
 	els_iocb->d_id[1] = sp->fcport->d_id.b.area;
 	els_iocb->d_id[2] = sp->fcport->d_id.b.domain;
@@ -3138,9 +3344,10 @@ qla24xx_els_dcmd2_iocb(scsi_qla_host_t *vha, int els_opcode,
 
 /* it is assume qpair lock is held */
 void qla_els_pt_iocb(struct scsi_qla_host *vha,
-	struct els_entry_24xx *els_iocb,
-	struct qla_els_pt_arg *a)
+	void *pkt, struct qla_els_pt_arg *a)
 {
+	struct els_entry_24xx *els_iocb = pkt;
+
 	els_iocb->entry_type = ELS_IOCB_TYPE;
 	els_iocb->entry_count = 1;
 	els_iocb->sys_define = 0;
@@ -3149,11 +3356,11 @@ void qla_els_pt_iocb(struct scsi_qla_host *vha,
 	els_iocb->nport_handle = a->nport_handle;
 	els_iocb->rx_xchg_address = a->rx_xchg_address;
 	els_iocb->tx_dsd_count = cpu_to_le16(1);
-	els_iocb->vp_index = a->vp_idx;
-	els_iocb->sof_type = EST_SOFI3;
 	els_iocb->rx_dsd_count = cpu_to_le16(0);
 	els_iocb->opcode = a->els_opcode;
 
+	qla_els_set_vp_sof(vha, pkt, a->vp_idx);
+
 	els_iocb->d_id[0] = a->did.b.al_pa;
 	els_iocb->d_id[1] = a->did.b.area;
 	els_iocb->d_id[2] = a->did.b.domain;
@@ -3174,8 +3381,9 @@ void qla_els_pt_iocb(struct scsi_qla_host *vha,
 }
 
 static void
-qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
+qla24xx_els_iocb(srb_t *sp, void *pkt)
 {
+	struct els_entry_24xx *els_iocb = pkt;
 	struct bsg_job *bsg_job = sp->u.bsg_job;
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 
@@ -3186,8 +3394,8 @@ qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
         els_iocb->handle = sp->handle;
 	els_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
 	els_iocb->tx_dsd_count = cpu_to_le16(bsg_job->request_payload.sg_cnt);
-	els_iocb->vp_index = sp->vha->vp_idx;
-        els_iocb->sof_type = EST_SOFI3;
+
+	qla_els_set_vp_sof(sp->vha, pkt, sp->vha->vp_idx);
 	els_iocb->rx_dsd_count = cpu_to_le16(bsg_job->reply_payload.sg_cnt);
 
 	els_iocb->opcode =
@@ -3269,7 +3477,7 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb)
 			* Type 1 IOCB.
 			       */
 			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
-			    vha->hw->req_q_map[0]);
+			    ha, ha->req_q_map[0]);
 			cur_dsd = cont_pkt->dsd;
 			avail_dsds = 5;
 			entry_count++;
@@ -3284,7 +3492,7 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb)
 }
 
 static void
-qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
+qla24xx_ct_iocb(srb_t *sp, void *pkt)
 {
 	uint16_t        avail_dsds;
 	struct dsd64	*cur_dsd;
@@ -3295,41 +3503,67 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
 	struct qla_hw_data *ha = vha->hw;
 	struct bsg_job *bsg_job = sp->u.bsg_job;
 	int entry_count = 1;
-	cont_a64_entry_t *cont_pkt = NULL;
-
-	ct_iocb->entry_type = CT_IOCB_TYPE;
-        ct_iocb->entry_status = 0;
-        ct_iocb->sys_define = 0;
-        ct_iocb->handle = sp->handle;
-
-	ct_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	ct_iocb->vp_index = sp->vha->vp_idx;
-	ct_iocb->comp_status = cpu_to_le16(0);
 
 	cmd_dsds = bsg_job->request_payload.sg_cnt;
 	rsp_dsds = bsg_job->reply_payload.sg_cnt;
 
-	ct_iocb->cmd_dsd_count = cpu_to_le16(cmd_dsds);
-        ct_iocb->timeout = 0;
-	ct_iocb->rsp_dsd_count = cpu_to_le16(rsp_dsds);
-        ct_iocb->cmd_byte_count =
-            cpu_to_le32(bsg_job->request_payload.payload_len);
+	if (IS_QLA29XX(ha)) {
+		struct ct_entry_24xx_ext *ct_iocb = pkt;
+
+		ct_iocb->entry_type = CT_IOCB_TYPE;
+		ct_iocb->entry_status = 0;
+		ct_iocb->sys_define = 0;
+		ct_iocb->handle = sp->handle;
+		ct_iocb->nport_handle =
+		    cpu_to_le16(sp->fcport->loop_id);
+		ct_iocb->vp_index = cpu_to_le16(sp->vha->vp_idx);
+		ct_iocb->comp_status = cpu_to_le16(0);
+		ct_iocb->cmd_dsd_count = cpu_to_le16(cmd_dsds);
+		ct_iocb->timeout = 0;
+		ct_iocb->rsp_dsd_count = cpu_to_le16(rsp_dsds);
+		ct_iocb->cmd_byte_count =
+		    cpu_to_le32(bsg_job->request_payload.payload_len);
+		avail_dsds = NUM_CT_DSDS;
+		cur_dsd = ct_iocb->dsd;
+	} else {
+		struct ct_entry_24xx *ct_iocb = pkt;
+
+		ct_iocb->entry_type = CT_IOCB_TYPE;
+		ct_iocb->entry_status = 0;
+		ct_iocb->sys_define = 0;
+		ct_iocb->handle = sp->handle;
+		ct_iocb->nport_handle =
+		    cpu_to_le16(sp->fcport->loop_id);
+		ct_iocb->vp_index = sp->vha->vp_idx;
+		ct_iocb->comp_status = cpu_to_le16(0);
+		ct_iocb->cmd_dsd_count = cpu_to_le16(cmd_dsds);
+		ct_iocb->timeout = 0;
+		ct_iocb->rsp_dsd_count = cpu_to_le16(rsp_dsds);
+		ct_iocb->cmd_byte_count =
+		    cpu_to_le32(bsg_job->request_payload.payload_len);
+		avail_dsds = 2;
+		cur_dsd = ct_iocb->dsd;
+	}
 
-	avail_dsds = 2;
-	cur_dsd = ct_iocb->dsd;
 	index = 0;
 
 	for_each_sg(bsg_job->request_payload.sg_list, sg, cmd_dsds, index) {
-		/* Allocate additional continuation packets? */
 		if (avail_dsds == 0) {
-			/*
-			 * Five DSDs are available in the Cont.
-			 * Type 1 IOCB.
-			 */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(
-			    vha, ha->req_q_map[0]);
-			cur_dsd = cont_pkt->dsd;
-			avail_dsds = 5;
+			if (IS_QLA29XX(ha)) {
+				struct cont_a64_entry_ext *cont_pkt;
+
+				cont_pkt = qla2900_prep_cont_type1_iocb(vha,
+				    ha->req_q_map[0]);
+				cur_dsd = cont_pkt->dsd;
+				avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
+			} else {
+				cont_a64_entry_t *cont_pkt;
+
+				cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+				    ha, ha->req_q_map[0]);
+				cur_dsd = cont_pkt->dsd;
+				avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
+			}
 			entry_count++;
 		}
 
@@ -3340,23 +3574,33 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
 	index = 0;
 
 	for_each_sg(bsg_job->reply_payload.sg_list, sg, rsp_dsds, index) {
-		/* Allocate additional continuation packets? */
 		if (avail_dsds == 0) {
-			/*
-			* Five DSDs are available in the Cont.
-			* Type 1 IOCB.
-			       */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
-			    ha->req_q_map[0]);
-			cur_dsd = cont_pkt->dsd;
-			avail_dsds = 5;
+			if (IS_QLA29XX(ha)) {
+				struct cont_a64_entry_ext *cont_pkt;
+
+				cont_pkt = qla2900_prep_cont_type1_iocb(vha,
+				    ha->req_q_map[0]);
+				cur_dsd = cont_pkt->dsd;
+				avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
+			} else {
+				cont_a64_entry_t *cont_pkt;
+
+				cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+				    ha, ha->req_q_map[0]);
+				cur_dsd = cont_pkt->dsd;
+				avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
+			}
 			entry_count++;
 		}
 
 		append_dsd64(&cur_dsd, sg);
 		avail_dsds--;
 	}
-        ct_iocb->entry_count = entry_count;
+
+	if (IS_QLA29XX(ha))
+		((struct ct_entry_24xx_ext *)pkt)->entry_count = entry_count;
+	else
+		((struct ct_entry_24xx *)pkt)->entry_count = entry_count;
 }
 
 /*
@@ -3684,32 +3928,50 @@ qla82xx_start_scsi(srb_t *sp)
 }
 
 static void
-qla24xx_abort_iocb(srb_t *sp, struct abort_entry_24xx *abt_iocb)
+qla24xx_abort_iocb(srb_t *sp, void *pkt)
 {
 	struct srb_iocb *aio = &sp->u.iocb_cmd;
 	scsi_qla_host_t *vha = sp->vha;
+	struct qla_hw_data *ha = vha->hw;
 	struct req_que *req = sp->qpair->req;
 	srb_t *orig_sp = sp->cmd_sp;
+	struct abort_entry_24xx *abt = pkt;
 
-	memset(abt_iocb, 0, sizeof(struct abort_entry_24xx));
-	abt_iocb->entry_type = ABORT_IOCB_TYPE;
-	abt_iocb->entry_count = 1;
-	abt_iocb->handle = make_handle(req->id, sp->handle);
-	if (sp->fcport) {
-		abt_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-		abt_iocb->port_id[0] = sp->fcport->d_id.b.al_pa;
-		abt_iocb->port_id[1] = sp->fcport->d_id.b.area;
-		abt_iocb->port_id[2] = sp->fcport->d_id.b.domain;
-	}
-	abt_iocb->handle_to_abort =
-		make_handle(le16_to_cpu(aio->u.abt.req_que_no),
-			    aio->u.abt.cmd_hndl);
-	abt_iocb->vp_index = vha->vp_idx;
-	abt_iocb->req_que_no = aio->u.abt.req_que_no;
+	/*
+	 * abort_entry_24xx_ext overlays abort_entry_24xx through
+	 * req_que_no (offsets 0-17).  After that the layouts diverge:
+	 * the 24xx variant has 30 bytes of reserved_1 followed by
+	 * port_id[3] and a u8 vp_index at offsets 48-51, while the
+	 * ext variant places a __le16 vp_index at offset 18 and has
+	 * no port_id field.  Common-header writes go through one
+	 * struct abort_entry_24xx * view; only port_id / vp_index
+	 * branch on stride.
+	 */
+	memset(pkt, 0, qla_req_entry_size(ha));
+	abt->entry_type = ABORT_IOCB_TYPE;
+	abt->entry_count = 1;
+	abt->handle = make_handle(req->id, sp->handle);
+	if (sp->fcport)
+		abt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+	abt->handle_to_abort =
+	    make_handle(le16_to_cpu(aio->u.abt.req_que_no),
+			aio->u.abt.cmd_hndl);
+	abt->req_que_no = aio->u.abt.req_que_no;
+	if (IS_QLA29XX(ha)) {
+		((struct abort_entry_24xx_ext *)pkt)->vp_index =
+		    cpu_to_le16(vha->vp_idx);
+	} else {
+		if (sp->fcport) {
+			abt->port_id[0] = sp->fcport->d_id.b.al_pa;
+			abt->port_id[1] = sp->fcport->d_id.b.area;
+			abt->port_id[2] = sp->fcport->d_id.b.domain;
+		}
+		abt->vp_index = vha->vp_idx;
+	}
 
 	/* need to pass original sp */
 	if (orig_sp)
-		qla_nvme_abort_set_option(abt_iocb, orig_sp);
+		qla_nvme_abort_set_option(pkt, orig_sp);
 
 	/* Send the command to the firmware */
 	wmb();
@@ -3729,11 +3991,15 @@ qla2x00_mb_iocb(srb_t *sp, struct mbx_24xx_entry *mbx)
 }
 
 static void
-qla2x00_ctpthru_cmd_iocb(srb_t *sp, struct ct_entry_24xx *ct_pkt)
+qla2x00_ctpthru_cmd_iocb(srb_t *sp, void *pkt)
 {
-	sp->u.iocb_cmd.u.ctarg.iocb = ct_pkt;
+	sp->u.iocb_cmd.u.ctarg.iocb = pkt;
 	qla24xx_prep_ms_iocb(sp->vha, &sp->u.iocb_cmd.u.ctarg);
-	ct_pkt->handle = sp->handle;
+
+	if (IS_QLA29XX(sp->vha->hw))
+		((struct ct_entry_24xx_ext *)pkt)->handle = sp->handle;
+	else
+		((struct ct_entry_24xx *)pkt)->handle = sp->handle;
 }
 
 static void qla2x00_send_notify_ack_iocb(srb_t *sp,
@@ -3775,63 +4041,107 @@ static void qla2x00_send_notify_ack_iocb(srb_t *sp,
 }
 
 /*
- * Build NVME LS request
+ * Build NVME LS request.
+ *
+ * pt_ls4_request_ext overlays pt_ls4_request through exchange_address
+ * (offsets 0-27 are byte-identical), so the common-header writes go
+ * through one struct pt_ls4_request * view.  The ext layout has a wider
+ * __le16 vp_index and places rx_/tx_byte_count and dsd[] at different
+ * offsets, so those assignments diverge per stride.
  */
 static void
-qla_nvme_ls(srb_t *sp, struct pt_ls4_request *cmd_pkt)
+qla_nvme_ls(srb_t *sp, void *cmd_pkt)
 {
-	struct srb_iocb *nvme;
+	struct srb_iocb *nvme = &sp->u.iocb_cmd;
+	struct qla_hw_data *ha = sp->vha->hw;
+	struct pt_ls4_request *pkt = cmd_pkt;
 
-	nvme = &sp->u.iocb_cmd;
-	cmd_pkt->entry_type = PT_LS4_REQUEST;
-	cmd_pkt->entry_count = 1;
-	cmd_pkt->timeout = cpu_to_le16(nvme->u.nvme.timeout_sec);
-	cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
+	pkt->entry_type = PT_LS4_REQUEST;
+	pkt->entry_count = 1;
+	pkt->timeout = cpu_to_le16(nvme->u.nvme.timeout_sec);
+	pkt->tx_dseg_count = cpu_to_le16(1);
 
 	if (sp->unsol_rsp) {
-		cmd_pkt->control_flags =
-				cpu_to_le16(CF_LS4_RESPONDER << CF_LS4_SHIFT);
-		cmd_pkt->nport_handle = nvme->u.nvme.nport_handle;
-		cmd_pkt->exchange_address = nvme->u.nvme.exchange_address;
+		pkt->control_flags =
+			cpu_to_le16(CF_LS4_RESPONDER << CF_LS4_SHIFT);
+		pkt->nport_handle = nvme->u.nvme.nport_handle;
+		pkt->exchange_address = nvme->u.nvme.exchange_address;
 	} else {
-		cmd_pkt->control_flags =
-				cpu_to_le16(CF_LS4_ORIGINATOR << CF_LS4_SHIFT);
-		cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-		cmd_pkt->rx_dseg_count = cpu_to_le16(1);
-		cmd_pkt->rx_byte_count = nvme->u.nvme.rsp_len;
-		cmd_pkt->dsd[1].length  = nvme->u.nvme.rsp_len;
-		put_unaligned_le64(nvme->u.nvme.rsp_dma, &cmd_pkt->dsd[1].address);
+		pkt->control_flags =
+			cpu_to_le16(CF_LS4_ORIGINATOR << CF_LS4_SHIFT);
+		pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+		pkt->rx_dseg_count = cpu_to_le16(1);
 	}
 
-	cmd_pkt->tx_dseg_count = cpu_to_le16(1);
-	cmd_pkt->tx_byte_count = nvme->u.nvme.cmd_len;
-	cmd_pkt->dsd[0].length = nvme->u.nvme.cmd_len;
-	put_unaligned_le64(nvme->u.nvme.cmd_dma, &cmd_pkt->dsd[0].address);
+	if (IS_QLA29XX(ha)) {
+		struct pt_ls4_request_ext *ext = cmd_pkt;
+
+		ext->vp_index = cpu_to_le16(sp->fcport->vha->vp_idx);
+		ext->tx_byte_count = nvme->u.nvme.cmd_len;
+		ext->dsd[0].length = nvme->u.nvme.cmd_len;
+		put_unaligned_le64(nvme->u.nvme.cmd_dma,
+				   &ext->dsd[0].address);
+		if (!sp->unsol_rsp) {
+			ext->rx_byte_count = nvme->u.nvme.rsp_len;
+			ext->dsd[1].length = nvme->u.nvme.rsp_len;
+			put_unaligned_le64(nvme->u.nvme.rsp_dma,
+					   &ext->dsd[1].address);
+		}
+	} else {
+		pkt->vp_index = sp->fcport->vha->vp_idx;
+		pkt->tx_byte_count = nvme->u.nvme.cmd_len;
+		pkt->dsd[0].length = nvme->u.nvme.cmd_len;
+		put_unaligned_le64(nvme->u.nvme.cmd_dma,
+				   &pkt->dsd[0].address);
+		if (!sp->unsol_rsp) {
+			pkt->rx_byte_count = nvme->u.nvme.rsp_len;
+			pkt->dsd[1].length = nvme->u.nvme.rsp_len;
+			put_unaligned_le64(nvme->u.nvme.rsp_dma,
+					   &pkt->dsd[1].address);
+		}
+	}
 }
 
 static void
-qla25xx_ctrlvp_iocb(srb_t *sp, struct vp_ctrl_entry_24xx *vce)
+qla25xx_ctrlvp_iocb(srb_t *sp, void *pkt)
 {
+	/*
+	 * vp_ctrl_entry_24xx_ext is layout-identical to vp_ctrl_entry_24xx
+	 * for all fields touched here (entry_type, handle, entry_count,
+	 * command, vp_count, vp_idx_map) -- they all sit at the same
+	 * offsets and types in both structs, and the ext layout merely
+	 * tacks on flags/id/hopct/reserved at offset 32+.  So no
+	 * IS_QLA29XX(ha) dispatch is needed on the issue path.
+	 */
+	struct vp_ctrl_entry_24xx *vce = pkt;
 	int map, pos;
 
-	vce->entry_type = VP_CTRL_IOCB_TYPE;
-	vce->handle = sp->handle;
-	vce->entry_count = 1;
-	vce->command = cpu_to_le16(sp->u.iocb_cmd.u.ctrlvp.cmd);
-	vce->vp_count = cpu_to_le16(1);
-
 	/*
 	 * index map in firmware starts with 1; decrement index
 	 * this is ok as we never use index 0
 	 */
 	map = (sp->u.iocb_cmd.u.ctrlvp.vp_index - 1) / 8;
 	pos = (sp->u.iocb_cmd.u.ctrlvp.vp_index - 1) & 7;
+
+	vce->entry_type = VP_CTRL_IOCB_TYPE;
+	vce->handle = sp->handle;
+	vce->entry_count = 1;
+	vce->command = cpu_to_le16(sp->u.iocb_cmd.u.ctrlvp.cmd);
+	vce->vp_count = cpu_to_le16(1);
+	if (map >= ARRAY_SIZE(vce->vp_idx_map)) {
+		ql_log(ql_log_warn, sp->vha, 0x307c,
+		       "ctrlvp: vp_index %u exceeds vp_idx_map capacity\n",
+		       sp->u.iocb_cmd.u.ctrlvp.vp_index);
+		return;
+	}
 	vce->vp_idx_map[map] |= 1 << pos;
 }
 
 static void
-qla24xx_prlo_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+qla24xx_prlo_iocb(srb_t *sp, void *pkt)
 {
+	struct logio_entry_24xx *logio = pkt;
+
 	logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
 	logio->control_flags =
 	    cpu_to_le16(LCF_COMMAND_PRLO|LCF_IMPL_PRLO);
@@ -3840,7 +4150,7 @@ qla24xx_prlo_iocb(srb_t *sp, struct logio_entry_24xx *logio)
 	logio->port_id[0] = sp->fcport->d_id.b.al_pa;
 	logio->port_id[1] = sp->fcport->d_id.b.area;
 	logio->port_id[2] = sp->fcport->d_id.b.domain;
-	logio->vp_index = sp->fcport->vha->vp_idx;
+	qla_logio_set_vp_index(sp->vha->hw, pkt, sp->fcport->vha->vp_idx);
 }
 
 static int qla_get_iocbs_resource(struct srb *sp)
@@ -3903,16 +4213,31 @@ static int qla_get_iocbs_resource(struct srb *sp)
 }
 
 static void
-qla_marker_iocb(srb_t *sp, struct mrk_entry_24xx *mrk)
+qla_marker_iocb(srb_t *sp, void *pkt)
 {
+	struct qla_hw_data *ha = sp->vha->hw;
+	struct mrk_entry_24xx *mrk = pkt;
+
+	/*
+	 * mrk_entry_24xx_ext overlays mrk_entry_24xx through 'lun', so
+	 * the common-header writes go through the 24xx-typed pointer;
+	 * only vp_index (u8 in 24xx, __le16 in the ext layout) needs a
+	 * stride-aware branch.
+	 */
 	mrk->entry_type = MARKER_TYPE;
 	mrk->modifier = sp->u.iocb_cmd.u.tmf.modifier;
 	mrk->handle = make_handle(sp->qpair->req->id, sp->handle);
 	if (sp->u.iocb_cmd.u.tmf.modifier != MK_SYNC_ALL) {
-		mrk->nport_handle = cpu_to_le16(sp->u.iocb_cmd.u.tmf.loop_id);
-		int_to_scsilun(sp->u.iocb_cmd.u.tmf.lun, (struct scsi_lun *)&mrk->lun);
+		mrk->nport_handle =
+		    cpu_to_le16(sp->u.iocb_cmd.u.tmf.loop_id);
+		int_to_scsilun(sp->u.iocb_cmd.u.tmf.lun,
+		    (struct scsi_lun *)&mrk->lun);
 		host_to_fcp_swap(mrk->lun, sizeof(mrk->lun));
-		mrk->vp_index = sp->u.iocb_cmd.u.tmf.vp_index;
+		if (IS_QLA29XX(ha))
+			((struct mrk_entry_24xx_ext *)pkt)->vp_index =
+			    cpu_to_le16(sp->u.iocb_cmd.u.tmf.vp_index);
+		else
+			mrk->vp_index = sp->u.iocb_cmd.u.tmf.vp_index;
 	}
 }
 
@@ -3964,6 +4289,11 @@ qla2x00_start_sp(srb_t *sp)
 		break;
 	case SRB_ELS_CMD_HST_NOLOGIN:
 		qla_els_pt_iocb(sp->vha, pkt,  &sp->u.bsg_cmd.u.els_arg);
+		/*
+		 * els_entry_24xx::handle and els_entry_24xx_ext::handle are
+		 * both u32 at offset 4, so a 24xx-view write is layout-
+		 * compatible with both strides.
+		 */
 		((struct els_entry_24xx *)pkt)->handle = sp->handle;
 		break;
 	case SRB_CT_CMD:
@@ -4098,7 +4428,8 @@ qla25xx_build_bidir_iocb(srb_t *sp, struct scsi_qla_host *vha,
 			/* Continuation type 1 IOCB can accomodate
 			 * 5 DSDS
 			 */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req);
+			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+			    vha->hw, vha->req);
 			cur_dsd = cont_pkt->dsd;
 			avail_dsds = 5;
 			entry_count++;
@@ -4120,7 +4451,8 @@ qla25xx_build_bidir_iocb(srb_t *sp, struct scsi_qla_host *vha,
 			/* Continuation type 1 IOCB can accomodate
 			 * 5 DSDS
 			 */
-			cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req);
+			cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+			    vha->hw, vha->req);
 			cur_dsd = cont_pkt->dsd;
 			avail_dsds = 5;
 			entry_count++;
@@ -4152,6 +4484,21 @@ qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds)
 	rsp = ha->rsp_q_map[0];
 	req = vha->req;
 
+	/*
+	 * 29xx uses a 128-byte extended IOCB ring, but no extended
+	 * COMMAND_BIDIRECTIONAL IOCB layout has been defined and no
+	 * firmware-spec'd cmd_bidir_ext exists.  Posting the 64-byte
+	 * struct cmd_bidir against req->ring_ptr would overlap the next
+	 * ring slot on 29xx HW (see BUILD_BUG_ON(sizeof(struct cmd_bidir)
+	 * != 64) in qla_os.c), so refuse BSG BIDIR on 29xx rather than
+	 * corrupt the request ring.
+	 */
+	if (IS_QLA29XX(ha)) {
+		ql_log(ql_log_warn, vha, 0x70af,
+		    "BSG BIDIR not supported on 29xx adapters.\n");
+		return EXT_STATUS_INVALID_PARAM;
+	}
+
 	/* Send marker if required */
 	if (vha->marker_needed != 0) {
 		if (qla2x00_marker(vha, ha->base_qpair,
@@ -4289,8 +4636,15 @@ qla_start_scsi_type6(srb_t *sp)
 
 	tot_dsds = nseg;
 
-	/* eventhough driver only need 1 T6 IOCB, FW still convert DSD to Continueation IOCB */
-	req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+	/*
+	 * Even though the driver only needs 1 T6 IOCB, FW still converts the
+	 * DSD list into continuation IOCBs for reservation purposes.  29xx
+	 * follows the same rule against its extended-IOCB inline DSD count.
+	 */
+	if (IS_QLA29XX(ha))
+		req_cnt = qla29xx_calc_iocbs(vha, tot_dsds, NUM_CMD67_DSDS);
+	else
+		req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
 
 	sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
 	sp->iores.exch_cnt = 1;
@@ -4394,23 +4748,44 @@ qla_start_scsi_type6(srb_t *sp)
 	cmd->host_scribble = (unsigned char *)(unsigned long)handle;
 	req->cnt -= req_cnt;
 
-	cmd_pkt = (struct cmd_type_6 *)req->ring_ptr;
+	/*
+	 * cmd_type_6 and cmd_type_6_ext share an identical header layout
+	 * from offset 0 through byte_count (offset 48), so the common
+	 * header/nport/lun writes go through the 24xx-typed cmd_pkt even on
+	 * 29xx.  Only the divergent port_id/vp_index tail and the DSD layout
+	 * need hardware-specific handling.
+	 */
+	if (IS_QLA29XX(ha))
+		cmd_pkt = (struct cmd_type_6 *)req->ring_ext_ptr;
+	else
+		cmd_pkt = (struct cmd_type_6 *)req->ring_ptr;
 	cmd_pkt->handle = make_handle(req->id, handle);
 
 	/* tagged queuing modifier -- default is TSK_SIMPLE (0). */
 	clr_ptr = (uint32_t *)cmd_pkt + 2;
-	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+	memset(clr_ptr, 0, qla_req_entry_size(ha) - 8);
 	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
 
 	/* Set NPORT-ID and LUN number */
 	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
-	cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
-	cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
-	cmd_pkt->vp_index = sp->vha->vp_idx;
 
-	/* Build IOCB segments */
-	qla24xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds);
+	if (IS_QLA29XX(ha)) {
+		struct cmd_type_6_ext *cmd_pkt_ext =
+			(struct cmd_type_6_ext *)cmd_pkt;
+
+		cmd_pkt_ext->vp_index = cpu_to_le16(sp->vha->vp_idx);
+
+		/* Build IOCB segments (dsd[] in 29xx extended layout). */
+		qla29xx_build_scsi_type_6_iocbs(sp, cmd_pkt_ext, tot_dsds);
+	} else {
+		cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+		cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+		cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+		cmd_pkt->vp_index = sp->vha->vp_idx;
+
+		/* Build IOCB segments (24xx fcp_dsd single-DSD layout). */
+		qla24xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds);
+	}
 
 	int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
 	host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
@@ -4445,14 +4820,8 @@ qla_start_scsi_type6(srb_t *sp)
 	cmd_pkt->entry_count = (uint8_t)req_cnt;
 
 	wmb();
-	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else {
-		req->ring_ptr++;
-	}
+	/* Adjust ring index. 29xx uses the 128-byte extended ring pointer.*/
+	qla_req_ring_advance(ha, req);
 
 	sp->qpair->cmd_cnt++;
 	sp->flags |= SRB_DMA_VALID;
@@ -4460,8 +4829,11 @@ qla_start_scsi_type6(srb_t *sp)
 	/* Set chip new ring index. */
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 
-	/* Manage unprocessed RIO/ZIO commands in response queue. */
-	if (vha->flags.process_response_queue &&
+	/*
+	 * Manage unprocessed RIO/ZIO commands in response queue. 29xx skips
+	 * this optimization until rsp_que is made 128-byte-stride aware.
+	 */
+	if (!IS_QLA29XX(ha) &&  vha->flags.process_response_queue &&
 	    rsp->ring_ptr->signature != RESPONSE_PROCESSED)
 		qla24xx_process_response_queue(vha, rsp);
 
@@ -4487,3 +4859,492 @@ qla_start_scsi_type6(srb_t *sp)
 
 	return QLA_FUNCTION_FAILED;
 }
+
+/*
+ * ---------------------------------------------------------------------------
+ * 29xx extended (128-byte) IOCB fast-path helpers.
+ *
+ * These helpers drive the extended IOCB ring used by 29xx-class adapters and
+ * operate on the *_ext IOCB variants declared in qla_fw29.h
+ * (cmd_type_6_ext, cmd_type_7_ext, cmd_type_crc_2_ext, struct cont_a64_entry_ext).
+ *
+ * They are not yet wired into any dispatch path; the top-level entry points
+ * (e.g. qla29xx_start_scsi / qla29xx_dif_start_scsi_mq) and isp_ops hooks are
+ * added in subsequent merge phases.  Until then the top-of-tree builder
+ * functions are marked __maybe_unused so the build stays warning-clean.
+ *
+ * Ported from the out-of-tree qla29xx driver.  Signatures have been adapted
+ * to the Mach qla2xxx single-srb_t model:
+ *   struct qla_io_srb *  -->  srb_t *
+ *   sp->qcb.qpair        -->  sp->qpair
+ *   sp->qcb.iores        -->  sp->iores
+ *   sp->qcb.handle       -->  sp->handle
+ *   qla2900_hba_err_chk_enabled()  -->  qla2x00_hba_err_chk_enabled()
+ * ---------------------------------------------------------------------------
+ */
+
+static void qla29xx_copy_dif_iocb_data(struct cmd_type_crc_2_ext *cmd_pkt,
+	struct crc_context *crc_ctx_pkt, uint8_t dif_bundling);
+
+/**
+ * qla2900_prep_cont_type1_iocb() - Initialize a 29xx-ext Continuation
+ * Type 1 IOCB on the extended IOCB ring.
+ * @vha: HA context
+ * @req: request queue
+ *
+ * Returns a pointer to the continuation type 1 IOCB packet.
+ */
+struct cont_a64_entry_ext *
+qla2900_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req)
+{
+	struct cont_a64_entry_ext *cont_pkt;
+
+	/*
+	 * 29xx uses the 128-byte extended IOCB ring.  Advance via ring_ext_ptr
+	 * so the pointer arithmetic matches the on-ring stride.
+	 */
+	req->ring_index++;
+	if (req->ring_index == req->length) {
+		req->ring_index = 0;
+		req->ring_ext_ptr = req->ring_ext;
+	} else {
+		req->ring_ext_ptr++;
+	}
+
+	cont_pkt = (struct cont_a64_entry_ext *)req->ring_ext_ptr;
+
+	/* Load packet defaults. */
+	put_unaligned_le32(CONTINUE_A64_TYPE, &cont_pkt->entry_type);
+
+	return cont_pkt;
+}
+
+/*
+ * Note: the 29xx extended path reuses qla24xx_configure_prot_mode() -- the
+ * SCSI-prot-op to PO_MODE_DIF_* translation is independent of the IOCB
+ * generation.
+ */
+
+/**
+ * qla29xx_build_scsi_type_6_iocbs() - Build IOCB command utilizing Command
+ * Type 6 IOCB types on the 29xx extended ring.
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type 6 extended IOCB
+ * @tot_dsds: Total number of segments to transfer
+ */
+static void
+qla29xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6_ext *cmd_pkt,
+	uint16_t tot_dsds)
+{
+	struct dsd64 *cur_dsd = NULL, *next_dsd;
+	struct scsi_cmnd *cmd;
+	struct	scatterlist *cur_seg;
+	uint8_t avail_dsds;
+	uint8_t first_iocb = 1;
+	uint32_t dsd_list_len;
+	struct dsd_dma *dsd_ptr;
+	struct ct6_dsd *ctx;
+	struct qla_qpair *qpair = sp->qpair;
+
+	cmd = GET_CMD_SP(sp);
+
+	/* Update entry type to indicate Command Type 6 IOCB */
+	put_unaligned_le32(COMMAND_TYPE_6, &cmd_pkt->entry_type);
+
+	/* No data transfer */
+	if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) {
+		cmd_pkt->byte_count = cpu_to_le32(0);
+		goto function_end;
+	}
+
+	/* Set transfer direction */
+	if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+		cmd_pkt->control_flags = cpu_to_le16(CF_WRITE_DATA);
+		qpair->counters.output_bytes += scsi_bufflen(cmd);
+		qpair->counters.output_requests++;
+	} else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+		cmd_pkt->control_flags = cpu_to_le16(CF_READ_DATA);
+		qpair->counters.input_bytes += scsi_bufflen(cmd);
+		qpair->counters.input_requests++;
+	}
+
+	cur_seg = scsi_sglist(cmd);
+	ctx = &sp->u.scmd.ct6_ctx;
+
+	while (tot_dsds) {
+		avail_dsds = (tot_dsds > QLA_DSDS_PER_IOCB) ?
+		    QLA_DSDS_PER_IOCB : tot_dsds;
+		tot_dsds -= avail_dsds;
+		dsd_list_len = (avail_dsds + 1) * QLA_DSD_SIZE;
+
+		dsd_ptr = list_first_entry(&qpair->dsd_list, struct dsd_dma,
+					   list);
+		next_dsd = dsd_ptr->dsd_addr;
+		list_del(&dsd_ptr->list);
+		qpair->dsd_avail--;
+		list_add_tail(&dsd_ptr->list, &ctx->dsd_list);
+		ctx->dsd_use_cnt++;
+		qpair->dsd_inuse++;
+
+		if (first_iocb) {
+			first_iocb = 0;
+			put_unaligned_le64(dsd_ptr->dsd_list_dma,
+					   &cmd_pkt->dsd[0].address);
+			cmd_pkt->dsd[0].length = cpu_to_le32(dsd_list_len);
+		} else {
+			put_unaligned_le64(dsd_ptr->dsd_list_dma,
+					   &cur_dsd->address);
+			cur_dsd->length = cpu_to_le32(dsd_list_len);
+			cur_dsd++;
+		}
+		cur_dsd = next_dsd;
+		while (avail_dsds) {
+			append_dsd64(&cur_dsd, cur_seg);
+			cur_seg = sg_next(cur_seg);
+			avail_dsds--;
+		}
+	}
+
+	/* Null termination */
+	if (cur_dsd) {
+		cur_dsd->address = 0;
+		cur_dsd->length = 0;
+		cur_dsd++;
+	}
+	cmd_pkt->control_flags |= cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE);
+function_end:
+	return;
+}
+
+/*
+ * Note: the 29xx extended path reuses qla24xx_calc_dsd_lists() -- the
+ * DSD-list-per-IOCB arithmetic (based on QLA_DSDS_PER_IOCB) is generation-
+ * agnostic.
+ */
+
+/**
+ * qla29xx_build_scsi_iocbs() - Build IOCB command utilizing Command Type 7
+ * IOCB types on the 29xx extended ring.
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type 7 extended IOCB
+ * @tot_dsds: Total number of segments to transfer
+ * @req: pointer to request queue
+ */
+static void __maybe_unused
+qla29xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7_ext *cmd_pkt,
+	uint16_t tot_dsds, struct req_que *req)
+{
+	uint8_t	avail_dsds;
+	struct dsd64 *cur_dsd;
+	scsi_qla_host_t	*vha;
+	struct scsi_cmnd *cmd;
+	struct scatterlist *sg;
+	int i;
+	struct qla_qpair *qpair = sp->qpair;
+
+	cmd = GET_CMD_SP(sp);
+
+	/* Update entry type to indicate Command Type 7 IOCB */
+	put_unaligned_le32(COMMAND_TYPE_7, &cmd_pkt->entry_type);
+
+	/* No data transfer */
+	if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) {
+		cmd_pkt->byte_count = cpu_to_le32(0);
+		return;
+	}
+
+	vha = sp->vha;
+
+	/* Set transfer direction */
+	if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+		cmd_pkt->task_mgmt_flags = cpu_to_le16(TMF_WRITE_DATA);
+		qpair->counters.output_bytes += scsi_bufflen(cmd);
+		qpair->counters.output_requests++;
+	} else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+		cmd_pkt->task_mgmt_flags = cpu_to_le16(TMF_READ_DATA);
+		qpair->counters.input_bytes += scsi_bufflen(cmd);
+		qpair->counters.input_requests++;
+	}
+
+	/* NUM_CMD67_DSDS DSDs available in the Command Type 7 ext IOCB */
+	avail_dsds = NUM_CMD67_DSDS;
+	cur_dsd = &cmd_pkt->dsd[0];
+
+	scsi_for_each_sg(cmd, sg, tot_dsds, i) {
+		struct cont_a64_entry_ext *cont_pkt;
+
+		/* Allocate additional continuation packets? */
+		if (avail_dsds == 0) {
+			/*
+			 * NUM_CONT1_DSDS DSDs are available in the Continuation
+			 * Type 1 extended IOCB.
+			 */
+			cont_pkt = qla2900_prep_cont_type1_iocb(vha, req);
+			cur_dsd = cont_pkt->dsd;
+			avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
+		}
+
+		append_dsd64(&cur_dsd, sg);
+		avail_dsds--;
+	}
+}
+
+/*
+ * Note: the 29xx extended path reuses qla24xx_set_t10dif_tags() (defined
+ * earlier in this file) -- the T10-DIF ref/app tag layout is identical
+ * between the 24xx and 29xx extended CRC_2 IOCBs (both share the same
+ * fw_dif_context layout inside struct crc_context).
+ */
+
+/*
+ * Note: the 29xx extended CRC_2 path reuses the 24xx SG walkers
+ * (qla24xx_get_one_block_sg, qla24xx_walk_and_build_sglist_no_difb,
+ * qla24xx_walk_and_build_sglist, qla24xx_walk_and_build_prot_sglist).
+ * The DSD-chain / CRC-context layout they produce is generation-agnostic;
+ * only the IOCB that points at the chain (Type-6/7/CRC_2 vs their _ext
+ * variants) differs between generations, and that is handled by the
+ * separate qla29xx_build_* builders.
+ *
+ * The walkers that take a struct qla_tc_param *tc are invoked with tc=NULL
+ * here because the 29xx fast-path does not use the tape/copy offload.
+ */
+
+/**
+ * qla29xx_build_scsi_crc_2_iocbs() - Build IOCB command utilizing Command
+ * Type CRC_2 IOCB types on the 29xx extended ring.
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type CRC_2 extended IOCB
+ * @tot_dsds: Total number of segments to transfer
+ * @tot_prot_dsds: Total number of segments with protection information
+ * @fw_prot_opts: Protection options to be passed to firmware
+ *
+ * Returns 0 if successful, 1 otherwise.
+ */
+static int
+qla29xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2_ext *cmd_pkt,
+	uint16_t tot_dsds, uint16_t tot_prot_dsds, uint16_t fw_prot_opts)
+{
+	struct dsd64		*cur_dsd;
+	__be32			*fcp_dl;
+	scsi_qla_host_t		*vha;
+	struct scsi_cmnd	*cmd;
+	uint32_t		total_bytes = 0;
+	uint32_t		data_bytes;
+	uint32_t		dif_bytes;
+	uint8_t			bundling = 1;
+	uint16_t		blk_size;
+	struct crc_context	*crc_ctx_pkt = NULL;
+	struct qla_hw_data	*ha;
+	uint8_t			additional_fcpcdb_len;
+	uint16_t		fcp_cmnd_len;
+	struct fcp_cmnd		*fcp_cmnd;
+	dma_addr_t		crc_ctx_dma;
+
+	cmd = GET_CMD_SP(sp);
+
+	/* Update entry type to indicate Command Type CRC_2 IOCB */
+	put_unaligned_le32(COMMAND_TYPE_CRC_2, &cmd_pkt->entry_type);
+
+	vha = sp->vha;
+	ha = vha->hw;
+
+	/* No data transfer */
+	data_bytes = scsi_bufflen(cmd);
+	if (!data_bytes || cmd->sc_data_direction == DMA_NONE) {
+		cmd_pkt->byte_count = cpu_to_le32(0);
+		return QLA_SUCCESS;
+	}
+
+	cmd_pkt->vp_index = cpu_to_le16(sp->vha->vp_idx);
+
+	/* Set transfer direction */
+	if (cmd->sc_data_direction == DMA_TO_DEVICE)
+		cmd_pkt->control_flags_1 = cpu_to_le16(CF_WRITE_DATA);
+	else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+		cmd_pkt->control_flags_1 = cpu_to_le16(CF_READ_DATA);
+
+	if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) ||
+	    (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_STRIP) ||
+	    (scsi_get_prot_op(cmd) == SCSI_PROT_READ_STRIP) ||
+	    (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_INSERT))
+		bundling = 0;
+
+	/* Allocate CRC context from global pool */
+	crc_ctx_pkt = sp->u.scmd.crc_ctx =
+	    dma_pool_zalloc(ha->dl_dma_pool, GFP_ATOMIC, &crc_ctx_dma);
+
+	if (!crc_ctx_pkt)
+		goto crc_queuing_error;
+
+	crc_ctx_pkt->crc_ctx_dma = crc_ctx_dma;
+
+	sp->flags |= SRB_CRC_CTX_DMA_VALID;
+
+	/* Set handle */
+	crc_ctx_pkt->handle = cmd_pkt->handle;
+
+	INIT_LIST_HEAD(&crc_ctx_pkt->dsd_list);
+
+	qla24xx_set_t10dif_tags(sp, (struct fw_dif_context *)
+	    &crc_ctx_pkt->ref_tag, tot_prot_dsds);
+
+	/* Determine SCSI command length -- align to 4 byte boundary */
+	if (cmd->cmd_len > 16) {
+		additional_fcpcdb_len = cmd->cmd_len - 16;
+		if ((cmd->cmd_len % 4) != 0) {
+			/* SCSI cmd > 16 bytes must be multiple of 4 */
+			goto crc_queuing_error;
+		}
+		fcp_cmnd_len = 12 + cmd->cmd_len + 4;
+	} else {
+		additional_fcpcdb_len = 0;
+		fcp_cmnd_len = 12 + 16 + 4;
+	}
+
+	fcp_cmnd = &crc_ctx_pkt->fcp_cmnd;
+
+	fcp_cmnd->additional_cdb_len = additional_fcpcdb_len;
+	if (cmd->sc_data_direction == DMA_TO_DEVICE)
+		fcp_cmnd->additional_cdb_len |= 1;
+	else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+		fcp_cmnd->additional_cdb_len |= 2;
+
+	int_to_scsilun(cmd->device->lun, &fcp_cmnd->lun);
+	memcpy(fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len);
+	cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(fcp_cmnd_len);
+	put_unaligned_le64(crc_ctx_dma + CRC_CONTEXT_FCPCMND_OFF,
+			   &cmd_pkt->fcp_cmnd_dseg_address);
+	fcp_cmnd->task_management = 0;
+	fcp_cmnd->task_attribute = TSK_SIMPLE;
+
+	cmd_pkt->fcp_rsp_dseg_len = 0; /* Let response come in status iocb */
+
+	/* Compute dif len and adjust data len to include protection */
+	dif_bytes = 0;
+	blk_size = cmd->device->sector_size;
+	dif_bytes = (data_bytes / blk_size) * 8;
+
+	switch (scsi_get_prot_op(GET_CMD_SP(sp))) {
+	case SCSI_PROT_READ_INSERT:
+	case SCSI_PROT_WRITE_STRIP:
+		total_bytes = data_bytes;
+		data_bytes += dif_bytes;
+		break;
+
+	case SCSI_PROT_READ_STRIP:
+	case SCSI_PROT_WRITE_INSERT:
+	case SCSI_PROT_READ_PASS:
+	case SCSI_PROT_WRITE_PASS:
+		total_bytes = data_bytes + dif_bytes;
+		break;
+	default:
+		WARN_ON_ONCE(1);
+		return 1;
+	}
+
+	if (!qla2x00_hba_err_chk_enabled(sp))
+		fw_prot_opts |= 0x10; /* Disable Guard tag checking */
+	/* HBA error checking enabled */
+	else {
+		if ((scsi_get_prot_type(GET_CMD_SP(sp)) == SCSI_PROT_DIF_TYPE1)
+		    || (scsi_get_prot_type(GET_CMD_SP(sp)) ==
+			SCSI_PROT_DIF_TYPE2))
+			fw_prot_opts |= BIT_10;
+		else if (scsi_get_prot_type(GET_CMD_SP(sp)) ==
+		    SCSI_PROT_DIF_TYPE3)
+			fw_prot_opts |= BIT_11;
+	}
+
+	if (!bundling) {
+		cur_dsd = &crc_ctx_pkt->u.nobundling.data_dsd[0];
+	} else {
+		/*
+		 * Configure Bundling if we need to fetch interleaving
+		 * protection PCI accesses
+		 */
+		fw_prot_opts |= PO_ENABLE_DIF_BUNDLING;
+		crc_ctx_pkt->u.bundling.dif_byte_count = cpu_to_le32(dif_bytes);
+		crc_ctx_pkt->u.bundling.dseg_count = cpu_to_le16(tot_dsds -
+							tot_prot_dsds);
+		cur_dsd = &crc_ctx_pkt->u.bundling.data_dsd[0];
+	}
+
+	/* Finish the common fields of CRC pkt */
+	crc_ctx_pkt->blk_size = cpu_to_le16(blk_size);
+	crc_ctx_pkt->prot_opts = cpu_to_le16(fw_prot_opts);
+	crc_ctx_pkt->byte_count = cpu_to_le32(data_bytes);
+	crc_ctx_pkt->guard_seed = cpu_to_le16(0);
+	/* Fibre channel byte count */
+	cmd_pkt->byte_count = cpu_to_le32(total_bytes);
+	fcp_dl = (__be32 *)(crc_ctx_pkt->fcp_cmnd.cdb + 16 +
+	    additional_fcpcdb_len);
+	*fcp_dl = htonl(total_bytes);
+
+	/* Walks data segments */
+
+	cmd_pkt->control_flags_1 |= cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE);
+
+	if (!bundling && tot_prot_dsds) {
+		if (qla24xx_walk_and_build_sglist_no_difb(ha, sp,
+			cur_dsd, tot_dsds, NULL))
+			goto crc_queuing_error;
+	} else if (qla24xx_walk_and_build_sglist(ha, sp, cur_dsd,
+			(tot_dsds - tot_prot_dsds), NULL))
+		goto crc_queuing_error;
+
+	if (bundling && tot_prot_dsds) {
+		/* Walks dif segments */
+		cmd_pkt->control_flags_1 |= cpu_to_le16(CF_DIF_SEG_DESCR_ENABLE);
+		cur_dsd = &crc_ctx_pkt->u.bundling.dif_dsd;
+		if (qla24xx_walk_and_build_prot_sglist(ha, sp, cur_dsd,
+				tot_prot_dsds, NULL))
+			goto crc_queuing_error;
+	}
+	qla29xx_copy_dif_iocb_data(cmd_pkt, crc_ctx_pkt, bundling);
+
+	return QLA_SUCCESS;
+
+crc_queuing_error:
+	/* Cleanup will be performed by the caller */
+	return QLA_FUNCTION_FAILED;
+}
+
+/**
+ * qla29xx_copy_dif_iocb_data() - Populate the DIF-related fields of an
+ * extended CRC_2 command IOCB from a freshly populated crc_context.
+ * @cmd_pkt: Extended CRC_2 command IOCB
+ * @crc_ctx_pkt: Source CRC context
+ * @dif_bundling: DIF bundling flag
+ */
+static void
+qla29xx_copy_dif_iocb_data(struct cmd_type_crc_2_ext *cmd_pkt,
+	struct crc_context *crc_ctx_pkt, uint8_t dif_bundling)
+{
+	cmd_pkt->ref_tag = crc_ctx_pkt->ref_tag;
+	cmd_pkt->app_tag = crc_ctx_pkt->app_tag;
+	memcpy(cmd_pkt->ref_tag_mask, crc_ctx_pkt->ref_tag_mask,
+	       sizeof(cmd_pkt->ref_tag_mask));
+	memcpy(cmd_pkt->app_tag_mask, crc_ctx_pkt->app_tag_mask,
+	       sizeof(cmd_pkt->app_tag_mask));
+	cmd_pkt->blk_size = crc_ctx_pkt->blk_size;
+	cmd_pkt->prot_opts = crc_ctx_pkt->prot_opts;
+	cmd_pkt->tot_byte_count = crc_ctx_pkt->byte_count;
+	if (dif_bundling) {
+		cmd_pkt->u.bundling.guard_seed = crc_ctx_pkt->guard_seed;
+		cmd_pkt->u.bundling.dif_byte_count =
+			crc_ctx_pkt->u.bundling.dif_byte_count;
+		cmd_pkt->u.bundling.dseg_count =
+			crc_ctx_pkt->u.bundling.dseg_count;
+		memcpy(cmd_pkt->u.bundling.data_dsd,
+		       crc_ctx_pkt->u.bundling.data_dsd,
+		       sizeof(cmd_pkt->u.bundling.data_dsd));
+		memcpy(&cmd_pkt->u.bundling.dif_dsd,
+		       &crc_ctx_pkt->u.bundling.dif_dsd,
+		       sizeof(cmd_pkt->u.bundling.dif_dsd));
+	} else {
+		cmd_pkt->u.nobundling.guard_seed = crc_ctx_pkt->guard_seed;
+		memcpy(cmd_pkt->u.nobundling.data_dsd,
+		       crc_ctx_pkt->u.nobundling.data_dsd,
+		       sizeof(cmd_pkt->u.nobundling.data_dsd));
+	}
+}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 33776330956c..63b70d4abbf0 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -19,7 +19,7 @@
 
 static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t);
 static void qla2x00_status_entry(scsi_qla_host_t *, struct rsp_que *, void *);
-static void qla2x00_status_cont_entry(struct rsp_que *, sts_cont_entry_t *);
+static void qla2x00_status_cont_entry(struct rsp_que *, void *, u32);
 static int qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *,
 	sts_entry_t *);
 static void qla27xx_process_purex_fpin(struct scsi_qla_host *vha,
@@ -72,51 +72,130 @@ static inline void display_Laser_info(scsi_qla_host_t *vha,
 		       mb3);
 }
 
+/*
+ * Type-generic helpers for qla24xx_process_abts().
+ * Both abts_entry_24xx and abts_entry_24xx_ext share field names for all
+ * accessed fields, so these macros expand correctly for either type.
+ */
+#define QLA_LOG_ABTS_RCV(vha, abts_ptr) do {				\
+	ql_log(ql_log_warn, (vha), 0x0287,				\
+	    "Processing ABTS xchg=%#x oxid=%#x rxid=%#x "		\
+	    "seqid=%#x seqcnt=%#x\n",					\
+	    (abts_ptr)->rx_xch_addr_to_abort, (abts_ptr)->ox_id,	\
+	    (abts_ptr)->rx_id, (abts_ptr)->seq_id,			\
+	    (abts_ptr)->seq_cnt);					\
+	ql_dbg(ql_dbg_init + ql_dbg_verbose, (vha), 0x0287,		\
+	    "-------- ABTS RCV -------\n");				\
+	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, (vha), 0x0287,	\
+	    (uint8_t *)(abts_ptr), sizeof(*(abts_ptr)));		\
+} while (0)
+
+#define QLA_BUILD_ABTS_BA_ACC(rsp, src, fctl) do {			\
+	memset((rsp), 0, sizeof(*(rsp)));				\
+	(rsp)->entry_type = ABTS_RSP_TYPE;				\
+	(rsp)->entry_count = 1;						\
+	(rsp)->nport_handle = (src)->nport_handle;			\
+	(rsp)->rx_xch_addr = (src)->rx_xch_addr;			\
+	(rsp)->d_id[0] = (src)->s_id[0];				\
+	(rsp)->d_id[1] = (src)->s_id[1];				\
+	(rsp)->d_id[2] = (src)->s_id[2];				\
+	(rsp)->r_ctl = FC_ROUTING_BLD | FC_R_CTL_BLD_BA_ACC;		\
+	(rsp)->s_id[0] = (src)->d_id[0];				\
+	(rsp)->s_id[1] = (src)->d_id[1];				\
+	(rsp)->s_id[2] = (src)->d_id[2];				\
+	(rsp)->cs_ctl = (src)->cs_ctl;					\
+	(fctl) = ~((src)->f_ctl[2] | 0x7F) << 16 |			\
+	    FC_F_CTL_LAST_SEQ | FC_F_CTL_END_SEQ | FC_F_CTL_SEQ_INIT;	\
+	(rsp)->f_ctl[0] = (fctl) >> 0 & 0xff;				\
+	(rsp)->f_ctl[1] = (fctl) >> 8 & 0xff;				\
+	(rsp)->f_ctl[2] = (fctl) >> 16 & 0xff;				\
+	(rsp)->type = FC_TYPE_BLD;					\
+	(rsp)->rx_id = (src)->rx_id;					\
+	(rsp)->ox_id = (src)->ox_id;					\
+	(rsp)->payload.ba_acc.aborted_rx_id = (src)->rx_id;		\
+	(rsp)->payload.ba_acc.aborted_ox_id = (src)->ox_id;		\
+	(rsp)->payload.ba_acc.high_seq_cnt = cpu_to_le16(~0);		\
+	(rsp)->rx_xch_addr_to_abort = (src)->rx_xch_addr_to_abort;	\
+} while (0)
+
+#define QLA_LOG_ISSUE_ABTS_RSP(vha, rsp, dma, rval) do {		\
+	ql_dbg(ql_dbg_init, (vha), 0x028b,				\
+	    "Sending BA ACC response to ABTS %#x...\n",			\
+	    (rsp)->rx_xch_addr_to_abort);				\
+	ql_dbg(ql_dbg_init + ql_dbg_verbose, (vha), 0x028b,		\
+	    "-------- ELS RSP -------\n");				\
+	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, (vha), 0x028b,	\
+	    (uint8_t *)(rsp), sizeof(*(rsp)));				\
+	(rval) = qla2x00_issue_iocb((vha), (rsp), (dma), 0);		\
+	if (rval) {							\
+		ql_log(ql_log_warn, (vha), 0x028c,			\
+		    "%s: iocb failed to execute -> %x\n",		\
+		    __func__, (rval));					\
+	} else if ((rsp)->comp_status) {				\
+		ql_log(ql_log_warn, (vha), 0x028d,			\
+		    "%s: iocb failed to complete -> "			\
+		    "completion=%#x subcode=(%#x,%#x)\n",		\
+		    __func__, (rsp)->comp_status,			\
+		    (rsp)->payload.error.subcode1,			\
+		    (rsp)->payload.error.subcode2);			\
+	} else {							\
+		ql_dbg(ql_dbg_init, (vha), 0x028ea,			\
+		    "%s: done.\n", __func__);				\
+	}								\
+} while (0)
+
 static void
 qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
 {
-	struct abts_entry_24xx *abts =
-	    (struct abts_entry_24xx *)&pkt->iocb;
 	struct qla_hw_data *ha = vha->hw;
+	struct abts_entry_24xx *abts = NULL;
+	struct abts_entry_24xx_ext *abts_ext = NULL;
 	struct els_entry_24xx *rsp_els;
-	struct abts_entry_24xx *abts_rsp;
 	dma_addr_t dma;
+	__le32 rx_xch_addr_to_abort;
 	uint32_t fctl;
 	int rval;
+	void *rsp_pkt;
+	size_t rsp_sz;
 
 	ql_dbg(ql_dbg_init, vha, 0x0286, "%s: entered.\n", __func__);
 
-	ql_log(ql_log_warn, vha, 0x0287,
-	    "Processing ABTS xchg=%#x oxid=%#x rxid=%#x seqid=%#x seqcnt=%#x\n",
-	    abts->rx_xch_addr_to_abort, abts->ox_id, abts->rx_id,
-	    abts->seq_id, abts->seq_cnt);
-	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0287,
-	    "-------- ABTS RCV -------\n");
-	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0287,
-	    (uint8_t *)abts, sizeof(*abts));
+	if (IS_QLA29XX(ha)) {
+		abts_ext = (struct abts_entry_24xx_ext *)&pkt->iocb;
+		QLA_LOG_ABTS_RCV(vha, abts_ext);
+		rsp_sz = sizeof(struct els_entry_24xx_ext);
+		rx_xch_addr_to_abort = abts_ext->rx_xch_addr_to_abort;
+	} else {
+		abts = (struct abts_entry_24xx *)&pkt->iocb;
+		QLA_LOG_ABTS_RCV(vha, abts);
+		rsp_sz = sizeof(struct els_entry_24xx);
+		rx_xch_addr_to_abort = abts->rx_xch_addr_to_abort;
+	}
 
-	rsp_els = dma_alloc_coherent(&ha->pdev->dev, sizeof(*rsp_els), &dma,
+	rsp_pkt = dma_alloc_coherent(&ha->pdev->dev, rsp_sz, &dma,
 	    GFP_KERNEL);
-	if (!rsp_els) {
+	if (!rsp_pkt) {
 		ql_log(ql_log_warn, vha, 0x0287,
 		    "Failed allocate dma buffer ABTS/ELS RSP.\n");
 		return;
 	}
+	rsp_els = rsp_pkt;
 
 	/* terminate exchange */
+	memset(rsp_pkt, 0, rsp_sz);
 	rsp_els->entry_type = ELS_IOCB_TYPE;
 	rsp_els->entry_count = 1;
 	rsp_els->nport_handle = cpu_to_le16(~0);
-	rsp_els->rx_xchg_address = abts->rx_xch_addr_to_abort;
+	rsp_els->rx_xchg_address = rx_xch_addr_to_abort;
 	rsp_els->control_flags = cpu_to_le16(EPD_RX_XCHG);
 	ql_dbg(ql_dbg_init, vha, 0x0283,
 	    "Sending ELS Response to terminate exchange %#x...\n",
-	    abts->rx_xch_addr_to_abort);
+	    rx_xch_addr_to_abort);
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0283,
 	    "-------- ELS RSP -------\n");
 	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0283,
-	    (uint8_t *)rsp_els, sizeof(*rsp_els));
-	rval = qla2x00_issue_iocb(vha, rsp_els, dma, 0);
+	    (uint8_t *)rsp_pkt, rsp_sz);
+	rval = qla2x00_issue_iocb(vha, rsp_pkt, dma, 0);
 	if (rval) {
 		ql_log(ql_log_warn, vha, 0x0288,
 		    "%s: iocb failed to execute -> %x\n", __func__, rval);
@@ -131,60 +210,30 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt)
 	}
 
 	/* send ABTS response */
-	abts_rsp = (void *)rsp_els;
-	memset(abts_rsp, 0, sizeof(*abts_rsp));
-	abts_rsp->entry_type = ABTS_RSP_TYPE;
-	abts_rsp->entry_count = 1;
-	abts_rsp->nport_handle = abts->nport_handle;
-	abts_rsp->vp_idx = abts->vp_idx;
-	abts_rsp->sof_type = abts->sof_type & 0xf0;
-	abts_rsp->rx_xch_addr = abts->rx_xch_addr;
-	abts_rsp->d_id[0] = abts->s_id[0];
-	abts_rsp->d_id[1] = abts->s_id[1];
-	abts_rsp->d_id[2] = abts->s_id[2];
-	abts_rsp->r_ctl = FC_ROUTING_BLD | FC_R_CTL_BLD_BA_ACC;
-	abts_rsp->s_id[0] = abts->d_id[0];
-	abts_rsp->s_id[1] = abts->d_id[1];
-	abts_rsp->s_id[2] = abts->d_id[2];
-	abts_rsp->cs_ctl = abts->cs_ctl;
-	/* include flipping bit23 in fctl */
-	fctl = ~(abts->f_ctl[2] | 0x7F) << 16 |
-	    FC_F_CTL_LAST_SEQ | FC_F_CTL_END_SEQ | FC_F_CTL_SEQ_INIT;
-	abts_rsp->f_ctl[0] = fctl >> 0 & 0xff;
-	abts_rsp->f_ctl[1] = fctl >> 8 & 0xff;
-	abts_rsp->f_ctl[2] = fctl >> 16 & 0xff;
-	abts_rsp->type = FC_TYPE_BLD;
-	abts_rsp->rx_id = abts->rx_id;
-	abts_rsp->ox_id = abts->ox_id;
-	abts_rsp->payload.ba_acc.aborted_rx_id = abts->rx_id;
-	abts_rsp->payload.ba_acc.aborted_ox_id = abts->ox_id;
-	abts_rsp->payload.ba_acc.high_seq_cnt = cpu_to_le16(~0);
-	abts_rsp->rx_xch_addr_to_abort = abts->rx_xch_addr_to_abort;
-	ql_dbg(ql_dbg_init, vha, 0x028b,
-	    "Sending BA ACC response to ABTS %#x...\n",
-	    abts->rx_xch_addr_to_abort);
-	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x028b,
-	    "-------- ELS RSP -------\n");
-	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x028b,
-	    (uint8_t *)abts_rsp, sizeof(*abts_rsp));
-	rval = qla2x00_issue_iocb(vha, abts_rsp, dma, 0);
-	if (rval) {
-		ql_log(ql_log_warn, vha, 0x028c,
-		    "%s: iocb failed to execute -> %x\n", __func__, rval);
-	} else if (abts_rsp->comp_status) {
-		ql_log(ql_log_warn, vha, 0x028d,
-		    "%s: iocb failed to complete -> completion=%#x subcode=(%#x,%#x)\n",
-		    __func__, abts_rsp->comp_status,
-		    abts_rsp->payload.error.subcode1,
-		    abts_rsp->payload.error.subcode2);
+	if (IS_QLA29XX(ha)) {
+		struct abts_entry_24xx_ext *rsp_ext = rsp_pkt;
+
+		QLA_BUILD_ABTS_BA_ACC(rsp_ext, abts_ext, fctl);
+		rsp_ext->vp_idx_sof = qla_ext_build_vp_sof(
+		    qla_ext_get_vp_index(abts_ext->vp_idx_sof),
+		    qla_ext_get_sof_type(abts_ext->vp_idx_sof));
+		QLA_LOG_ISSUE_ABTS_RSP(vha, rsp_ext, dma, rval);
 	} else {
-		ql_dbg(ql_dbg_init, vha, 0x028ea,
-		    "%s: done.\n", __func__);
+		struct abts_entry_24xx *abts_rsp = rsp_pkt;
+
+		QLA_BUILD_ABTS_BA_ACC(abts_rsp, abts, fctl);
+		abts_rsp->vp_idx = abts->vp_idx;
+		abts_rsp->sof_type = abts->sof_type & 0xf0;
+		QLA_LOG_ISSUE_ABTS_RSP(vha, abts_rsp, dma, rval);
 	}
 
-	dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_els), rsp_els, dma);
+	dma_free_coherent(&ha->pdev->dev, rsp_sz, rsp_pkt, dma);
 }
 
+#undef QLA_LOG_ABTS_RCV
+#undef QLA_BUILD_ABTS_BA_ACC
+#undef QLA_LOG_ISSUE_ABTS_RSP
+
 /**
  * __qla_consume_iocb - this routine is used to tell fw driver has processed
  *   or consumed the head IOCB along with the continuation IOCB's from the
@@ -202,20 +251,30 @@ void __qla_consume_iocb(struct scsi_qla_host *vha,
 	struct rsp_que *rsp_q = *rsp;
 	response_t *new_pkt;
 	uint16_t entry_count_remaining;
+	/*
+	 * entry_count is u8 at offset 1 in both purex_entry_24xx and
+	 * purex_entry_24xx_ext, so the 24xx view is layout-compatible with
+	 * either stride.
+	 */
 	struct purex_entry_24xx *purex = *pkt;
 
 	entry_count_remaining = purex->entry_count;
+
+	/*
+	 * The caller already advanced ring_ptr past the head IOCB, so mark
+	 * the head processed and account for it here, then consume only the
+	 * continuation IOCBs that follow.
+	 */
+	((response_t *)purex)->signature = RESPONSE_PROCESSED;
+	/* flush signature */
+	wmb();
+	--entry_count_remaining;
+
 	while (entry_count_remaining > 0) {
 		new_pkt = rsp_q->ring_ptr;
 		*pkt = new_pkt;
 
-		rsp_q->ring_index++;
-		if (rsp_q->ring_index == rsp_q->length) {
-			rsp_q->ring_index = 0;
-			rsp_q->ring_ptr = rsp_q->ring;
-		} else {
-			rsp_q->ring_ptr++;
-		}
+		qla_rsp_ring_advance(rsp_q);
 
 		new_pkt->signature = RESPONSE_PROCESSED;
 		/* flush signature */
@@ -236,17 +295,30 @@ void __qla_consume_iocb(struct scsi_qla_host *vha,
 int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
 	void **pkt, struct rsp_que **rsp, u8 *buf, u32 buf_len)
 {
+	/*
+	 * purex_entry_24xx_ext overlays purex_entry_24xx for entry_count
+	 * (offset 1), frame_size (offset 12) and els_frame_payload (offset
+	 * 44, base address only -- the array size grows from 20 to 84
+	 * bytes).  Header fields are read through the 24xx view; the
+	 * initial payload memcpy uses a purex_entry_24xx_ext pointer on
+	 * 29xx so that FORTIFY_SOURCE sees the correct 84-byte source.
+	 */
 	struct purex_entry_24xx *purex = *pkt;
+	struct qla_hw_data *ha = vha->hw;
 	struct rsp_que *rsp_q = *rsp;
-	sts_cont_entry_t *new_pkt;
+	size_t payload_size = IS_QLA29XX(ha) ?
+		sizeof_field(struct purex_entry_24xx_ext, els_frame_payload) :
+		sizeof_field(struct purex_entry_24xx, els_frame_payload);
+	u8 *data;
+	u32 data_sz;
 	uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
 	uint16_t buffer_copy_offset = 0;
 	uint16_t entry_count_remaining;
 	u16 tpad;
 
 	entry_count_remaining = purex->entry_count;
-	total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF)
-		- PURX_ELS_HEADER_SIZE;
+	total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF) -
+		PURX_ELS_HEADER_SIZE;
 
 	/*
 	 * end of payload may not end in 4bytes boundary.  Need to
@@ -263,41 +335,47 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
 	}
 
 	pending_bytes = total_bytes = tpad;
-	no_bytes = (pending_bytes > sizeof(purex->els_frame_payload))  ?
-	    sizeof(purex->els_frame_payload) : pending_bytes;
+	no_bytes = (pending_bytes > payload_size) ?
+		payload_size : pending_bytes;
+	if (IS_QLA29XX(ha)) {
+		struct purex_entry_24xx_ext *purex_ext = *pkt;
 
-	memcpy(buf, &purex->els_frame_payload[0], no_bytes);
+		memcpy(buf, &purex_ext->els_frame_payload[0], no_bytes);
+	} else {
+		memcpy(buf, &purex->els_frame_payload[0], no_bytes);
+	}
 	buffer_copy_offset += no_bytes;
 	pending_bytes -= no_bytes;
 	--entry_count_remaining;
 
+	/*
+	 * response_t::signature and struct response_ext::signature are both u32
+	 * at offset 60 (handle:4 + data[52]:60), so the 24xx view writes
+	 * the right slot regardless of stride.
+	 */
 	((response_t *)purex)->signature = RESPONSE_PROCESSED;
 	/* flush signature */
 	wmb();
 
 	do {
 		while ((total_bytes > 0) && (entry_count_remaining > 0)) {
-			new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
-			*pkt = new_pkt;
+			*pkt = rsp_q->ring_ptr;
+			data = ((sts_cont_entry_t *)*pkt)->data;
+			data_sz = qla_sts_cont_data_size(ha);
 
-			if (new_pkt->entry_type != STATUS_CONT_TYPE) {
+			if (((sts_cont_entry_t *)*pkt)->entry_type !=
+			    STATUS_CONT_TYPE) {
 				ql_log(ql_log_warn, vha, 0x507a,
 				    "Unexpected IOCB type, partial data 0x%x\n",
 				    buffer_copy_offset);
 				break;
 			}
 
-			rsp_q->ring_index++;
-			if (rsp_q->ring_index == rsp_q->length) {
-				rsp_q->ring_index = 0;
-				rsp_q->ring_ptr = rsp_q->ring;
-			} else {
-				rsp_q->ring_ptr++;
-			}
-			no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
-			    sizeof(new_pkt->data) : pending_bytes;
+			qla_rsp_ring_advance(rsp_q);
+			no_bytes = (pending_bytes > data_sz) ?
+			    data_sz : pending_bytes;
 			if ((buffer_copy_offset + no_bytes) <= total_bytes) {
-				memcpy((buf + buffer_copy_offset), new_pkt->data,
+				memcpy((buf + buffer_copy_offset), data,
 				    no_bytes);
 				buffer_copy_offset += no_bytes;
 				pending_bytes -= no_bytes;
@@ -306,11 +384,11 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
 				ql_log(ql_log_warn, vha, 0x5044,
 				    "Attempt to copy more that we got, optimizing..%x\n",
 				    buffer_copy_offset);
-				memcpy((buf + buffer_copy_offset), new_pkt->data,
+				memcpy((buf + buffer_copy_offset), data,
 				    total_bytes - buffer_copy_offset);
 			}
 
-			((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
+			((response_t *)*pkt)->signature = RESPONSE_PROCESSED;
 			/* flush signature */
 			wmb();
 		}
@@ -664,7 +742,7 @@ const char *
 qla2x00_get_link_speed_str(struct qla_hw_data *ha, uint16_t speed)
 {
 	static const char *const link_speeds[] = {
-		"1", "2", "?", "4", "8", "16", "32", "64", "10"
+		"1", "2", "?", "4", "8", "16", "32", "64", "128", "10"
 	};
 #define	QLA_LAST_SPEED (ARRAY_SIZE(link_speeds) - 1)
 
@@ -854,15 +932,18 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 			  struct rsp_que **rsp, bool is_purls,
 			  bool byte_order)
 {
+	struct purex_entry_24xx_ext *purex_ext = NULL;
 	struct purex_entry_24xx *purex = NULL;
 	struct pt_ls4_rx_unsol *purls = NULL;
+	struct qla_hw_data *ha = vha->hw;
 	struct rsp_que *rsp_q = *rsp;
-	sts_cont_entry_t *new_pkt;
 	uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
 	uint16_t buffer_copy_offset = 0, payload_size = 0;
 	uint16_t entry_count, entry_count_remaining;
 	struct purex_item *item;
 	void *iocb_pkt = NULL;
+	u8 *data;
+	u32 data_sz;
 
 	if (is_purls) {
 		purls = *pkt;
@@ -870,6 +951,13 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 			      PURX_ELS_HEADER_SIZE;
 		entry_count = entry_count_remaining = purls->entry_count;
 		payload_size = sizeof(purls->payload);
+	} else if (IS_QLA29XX(ha)) {
+		purex_ext = *pkt;
+		total_bytes = (le16_to_cpu(purex_ext->frame_size) & 0x0FFF) -
+			      PURX_ELS_HEADER_SIZE;
+		entry_count = entry_count_remaining =
+		    purex_ext->entry_count;
+		payload_size = sizeof(purex_ext->els_frame_payload);
 	} else {
 		purex = *pkt;
 		total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF) -
@@ -878,8 +966,8 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 		payload_size = sizeof(purex->els_frame_payload);
 	}
 
-	if (total_bytes > sizeof(item->iocb.iocb))
-		total_bytes = sizeof(item->iocb.iocb);
+	if (total_bytes > QLA_MAX_IOCB_SIZE)
+		total_bytes = QLA_MAX_IOCB_SIZE;
 
 	pending_bytes = total_bytes;
 	no_bytes = (pending_bytes > payload_size) ? payload_size :
@@ -896,6 +984,8 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 
 	if (is_purls)
 		memcpy(iocb_pkt, &purls->payload[0], no_bytes);
+	else if (IS_QLA29XX(ha))
+		memcpy(iocb_pkt, &purex_ext->els_frame_payload[0], no_bytes);
 	else
 		memcpy(iocb_pkt, &purex->els_frame_payload[0], no_bytes);
 	buffer_copy_offset += no_bytes;
@@ -904,42 +994,32 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 
 	if (is_purls)
 		((response_t *)purls)->signature = RESPONSE_PROCESSED;
+	else if (IS_QLA29XX(ha))
+		((struct response_ext *)purex_ext)->signature = RESPONSE_PROCESSED;
 	else
 		((response_t *)purex)->signature = RESPONSE_PROCESSED;
 	wmb();
 
 	do {
 		while ((total_bytes > 0) && (entry_count_remaining > 0)) {
-			if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) {
-				ql_dbg(ql_dbg_async, vha, 0x5084,
-				       "Ran out of IOCBs, partial data 0x%x\n",
-				       buffer_copy_offset);
-				cpu_relax();
-				continue;
-			}
-
-			new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
-			*pkt = new_pkt;
+			*pkt = rsp_q->ring_ptr;
+			data = ((sts_cont_entry_t *)*pkt)->data;
+			data_sz = qla_sts_cont_data_size(ha);
 
-			if (new_pkt->entry_type != STATUS_CONT_TYPE) {
+			if (((sts_cont_entry_t *)*pkt)->entry_type !=
+			    STATUS_CONT_TYPE) {
 				ql_log(ql_log_warn, vha, 0x507a,
 				       "Unexpected IOCB type, partial data 0x%x\n",
 				       buffer_copy_offset);
 				break;
 			}
 
-			rsp_q->ring_index++;
-			if (rsp_q->ring_index == rsp_q->length) {
-				rsp_q->ring_index = 0;
-				rsp_q->ring_ptr = rsp_q->ring;
-			} else {
-				rsp_q->ring_ptr++;
-			}
-			no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
-				sizeof(new_pkt->data) : pending_bytes;
+			qla_rsp_ring_advance(rsp_q);
+			no_bytes = (pending_bytes > data_sz) ?
+				   data_sz : pending_bytes;
 			if ((buffer_copy_offset + no_bytes) <= total_bytes) {
-				memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset),
-				       new_pkt->data, no_bytes);
+				memcpy(((uint8_t *)iocb_pkt +
+					buffer_copy_offset), data, no_bytes);
 				buffer_copy_offset += no_bytes;
 				pending_bytes -= no_bytes;
 				--entry_count_remaining;
@@ -947,12 +1027,12 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt,
 				ql_log(ql_log_warn, vha, 0x5044,
 				       "Attempt to copy more that we got, optimizing..%x\n",
 				       buffer_copy_offset);
-				memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset),
-				       new_pkt->data,
-				       total_bytes - buffer_copy_offset);
+				memcpy(((uint8_t *)iocb_pkt +
+					buffer_copy_offset), data,
+					total_bytes - buffer_copy_offset);
 			}
 
-			((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
+			((response_t *)*pkt)->signature = RESPONSE_PROCESSED;
 			wmb();
 		}
 
@@ -1082,9 +1162,9 @@ qla24xx_alloc_purex_item(scsi_qla_host_t *vha, uint16_t size)
 	struct purex_item *item = NULL;
 	uint8_t item_hdr_size = sizeof(*item);
 
-	if (size > QLA_DEFAULT_PAYLOAD_SIZE) {
+	if (size > QLA_MAX_IOCB_SIZE) {
 		item = kzalloc(item_hdr_size +
-		    (size - QLA_DEFAULT_PAYLOAD_SIZE), GFP_ATOMIC);
+		    (size - QLA_MAX_IOCB_SIZE), GFP_ATOMIC);
 	} else {
 		if (atomic_inc_return(&vha->default_item.in_use) == 1) {
 			item = &vha->default_item;
@@ -1133,14 +1213,20 @@ qla24xx_queue_purex_item(scsi_qla_host_t *vha, struct purex_item *pkt,
 static struct purex_item
 *qla24xx_copy_std_pkt(struct scsi_qla_host *vha, void *pkt)
 {
+	struct qla_hw_data *ha = vha->hw;
 	struct purex_item *item;
+	u16 copy_sz;
+
+	if (IS_QLA29XX(ha))
+		copy_sz = sizeof(struct purex_entry_24xx_ext);
+	else
+		copy_sz = QLA_DEFAULT_PAYLOAD_SIZE;
 
-	item = qla24xx_alloc_purex_item(vha,
-					QLA_DEFAULT_PAYLOAD_SIZE);
+	item = qla24xx_alloc_purex_item(vha, copy_sz);
 	if (!item)
 		return item;
 
-	memcpy(&item->iocb, pkt, sizeof(item->iocb));
+	memcpy(&item->iocb, pkt, copy_sz);
 	return item;
 }
 
@@ -1156,8 +1242,13 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 		      struct rsp_que **rsp)
 {
 	struct purex_entry_24xx *purex = *pkt;
+	struct qla_hw_data *ha = vha->hw;
 	struct rsp_que *rsp_q = *rsp;
-	sts_cont_entry_t *new_pkt;
+	size_t payload_size = IS_QLA29XX(ha) ?
+		sizeof_field(struct purex_entry_24xx_ext, els_frame_payload) :
+		sizeof_field(struct purex_entry_24xx, els_frame_payload);
+	u8 *data;
+	u32 data_sz;
 	uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
 	uint16_t buffer_copy_offset = 0;
 	uint16_t entry_count, entry_count_remaining;
@@ -1167,13 +1258,13 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 	total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF)
 	    - PURX_ELS_HEADER_SIZE;
 
-	if (total_bytes > sizeof(item->iocb.iocb))
-		total_bytes = sizeof(item->iocb.iocb);
+	if (total_bytes > QLA_MAX_IOCB_SIZE)
+		total_bytes = QLA_MAX_IOCB_SIZE;
 
 	pending_bytes = total_bytes;
 	entry_count = entry_count_remaining = purex->entry_count;
-	no_bytes = (pending_bytes > sizeof(purex->els_frame_payload))  ?
-		   sizeof(purex->els_frame_payload) : pending_bytes;
+	no_bytes = (pending_bytes > payload_size) ?
+		   payload_size : pending_bytes;
 	ql_log(ql_log_info, vha, 0x509a,
 	       "FPIN ELS, frame_size 0x%x, entry count %d\n",
 	       total_bytes, entry_count);
@@ -1184,7 +1275,13 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 
 	fpin_pkt = &item->iocb;
 
-	memcpy(fpin_pkt, &purex->els_frame_payload[0], no_bytes);
+	if (IS_QLA29XX(ha)) {
+		struct purex_entry_24xx_ext *purex_ext = *pkt;
+
+		memcpy(fpin_pkt, &purex_ext->els_frame_payload[0], no_bytes);
+	} else {
+		memcpy(fpin_pkt, &purex->els_frame_payload[0], no_bytes);
+	}
 	buffer_copy_offset += no_bytes;
 	pending_bytes -= no_bytes;
 	--entry_count_remaining;
@@ -1194,36 +1291,24 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 
 	do {
 		while ((total_bytes > 0) && (entry_count_remaining > 0)) {
-			if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) {
-				ql_dbg(ql_dbg_async, vha, 0x5084,
-				       "Ran out of IOCBs, partial data 0x%x\n",
-				       buffer_copy_offset);
-				cpu_relax();
-				continue;
-			}
+			*pkt = rsp_q->ring_ptr;
+			data = ((sts_cont_entry_t *)*pkt)->data;
+			data_sz = qla_sts_cont_data_size(ha);
 
-			new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
-			*pkt = new_pkt;
-
-			if (new_pkt->entry_type != STATUS_CONT_TYPE) {
+			if (((sts_cont_entry_t *)*pkt)->entry_type !=
+			    STATUS_CONT_TYPE) {
 				ql_log(ql_log_warn, vha, 0x507a,
 				       "Unexpected IOCB type, partial data 0x%x\n",
 				       buffer_copy_offset);
 				break;
 			}
 
-			rsp_q->ring_index++;
-			if (rsp_q->ring_index == rsp_q->length) {
-				rsp_q->ring_index = 0;
-				rsp_q->ring_ptr = rsp_q->ring;
-			} else {
-				rsp_q->ring_ptr++;
-			}
-			no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
-			    sizeof(new_pkt->data) : pending_bytes;
+			qla_rsp_ring_advance(rsp_q);
+			no_bytes = (pending_bytes > data_sz) ?
+			    data_sz : pending_bytes;
 			if ((buffer_copy_offset + no_bytes) <= total_bytes) {
 				memcpy(((uint8_t *)fpin_pkt +
-				    buffer_copy_offset), new_pkt->data,
+				    buffer_copy_offset), data,
 				    no_bytes);
 				buffer_copy_offset += no_bytes;
 				pending_bytes -= no_bytes;
@@ -1233,11 +1318,11 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
 				       "Attempt to copy more that we got, optimizing..%x\n",
 				       buffer_copy_offset);
 				memcpy(((uint8_t *)fpin_pkt +
-				    buffer_copy_offset), new_pkt->data,
+				    buffer_copy_offset), data,
 				    total_bytes - buffer_copy_offset);
 			}
 
-			((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
+			((response_t *)*pkt)->signature = RESPONSE_PROCESSED;
 			wmb();
 		}
 
@@ -2307,8 +2392,25 @@ qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
 
 static void
 qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
-    struct sts_entry_24xx *pkt, int iocb_type)
+		     void *pkt, int iocb_type)
 {
+	/*
+	 * els_sts_entry_24xx_ext (29xx) overlays els_sts_entry_24xx for every
+	 * field touched in this completion handler: comp_status (offset 8),
+	 * total_byte_count (32), error_subcode_1 (36), error_subcode_2 (40),
+	 * d_id[]/s_id[] (24..29), control_flags (30) all sit at byte-identical
+	 * offsets in both layouts (only vp_index/sof_type at offset 14-15 are
+	 * bit-packed differently, and that field is write-only on the issue
+	 * path -- we never read it here). All reads in this function are
+	 * therefore stride-agnostic and go through a single struct
+	 * els_sts_entry_24xx * view; the trailing reserved_4[] of the extended
+	 * layout is irrelevant on completion.
+	 *
+	 * Likewise els_entry_24xx_ext overlays els_entry_24xx through
+	 * control_flags (offset 30), so the SRB_ELS_CMD_HST_NOLOGIN ctl_flags
+	 * read below also goes through the 24xx view.
+	 */
+	struct sts_entry_24xx *sts24 = pkt;
 	struct els_sts_entry_24xx *ese = (struct els_sts_entry_24xx *)pkt;
 	const char func[] = "ELS_CT_IOCB";
 	const char *type;
@@ -2331,9 +2433,9 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 
 	type = NULL;
 
-	comp_status = fw_status[0] = le16_to_cpu(pkt->comp_status);
-	fw_status[1] = le32_to_cpu(((struct els_sts_entry_24xx *)pkt)->error_subcode_1);
-	fw_status[2] = le32_to_cpu(((struct els_sts_entry_24xx *)pkt)->error_subcode_2);
+	comp_status = fw_status[0] = le16_to_cpu(sts24->comp_status);
+	fw_status[1] = le32_to_cpu(ese->error_subcode_1);
+	fw_status[2] = le32_to_cpu(ese->error_subcode_2);
 
 	switch (sp->type) {
 	case SRB_ELS_CMD_RPT:
@@ -2343,17 +2445,18 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 	case SRB_ELS_CMD_HST_NOLOGIN:
 		type = "els";
 		{
-			struct els_entry_24xx *els = (void *)pkt;
+			__le16 ctl_flags =
+				((struct els_entry_24xx *)pkt)->control_flags;
 			struct qla_bsg_auth_els_request *p =
 				(struct qla_bsg_auth_els_request *)bsg_job->request;
 
 			ql_dbg(ql_dbg_user, vha, 0x700f,
-			     "%s %s. portid=%02x%02x%02x status %x xchg %x bsg ptr %p\n",
+			     "%s %s complete portid=%02x%02x%02x status %x xchg %x bsg ptr %p\n",
 			     __func__, sc_to_str(p->e.sub_cmd),
 			     e->d_id[2], e->d_id[1], e->d_id[0],
 			     comp_status, p->e.extra_rx_xchg_address, bsg_job);
 
-			if (!(le16_to_cpu(els->control_flags) & ECF_PAYLOAD_DESCR_MASK)) {
+			if (!(le16_to_cpu(ctl_flags) & ECF_PAYLOAD_DESCR_MASK)) {
 				if (sp->remap.remapped) {
 					n = sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
 						bsg_job->reply_payload.sg_cnt,
@@ -2409,14 +2512,15 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 		} else {
 			if (comp_status == CS_DATA_UNDERRUN) {
 				res =  DID_OK << 16;
-				els->u.els_plogi.len = cpu_to_le16(le32_to_cpu(
-					ese->total_byte_count));
+				els->u.els_plogi.len = cpu_to_le16(
+					le32_to_cpu(ese->total_byte_count));
 
 				if (sp->remap.remapped &&
 				    ((u8 *)sp->remap.rsp.buf)[0] == ELS_LS_ACC) {
 					ql_dbg(ql_dbg_user, vha, 0x503f,
 					    "%s IOCB Done LS_ACC %02x%02x%02x -> %02x%02x%02x",
-					    __func__, e->s_id[0], e->s_id[2], e->s_id[1],
+					    __func__,
+					    e->s_id[0], e->s_id[2], e->s_id[1],
 					    e->d_id[2], e->d_id[1], e->d_id[0]);
 					logit = 0;
 				}
@@ -2444,8 +2548,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 					ql_dbg(ql_dbg_user, vha, 0x503f,
 					    "subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n",
 					    fw_status[1], fw_status[2],
-					    le32_to_cpu(((struct els_sts_entry_24xx *)
-						pkt)->total_byte_count),
+					    le32_to_cpu(ese->total_byte_count),
 					    e->s_id[0], e->s_id[2], e->s_id[1],
 					    e->d_id[2], e->d_id[1], e->d_id[0]);
 				}
@@ -2462,8 +2565,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 				ql_log(ql_log_info, vha, 0x503f,
 				    "subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n",
 				    fw_status[1], fw_status[2],
-				    le32_to_cpu(((struct els_sts_entry_24xx *)
-				    pkt)->total_byte_count),
+				    le32_to_cpu(ese->total_byte_count),
 				    e->s_id[0], e->s_id[2], e->s_id[1],
 				    e->d_id[2], e->d_id[1], e->d_id[0]);
 			}
@@ -2516,8 +2618,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
 }
 
 static void
-qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
-    struct logio_entry_24xx *logio)
+qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, void *pkt)
 {
 	const char func[] = "LOGIO-IOCB";
 	const char *type;
@@ -2527,8 +2628,20 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
 	uint16_t *data;
 	uint32_t iop[2];
 	int logit = 1;
+	struct qla_hw_data *ha = vha->hw;
+	/*
+	 * logio_entry_24xx_ext overlays logio_entry_24xx through
+	 * io_parameter[10]: comp_status, io_parameter[0..10] and
+	 * entry_status are at identical offsets and types in both layouts
+	 * (only vp_index width differs, and that field is write-only on
+	 * the issue path).  So all reads in this completion handler are
+	 * stride-agnostic and we read through a single struct
+	 * logio_entry_24xx * view; the trailing reserved_2[64] of the
+	 * extended layout is irrelevant here.
+	 */
+	struct logio_entry_24xx *logio = pkt;
 
-	sp = qla2x00_get_sp_from_handle(vha, func, req, logio);
+	sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
 	if (!sp)
 		return;
 
@@ -2548,7 +2661,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
 		    fcport->d_id.b.area, fcport->d_id.b.al_pa,
 		    logio->entry_status);
 		ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x504d,
-		    logio, sizeof(*logio));
+		    pkt, qla_rsp_entry_size(ha));
 
 		goto logio_done;
 	}
@@ -2559,7 +2672,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
 		    type, sp->handle, fcport->d_id.b24, fcport->port_name,
 		    le32_to_cpu(logio->io_parameter[0]));
 
-		vha->hw->exch_starvation = 0;
+		ha->exch_starvation = 0;
 		data[0] = MBS_COMMAND_COMPLETE;
 
 		if (sp->type == SRB_PRLI_CMD) {
@@ -2598,6 +2711,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
 
 	iop[0] = le32_to_cpu(logio->io_parameter[0]);
 	iop[1] = le32_to_cpu(logio->io_parameter[1]);
+
 	lio->u.logio.iop[0] = iop[0];
 	lio->u.logio.iop[1] = iop[1];
 	switch (iop[0]) {
@@ -2622,14 +2736,14 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
 		data[0] = MBS_COMMAND_ERROR;
 		break;
 	case LSC_SCODE_NOXCB:
-		vha->hw->exch_starvation++;
-		if (vha->hw->exch_starvation > 5) {
+		ha->exch_starvation++;
+		if (ha->exch_starvation > 5) {
 			ql_log(ql_log_warn, vha, 0xd046,
 			    "Exchange starvation. Resetting RISC\n");
 
-			vha->hw->exch_starvation = 0;
+			ha->exch_starvation = 0;
 
-			if (IS_P3P_TYPE(vha->hw))
+			if (IS_P3P_TYPE(ha))
 				set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
 			else
 				set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
@@ -2645,16 +2759,12 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
 		ql_log(ql_log_warn, sp->vha, 0x5037, "Async-%s failed: "
 		       "handle=%x pid=%06x wwpn=%8phC comp_status=%x iop0=%x iop1=%x\n",
 		       type, sp->handle, fcport->d_id.b24, fcport->port_name,
-		       le16_to_cpu(logio->comp_status),
-		       le32_to_cpu(logio->io_parameter[0]),
-		       le32_to_cpu(logio->io_parameter[1]));
+		       le16_to_cpu(logio->comp_status), iop[0], iop[1]);
 	else
 		ql_dbg(ql_dbg_disc, sp->vha, 0x5037, "Async-%s failed: "
 		       "handle=%x pid=%06x wwpn=%8phC comp_status=%x iop0=%x iop1=%x\n",
 		       type, sp->handle, fcport->d_id.b24, fcport->port_name,
-		       le16_to_cpu(logio->comp_status),
-		       le32_to_cpu(logio->io_parameter[0]),
-		       le32_to_cpu(logio->io_parameter[1]));
+		       le16_to_cpu(logio->comp_status), iop[0], iop[1]);
 
 logio_done:
 	sp->done(sp, 0);
@@ -2669,7 +2779,9 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
 	srb_t *sp;
 	struct srb_iocb *iocb;
 	struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk;
+	struct qla_hw_data *ha = vha->hw;
 	u16 comp_status;
+	struct qla_sts_fwi2 sf;
 
 	sp = qla2x00_get_sp_from_handle(vha, func, req, tsk);
 	if (!sp)
@@ -2691,18 +2803,19 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
 		    "Async-%s error - hdl=%x completion status(%x).\n",
 		    type, sp->handle, comp_status);
 		iocb->u.tmf.data = QLA_FUNCTION_FAILED;
-	} else if ((le16_to_cpu(sts->scsi_status) &
-	    SS_RESPONSE_INFO_LEN_VALID)) {
-		host_to_fcp_swap(sts->data, sizeof(sts->data));
-		if (le32_to_cpu(sts->rsp_data_len) < 4) {
-			ql_log(ql_log_warn, fcport->vha, 0x503b,
-			    "Async-%s error - hdl=%x not enough response(%d).\n",
-			    type, sp->handle, sts->rsp_data_len);
-		} else if (sts->data[3]) {
-			ql_log(ql_log_warn, fcport->vha, 0x503c,
-			    "Async-%s error - hdl=%x response(%x).\n",
-			    type, sp->handle, sts->data[3]);
-			iocb->u.tmf.data = QLA_FUNCTION_FAILED;
+	} else {
+		qla_sts_fwi2_extract(ha, tsk, &sf);
+		if (sf.scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
+			if (sf.rsp_data_len < 4) {
+				ql_log(ql_log_warn, fcport->vha, 0x503b,
+				    "Async-%s error - hdl=%x not enough response(%d).\n",
+				    type, sp->handle, sf.rsp_data_len);
+			} else if (sf.data[3]) {
+				ql_log(ql_log_warn, fcport->vha, 0x503c,
+				    "Async-%s error - hdl=%x response(%x).\n",
+				    type, sp->handle, sf.data[3]);
+				iocb->u.tmf.data = QLA_FUNCTION_FAILED;
+			}
 		}
 	}
 
@@ -2731,7 +2844,8 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
 
 	if (iocb->u.tmf.data != QLA_SUCCESS)
 		ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, sp->vha, 0x5055,
-		    sts, sizeof(*sts));
+		    tsk, IS_QLA29XX(ha) ?
+		    sizeof(struct sts_entry_24xx_ext) : sizeof(*sts));
 
 	sp->done(sp, 0);
 }
@@ -2742,6 +2856,8 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 	fc_port_t *fcport;
 	struct srb_iocb *iocb;
 	struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk;
+	struct sts_entry_24xx_ext *stsext = (struct sts_entry_24xx_ext *)tsk;
+	struct qla_hw_data *ha = vha->hw;
 	uint16_t        state_flags;
 	struct nvmefc_fcp_req *fd;
 	uint16_t        ret = QLA_SUCCESS;
@@ -2793,7 +2909,10 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 		uint32_t *inbuf, *outbuf;
 		uint16_t iter;
 
-		inbuf = (uint32_t *)&sts->nvme_ersp_data;
+		if (IS_QLA29XX(ha))
+			inbuf = (uint32_t *)stsext->u2.nvme_ersp_data;
+		else
+			inbuf = (uint32_t *)&sts->nvme_ersp_data;
 		outbuf = (uint32_t *)fd->rspaddr;
 		iocb->u.nvme.rsp_pyld_len = sts->nvme_rsp_pyld_len;
 		if (unlikely(le16_to_cpu(iocb->u.nvme.rsp_pyld_len) >
@@ -2877,13 +2996,19 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 }
 
 static void qla_ctrlvp_completed(scsi_qla_host_t *vha, struct req_que *req,
-    struct vp_ctrl_entry_24xx *vce)
+				 void *pkt)
 {
 	const char func[] = "CTRLVP-IOCB";
+	/*
+	 * vp_ctrl_entry_24xx_ext overlays vp_ctrl_entry_24xx for all
+	 * fields read here (entry_status, comp_status, vp_idx_failed),
+	 * so the read goes through one struct vp_ctrl_entry_24xx * view.
+	 */
+	struct vp_ctrl_entry_24xx *vce = pkt;
 	srb_t *sp;
 	int rval = QLA_SUCCESS;
 
-	sp = qla2x00_get_sp_from_handle(vha, func, req, vce);
+	sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
 	if (!sp)
 		return;
 
@@ -2936,7 +3061,8 @@ static void qla2x00_process_response_entry(struct scsi_qla_host *vha,
 						sts22_entry->handle[cnt]);
 		break;
 	case STATUS_CONT_TYPE:
-		qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt);
+		qla2x00_status_cont_entry(rsp, pkt,
+		    qla_sts_cont_data_size(rsp->hw));
 		break;
 	case MBX_IOCB_TYPE:
 		qla2x00_mbx_iocb_entry(vha, rsp->req, (struct mbx_entry *)pkt);
@@ -2973,13 +3099,7 @@ qla2x00_process_response_queue(struct rsp_que *rsp)
 	while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
 		pkt = (sts_entry_t *)rsp->ring_ptr;
 
-		rsp->ring_index++;
-		if (rsp->ring_index == rsp->length) {
-			rsp->ring_index = 0;
-			rsp->ring_ptr = rsp->ring;
-		} else {
-			rsp->ring_ptr++;
-		}
+		qla_rsp_ring_advance(rsp);
 
 		if (pkt->entry_status != 0) {
 			qla2x00_error_entry(vha, rsp, pkt);
@@ -3043,16 +3163,26 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len,
  * to indicate to the kernel that the HBA detected error.
  */
 static inline int
-qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24)
+qla2x00_handle_dif_error(srb_t *sp, void *pkt)
 {
 	struct scsi_qla_host *vha = sp->vha;
+	struct qla_hw_data *ha = vha->hw;
 	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
-	uint8_t		*ap = &sts24->data[12];
-	uint8_t		*ep = &sts24->data[20];
+	struct sts_entry_24xx *sts24 = pkt;
+	struct sts_entry_24xx_ext *stsext = pkt;
+	uint8_t		*ap, *ep;
 	uint32_t	e_ref_tag, a_ref_tag;
 	uint16_t	e_app_tag, a_app_tag;
 	uint16_t	e_guard, a_guard;
 
+	if (IS_QLA29XX(ha)) {
+		ap = stsext->act_dif;
+		ep = stsext->exp_dif;
+	} else {
+		ap = &sts24->data[12];
+		ep = &sts24->data[20];
+	}
+
 	/*
 	 * swab32 of the "data" field in the beginning of qla2x00_status_entry()
 	 * would make guard field appear at offset 2
@@ -3065,7 +3195,7 @@ qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24)
 	e_ref_tag = get_unaligned_le32(ep + 4);
 
 	ql_dbg(ql_dbg_io, vha, 0x3023,
-	    "iocb(s) %p Returned STATUS.\n", sts24);
+	    "iocb(s) %px Returned STATUS.\n", pkt);
 
 	ql_dbg(ql_dbg_io, vha, 0x3024,
 	    "DIF ERROR in cmd 0x%x lba 0x%llx act ref"
@@ -3197,7 +3327,9 @@ qla25xx_process_bidir_status_iocb(scsi_qla_host_t *vha, void *pkt,
 
 	if (IS_FWI2_CAPABLE(ha)) {
 		comp_status = le16_to_cpu(sts24->comp_status);
-		scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK;
+		scsi_status = IS_QLA29XX(ha) ?
+		    le16_to_cpu(((struct sts_entry_24xx_ext *)pkt)->u2.scsi_status) & SS_MASK :
+		    le16_to_cpu(sts24->scsi_status) & SS_MASK;
 	} else {
 		comp_status = le16_to_cpu(sts->comp_status);
 		scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK;
@@ -3333,10 +3465,13 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 	int res = 0;
 	uint16_t state_flags = 0;
 	uint16_t sts_qual = 0;
+	struct qla_sts_fwi2 sf;
 
 	if (IS_FWI2_CAPABLE(ha)) {
 		comp_status = le16_to_cpu(sts24->comp_status);
-		scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK;
+		scsi_status = IS_QLA29XX(ha) ?
+		    le16_to_cpu(((struct sts_entry_24xx_ext *)pkt)->u2.scsi_status) & SS_MASK :
+		    le16_to_cpu(sts24->scsi_status) & SS_MASK;
 		state_flags = le16_to_cpu(sts24->state_flags);
 	} else {
 		comp_status = le16_to_cpu(sts->comp_status);
@@ -3406,8 +3541,16 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 		return;
 	}
 
+	/* Everything below is the SCSI fast path; reject other SRB types. */
+	if (sp->type != SRB_SCSI_CMD) {
+		ql_dbg(ql_dbg_io, vha, 0x303d,
+		    "Unexpected SRB type %x for status IOCB, sp %p.\n",
+		    sp->type, sp);
+		return;
+	}
+
 	/* Fast path completion. */
-	qla_chk_edif_rx_sa_delete_pending(vha, sp, sts24);
+	qla_chk_edif_rx_sa_delete_pending(vha, sp, pkt);
 	sp->qpair->cmd_completion_cnt++;
 
 	if (comp_status == CS_COMPLETE && scsi_status == 0) {
@@ -3434,20 +3577,20 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 	sense_len = par_sense_len = rsp_info_len = resid_len =
 	    fw_resid_len = 0;
 	if (IS_FWI2_CAPABLE(ha)) {
+		qla_sts_fwi2_extract(ha, pkt, &sf);
 		if (scsi_status & SS_SENSE_LEN_VALID)
-			sense_len = le32_to_cpu(sts24->sense_len);
+			sense_len = sf.sense_len;
 		if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
-			rsp_info_len = le32_to_cpu(sts24->rsp_data_len);
+			rsp_info_len = sf.rsp_data_len;
 		if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER))
-			resid_len = le32_to_cpu(sts24->rsp_residual_count);
+			resid_len = sf.rsp_residual_count;
 		if (comp_status == CS_DATA_UNDERRUN)
 			fw_resid_len = le32_to_cpu(sts24->residual_len);
-		rsp_info = sts24->data;
-		sense_data = sts24->data;
-		host_to_fcp_swap(sts24->data, sizeof(sts24->data));
+		rsp_info = sf.data;
+		sense_data = sf.data;
+		par_sense_len = sf.data_sz;
+		sts_qual = sf.sts_qual;
 		ox_id = le16_to_cpu(sts24->ox_id);
-		par_sense_len = sizeof(sts24->data);
-		sts_qual = le16_to_cpu(sts24->status_qualifier);
 	} else {
 		if (scsi_status & SS_SENSE_LEN_VALID)
 			sense_len = le16_to_cpu(sts->req_sense_length);
@@ -3463,6 +3606,18 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 	if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
 		/* Sense data lies beyond any FCP RESPONSE data. */
 		if (IS_FWI2_CAPABLE(ha)) {
+			/*
+			 * A hostile or buggy target may report an
+			 * rsp_info_len larger than the IOCB data area.
+			 * Clamp it so the par_sense_len subtraction cannot
+			 * underflow and walk sense_data out of bounds.
+			 */
+			if (rsp_info_len > par_sense_len) {
+				ql_log(ql_log_warn, fcport->vha, 0x3107,
+				       "Truncating bogus rsp_info_len 0x%x to 0x%x.\n",
+				       rsp_info_len, par_sense_len);
+				rsp_info_len = par_sense_len;
+			}
 			sense_data += rsp_info_len;
 			par_sense_len -= rsp_info_len;
 		}
@@ -3647,7 +3802,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 		break;
 
 	case CS_DIF_ERROR:
-		logit = qla2x00_handle_dif_error(sp, sts24);
+		logit = qla2x00_handle_dif_error(sp, pkt);
 		res = cp->result;
 		break;
 
@@ -3672,7 +3827,8 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 		    ox_id, cp->cmnd, scsi_bufflen(cp), rsp_info_len,
 		    resid_len, fw_resid_len, sp, cp);
 		ql_dump_buffer(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe0ee,
-		    pkt, sizeof(*sts24));
+		    pkt, IS_QLA29XX(ha) ?
+		    sizeof(struct sts_entry_24xx_ext) : sizeof(*sts24));
 		res = DID_ERROR << 16;
 		vha->hw_err_cnt++;
 		break;
@@ -3702,11 +3858,12 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
  * qla2x00_status_cont_entry() - Process a Status Continuations entry.
  * @rsp: response queue
  * @pkt: Entry pointer
+ * @data_sz: maximum sense-data payload size for this entry
  *
  * Extended sense data.
  */
 static void
-qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
+qla2x00_status_cont_entry(struct rsp_que *rsp, void *pkt, u32 data_sz)
 {
 	uint8_t	sense_sz = 0;
 	struct qla_hw_data *ha = rsp->hw;
@@ -3715,6 +3872,7 @@ qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
 	struct scsi_cmnd *cp;
 	uint32_t sense_len;
 	uint8_t *sense_ptr;
+	u8 *data = ((sts_cont_entry_t *)pkt)->data;
 
 	if (!sp || !GET_CMD_SENSE_LEN(sp))
 		return;
@@ -3731,15 +3889,15 @@ qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
 		return;
 	}
 
-	if (sense_len > sizeof(pkt->data))
-		sense_sz = sizeof(pkt->data);
+	if (sense_len > data_sz)
+		sense_sz = data_sz;
 	else
 		sense_sz = sense_len;
 
 	/* Move sense data. */
 	if (IS_FWI2_CAPABLE(ha))
-		host_to_fcp_swap(pkt->data, sizeof(pkt->data));
-	memcpy(sense_ptr, pkt->data, sense_sz);
+		host_to_fcp_swap(data, data_sz);
+	memcpy(sense_ptr, data, sense_sz);
 	ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302c,
 		sense_ptr, sense_sz);
 
@@ -3778,10 +3936,12 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
 	    "iocb type %xh with error status %xh, handle %xh, rspq id %d\n",
 	    pkt->entry_type, pkt->entry_status, pkt->handle, rsp->id);
 
-	if (que >= ha->max_req_queues || !ha->req_q_map[que])
+	if (que >= ha->max_req_queues)
 		goto fatal;
 
 	req = ha->req_q_map[que];
+	if (!req)
+		goto fatal;
 
 	if (pkt->entry_status & RF_BUSY)
 		res = DID_BUS_BUSY << 16;
@@ -3864,7 +4024,7 @@ qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
 
 static void
 qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
-	struct abort_entry_24xx *pkt)
+	void *pkt)
 {
 	const char func[] = "ABT_IOCB";
 	srb_t *sp;
@@ -3876,7 +4036,14 @@ qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 		return;
 
 	abt = &sp->u.iocb_cmd;
-	abt->u.abt.comp_status = pkt->comp_status;
+	/*
+	 * abort_entry_24xx_ext overlays abort_entry_24xx through the
+	 * nport_handle/comp_status union at offset 8, so reading
+	 * comp_status is stride-agnostic and goes through the 24xx view.
+	 */
+	abt->u.abt.comp_status =
+	    ((struct abort_entry_24xx *)pkt)->comp_status;
+
 	orig_sp = sp->cmd_sp;
 	/* Need to pass original sp */
 	if (orig_sp)
@@ -3886,7 +4053,7 @@ qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 }
 
 void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha,
-    struct pt_ls4_request *pkt, struct req_que *req)
+			   void *pkt, struct req_que *req)
 {
 	srb_t *sp;
 	const char func[] = "LS4_IOCB";
@@ -3896,7 +4063,9 @@ void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha,
 	if (!sp)
 		return;
 
-	comp_status = le16_to_cpu(pkt->status);
+	/* status lives at the same offset (8) in both IOCB strides */
+	comp_status = le16_to_cpu(((struct pt_ls4_request *)pkt)->status);
+
 	sp->done(sp, comp_status);
 }
 
@@ -3941,9 +4110,10 @@ static int qla_chk_cont_iocb_avail(struct scsi_qla_host *vha,
 }
 
 static void qla_marker_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
-	struct mrk_entry_24xx *pkt)
+	void *pkt)
 {
 	const char func[] = "MRK-IOCB";
+	struct mrk_entry_24xx *mrk = pkt;
 	srb_t *sp;
 	int res = QLA_SUCCESS;
 
@@ -3954,7 +4124,7 @@ static void qla_marker_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 	if (!sp)
 		return;
 
-	if (pkt->entry_status) {
+	if (mrk->entry_status) {
 		ql_dbg(ql_dbg_taskm, vha, 0x8025, "marker failure.\n");
 		res = QLA_COMMAND_ERROR;
 	}
@@ -3970,8 +4140,9 @@ static void qla_marker_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
 void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 	struct rsp_que *rsp)
 {
-	struct sts_entry_24xx *pkt;
+	void *pkt;
 	struct qla_hw_data *ha = vha->hw;
+	struct purex_entry_24xx_ext *purex_entry_ext;
 	struct purex_entry_24xx *purex_entry;
 	struct purex_item *pure_item;
 	struct pt_ls4_rx_unsol *p;
@@ -4000,19 +4171,13 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 
 	while (rsp->ring_index != rsp_in &&
 		       rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
-		pkt = (struct sts_entry_24xx *)rsp->ring_ptr;
+		pkt = (void *)rsp->ring_ptr;
 		cur_ring_index = rsp->ring_index;
 
-		rsp->ring_index++;
-		if (rsp->ring_index == rsp->length) {
-			rsp->ring_index = 0;
-			rsp->ring_ptr = rsp->ring;
-		} else {
-			rsp->ring_ptr++;
-		}
+		qla_rsp_ring_advance(rsp);
 
-		if (pkt->entry_status != 0) {
-			if (qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt))
+		if (((response_t *)pkt)->entry_status != 0) {
+			if (qla2x00_error_entry(vha, rsp, (sts_entry_t *)pkt))
 				goto process_err;
 
 			((response_t *)pkt)->signature = RESPONSE_PROCESSED;
@@ -4021,20 +4186,19 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 		}
 process_err:
 
-		switch (pkt->entry_type) {
+		switch (((response_t *)pkt)->entry_type) {
 		case STATUS_TYPE:
 			qla2x00_status_entry(vha, rsp, pkt);
 			break;
 		case STATUS_CONT_TYPE:
-			qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt);
+			qla2x00_status_cont_entry(rsp, pkt,
+			    qla_sts_cont_data_size(rsp->hw));
 			break;
 		case VP_RPT_ID_IOCB_TYPE:
-			qla24xx_report_id_acquisition(vha,
-			    (struct vp_rpt_id_entry_24xx *)pkt);
+			qla24xx_report_id_acquisition(vha, pkt);
 			break;
 		case LOGINOUT_PORT_IOCB_TYPE:
-			qla24xx_logio_entry(vha, rsp->req,
-			    (struct logio_entry_24xx *)pkt);
+			qla24xx_logio_entry(vha, rsp->req, pkt);
 			break;
 		case CT_IOCB_TYPE:
 			qla24xx_els_ct_entry(vha, rsp->req, pkt, CT_IOCB_TYPE);
@@ -4067,11 +4231,10 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 			qlt_response_pkt_all_vps(vha, rsp, (response_t *)pkt);
 			break;
 		case PT_LS4_REQUEST:
-			qla24xx_nvme_ls4_iocb(vha, (struct pt_ls4_request *)pkt,
-			    rsp->req);
+			qla24xx_nvme_ls4_iocb(vha, pkt, rsp->req);
 			break;
 		case NOTIFY_ACK_TYPE:
-			if (pkt->handle == QLA_TGT_SKIP_HANDLE)
+			if (((response_t *)pkt)->handle == QLA_TGT_SKIP_HANDLE)
 				qlt_response_pkt_all_vps(vha, rsp,
 				    (response_t *)pkt);
 			else
@@ -4079,23 +4242,29 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 					(struct nack_to_isp *)pkt);
 			break;
 		case MARKER_TYPE:
-			qla_marker_iocb_entry(vha, rsp->req, (struct mrk_entry_24xx *)pkt);
+			qla_marker_iocb_entry(vha, rsp->req, pkt);
 			break;
 		case ABORT_IOCB_TYPE:
-			qla24xx_abort_iocb_entry(vha, rsp->req,
-			    (struct abort_entry_24xx *)pkt);
+			qla24xx_abort_iocb_entry(vha, rsp->req, pkt);
 			break;
 		case MBX_IOCB_TYPE:
 			qla24xx_mbx_iocb_entry(vha, rsp->req,
 			    (struct mbx_24xx_entry *)pkt);
 			break;
 		case VP_CTRL_IOCB_TYPE:
-			qla_ctrlvp_completed(vha, rsp->req,
-			    (struct vp_ctrl_entry_24xx *)pkt);
+			qla_ctrlvp_completed(vha, rsp->req, pkt);
 			break;
 		case PUREX_IOCB_TYPE:
-			purex_entry = (void *)pkt;
-			switch (purex_entry->els_frame_payload[3]) {
+			if (IS_QLA29XX(ha)) {
+				purex_entry_ext = (void *)pkt;
+				purex_entry = NULL;
+			} else {
+				purex_entry = (void *)pkt;
+				purex_entry_ext = NULL;
+			}
+			switch (IS_QLA29XX(ha) ?
+			    purex_entry_ext->els_frame_payload[3] :
+			    purex_entry->els_frame_payload[3]) {
 			case ELS_RDP:
 				pure_item = qla24xx_copy_std_pkt(vha, pkt);
 				if (!pure_item)
@@ -4109,9 +4278,24 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 					       "SCM not active for this port\n");
 					break;
 				}
+				if (qla_chk_cont_iocb_avail(vha, rsp,
+				    (response_t *)pkt, rsp_in)) {
+					/*
+					 * ring_ptr and ring_index were
+					 * pre-incremented above. Reset them
+					 * back to current. Wait for next
+					 * interrupt with all IOCBs to arrive
+					 * and re-process.
+					 */
+					qla_rsp_ring_rewind_to(rsp,
+					    (response_t *)pkt, cur_ring_index);
+
+					ql_dbg(ql_dbg_init, vha, 0x5095,
+					    "Defer processing FPIN...\n");
+					return;
+				}
 				pure_item = qla27xx_copy_fpin_pkt(vha,
 							  (void **)&pkt, &rsp);
-				__update_rsp_in(is_shadow_hba, rsp, rsp_in);
 				if (!pure_item)
 					break;
 				qla24xx_queue_purex_item(vha, pure_item,
@@ -4127,11 +4311,13 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 					 * interrupt with all IOCBs to arrive
 					 * and re-process.
 					 */
-					rsp->ring_ptr = (response_t *)pkt;
-					rsp->ring_index = cur_ring_index;
+					qla_rsp_ring_rewind_to(rsp,
+					    (response_t *)pkt, cur_ring_index);
 
 					ql_dbg(ql_dbg_init, vha, 0x5091,
 					    "Defer processing ELS opcode %#x...\n",
+					    IS_QLA29XX(ha) ?
+					    purex_entry_ext->els_frame_payload[3] :
 					    purex_entry->els_frame_payload[3]);
 					return;
 				}
@@ -4139,7 +4325,9 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 				break;
 			default:
 				ql_log(ql_log_warn, vha, 0x509c,
-				       "Discarding ELS Request opcode 0x%x\n",
+				       "Discarding ELS Request opcode 0x%x...\n",
+				       IS_QLA29XX(ha) ?
+				       purex_entry_ext->els_frame_payload[3] :
 				       purex_entry->els_frame_payload[3]);
 			}
 			break;
@@ -4150,8 +4338,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 		case PT_LS4_UNSOL:
 			p = (void *)pkt;
 			if (qla_chk_cont_iocb_avail(vha, rsp, (response_t *)pkt, rsp_in)) {
-				rsp->ring_ptr = (response_t *)pkt;
-				rsp->ring_index = cur_ring_index;
+				qla_rsp_ring_rewind_to(rsp, (response_t *)pkt,
+						       cur_ring_index);
 
 				ql_dbg(ql_dbg_init, vha, 0x2124,
 				       "Defer processing UNSOL LS req opcode %#x...\n",
@@ -4164,7 +4352,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 			/* Type Not Supported. */
 			ql_dbg(ql_dbg_async, vha, 0x5042,
 			       "Received unknown response pkt type 0x%x entry status=%x.\n",
-			       pkt->entry_type, pkt->entry_status);
+			       ((response_t *)pkt)->entry_type,
+			       ((response_t *)pkt)->entry_status);
 			break;
 		}
 		((response_t *)pkt)->signature = RESPONSE_PROCESSED;
@@ -4548,10 +4737,10 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp)
 		ha->msix_count = ret;
 		/* Recalculate queue values */
 		if (ha->mqiobase && (ql2xmqsupport || ql2xnvmeenable)) {
-			ha->max_req_queues = ha->msix_count - 1;
+			ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
 
 			/* ATIOQ needs 1 vector. That's 1 less QPair */
-			if (QLA_TGT_MODE_ENABLED())
+			if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1)
 				ha->max_req_queues--;
 
 			ha->max_rsp_queues = ha->max_req_queues;
@@ -4663,7 +4852,8 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp)
 	/* If possible, enable MSI-X. */
 	if (ql2xenablemsix == 0 || (!IS_QLA2432(ha) && !IS_QLA2532(ha) &&
 	    !IS_QLA8432(ha) && !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha) &&
-	    !IS_QLAFX00(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha)))
+	    !IS_QLAFX00(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+	    !IS_QLA29XX(ha)))
 		goto skip_msi;
 
 	if (ql2xenablemsix == 2)
@@ -4702,7 +4892,7 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp)
 
 	if (!IS_QLA24XX(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) &&
 	    !IS_QLA8001(ha) && !IS_P3P_TYPE(ha) && !IS_QLAFX00(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		goto skip_msi;
 
 	ret = pci_alloc_irq_vectors(ha->pdev, 1, 1, PCI_IRQ_MSI);
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 44e310f1a370..59ec5605930b 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -618,7 +618,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
 
 int
 qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr,
-    uint32_t risc_code_size)
+		 uint32_t risc_code_size, int opt)
 {
 	int rval;
 	struct qla_hw_data *ha = vha->hw;
@@ -651,7 +651,14 @@ qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr,
 		mcp->out_mb |= MBX_4;
 	}
 
-	mcp->in_mb = MBX_1|MBX_0;
+	if (IS_QLA29XX(ha)) {
+		mcp->mb[9] = opt;
+		mcp->out_mb |= MBX_9;
+		mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+	} else {
+		mcp->in_mb = MBX_1|MBX_0;
+	}
+
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
@@ -692,6 +699,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
 {
 	int rval;
 	struct qla_hw_data *ha = vha->hw;
+	struct nvram_81xx *nv = ha->nvram;
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 	u8 semaphore = 0;
@@ -720,14 +728,13 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
 				    ha->lr_distance << LR_DIST_FW_POS;
 		}
 
-		if (ql2xnvmeenable && (IS_QLA27XX(ha) || IS_QLA28XX(ha)))
+		if (ql2xnvmeenable && (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)))
 			mcp->mb[4] |= NVME_ENABLE_FLAG;
 
-		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
-			struct nvram_81xx *nv = ha->nvram;
+		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 			/* set minimum speed if specified in nvram */
 			if (nv->min_supported_speed >= 2 &&
-			    nv->min_supported_speed <= 5) {
+			    nv->min_supported_speed <= 7) {
 				mcp->mb[4] |= BIT_4;
 				mcp->mb[11] |= nv->min_supported_speed & 0xF;
 				mcp->out_mb |= MBX_11;
@@ -765,7 +772,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
 	rval = qla2x00_mailbox_command(vha, mcp);
 
 	if (rval != QLA_SUCCESS) {
-		if (IS_QLA28XX(ha) && rval == QLA_COMMAND_ERROR &&
+		if ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && rval == QLA_COMMAND_ERROR &&
 		    mcp->mb[1] == 0x27 && retry) {
 			semaphore = 1;
 			retry--;
@@ -793,17 +800,20 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
 	ql_dbg(ql_dbg_mbx, vha, 0x119a,
 	    "fw_ability_mask=%x.\n", ha->fw_ability_mask);
 	ql_dbg(ql_dbg_mbx, vha, 0x1027, "exchanges=%x.\n", mcp->mb[1]);
-	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
-		ha->max_supported_speed = mcp->mb[2] & (BIT_0|BIT_1);
+
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
+		ha->max_supported_speed = mcp->mb[2] & (BIT_0|BIT_1|BIT_2|BIT_3);
 		ql_dbg(ql_dbg_mbx, vha, 0x119b, "max_supported_speed=%s.\n",
 		    ha->max_supported_speed == 0 ? "16Gps" :
 		    ha->max_supported_speed == 1 ? "32Gps" :
-		    ha->max_supported_speed == 2 ? "64Gps" : "unknown");
+		    ha->max_supported_speed == 2 ? "64Gps" :
+		    ha->max_supported_speed == 3 ? "128Gps" : "unknown");
 		if (vha->min_supported_speed) {
 			ha->min_supported_speed = mcp->mb[5] &
-			    (BIT_0 | BIT_1 | BIT_2);
+			    (BIT_0 | BIT_1 | BIT_2 | BIT_3);
 			ql_dbg(ql_dbg_mbx, vha, 0x119c,
 			    "min_supported_speed=%s.\n",
+			    ha->min_supported_speed == 7 ? "128Gps" :
 			    ha->min_supported_speed == 6 ? "64Gps" :
 			    ha->min_supported_speed == 5 ? "32Gps" :
 			    ha->min_supported_speed == 4 ? "16Gps" :
@@ -812,7 +822,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
 		}
 	}
 
-	if (IS_QLA28XX(ha) && (mcp->mb[5] & EDIF_HW_SUPPORT)) {
+	if ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && (mcp->mb[5] & EDIF_HW_SUPPORT)) {
 		ha->flags.edif_hw = 1;
 		ql_log(ql_log_info, vha, 0xffff,
 		    "%s: edif HW\n", __func__);
@@ -846,15 +856,19 @@ qla28xx_load_flash_firmware(scsi_qla_host_t *vha)
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 
-	if (!IS_QLA28XX(ha))
+	if (!IS_QLA28XX(ha) && !IS_QLA29XX(ha))
 		return rval;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x11a6,
 	       "Entered %s.\n", __func__);
 
 	mcp->mb[0] = MBC_LOAD_FLASH_FIRMWARE;
+	mcp->mb[1] = 0;
 	mcp->out_mb = MBX_2 | MBX_1 | MBX_0;
-	mcp->in_mb = MBX_0;
+	if (IS_QLA29XX(ha))
+		mcp->in_mb = MBX_1 | MBX_0;
+	else
+		mcp->in_mb = MBX_0;
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
@@ -1125,7 +1139,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
 		mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8;
 	if (IS_FWI2_CAPABLE(ha))
 		mcp->in_mb |= MBX_17|MBX_16|MBX_15;
-	if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		mcp->in_mb |=
 		    MBX_25|MBX_24|MBX_23|MBX_22|MBX_21|MBX_20|MBX_19|MBX_18|
 		    MBX_14|MBX_13|MBX_11|MBX_10|MBX_9|MBX_8|MBX_7;
@@ -1201,7 +1215,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
 			vha->flags.nvme2_enabled = 1;
 		}
 
-		if (IS_QLA28XX(ha) && ha->flags.edif_hw && ql2xsecenable &&
+		if ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && ha->flags.edif_hw && ql2xsecenable &&
 		    (ha->fw_attributes_ext[0] & FW_ATTR_EXT0_EDIF)) {
 			ha->flags.edif_enabled = 1;
 			ql_log(ql_log_info, vha, 0xffff,
@@ -1209,7 +1223,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
 		}
 	}
 
-	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		ha->serdes_version[0] = mcp->mb[7] & 0xff;
 		ha->serdes_version[1] = mcp->mb[8] >> 8;
 		ha->serdes_version[2] = mcp->mb[8] & 0xff;
@@ -1223,7 +1237,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha)
 		ha->fw_shared_ram_end = (mcp->mb[21] << 16) | mcp->mb[20];
 		ha->fw_ddr_ram_start = (mcp->mb[23] << 16) | mcp->mb[22];
 		ha->fw_ddr_ram_end = (mcp->mb[25] << 16) | mcp->mb[24];
-		if (IS_QLA28XX(ha)) {
+		if (IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 			if (mcp->mb[16] & BIT_10)
 				ha->flags.secure_fw = 1;
 
@@ -1758,7 +1772,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
 		mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10;
 	if (IS_FWI2_CAPABLE(vha->hw))
 		mcp->in_mb |= MBX_19|MBX_18|MBX_17|MBX_16;
-	if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw))
+	if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw) || IS_QLA29XX(vha->hw))
 		mcp->in_mb |= MBX_15|MBX_21|MBX_22|MBX_23;
 
 	mcp->tov = MBX_TOV_SECONDS;
@@ -1813,7 +1827,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
 			}
 		}
 
-		if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw)) {
+		if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw) || IS_QLA29XX(vha->hw)) {
 			vha->bbcr = mcp->mb[15];
 			if (mcp->mb[7] & SCM_EDC_ACC_RECEIVED) {
 				ql_log(ql_log_info, vha, 0x11a4,
@@ -1954,7 +1968,7 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
 
 	/* 1 and 2 should normally be captured. */
 	mcp->in_mb = MBX_2|MBX_1|MBX_0;
-	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		/* mb3 is additional info about the installed SFP. */
 		mcp->in_mb  |= MBX_3;
 	mcp->buf_size = size;
@@ -1978,7 +1992,7 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
 			    0x0104d, ha->ex_init_cb, sizeof(*ha->ex_init_cb));
 		}
 	} else {
-		if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 			if (mcp->mb[2] == 6 || mcp->mb[3] == 2)
 				ql_dbg(ql_dbg_mbx, vha, 0x119d,
 				    "Invalid SFP/Validation Failed\n");
@@ -2262,6 +2276,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
 	if (!ha->flags.fw_started)
 		return QLA_FUNCTION_FAILED;
 
+	memset(&mc, 0, sizeof(mc));
+
 	mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
 	mcp->out_mb = MBX_0;
 	if (IS_FWI2_CAPABLE(vha->hw))
@@ -2269,7 +2285,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
 	else
 		mcp->in_mb = MBX_1|MBX_0;
 
-	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		mcp->mb[12] = 0;
 		mcp->out_mb |= MBX_12;
 		mcp->in_mb |= MBX_12;
@@ -2287,7 +2303,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
 		states[3] = mcp->mb[4];
 		states[4] = mcp->mb[5];
 		states[5] = mcp->mb[6];  /* DPORT status */
-		if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
+		if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 			states[11] = mcp->mb[12]; /* MPI state. */
 	}
 
@@ -2295,7 +2311,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
 		/*EMPTY*/
 		ql_dbg(ql_dbg_mbx, vha, 0x1055, "Failed=%x.\n", rval);
 	} else {
-		if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 			if (mcp->mb[2] == 6 || mcp->mb[3] == 2)
 				ql_dbg(ql_dbg_mbx, vha, 0x119e,
 				    "Invalid SFP/Validation Failed\n");
@@ -2559,7 +2575,7 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
     uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt)
 {
 	int		rval;
-
+	void		*lg_buf;
 	struct logio_entry_24xx *lg;
 	dma_addr_t	lg_dma;
 	uint32_t	iop[2];
@@ -2574,12 +2590,13 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
 	else
 		req = ha->req_q_map[0];
 
-	lg = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
-	if (lg == NULL) {
+	lg_buf = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
+	if (!lg_buf) {
 		ql_log(ql_log_warn, vha, 0x1062,
 		    "Failed to allocate login IOCB.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
+	lg = lg_buf;
 
 	lg->entry_type = LOGINOUT_PORT_IOCB_TYPE;
 	lg->entry_count = 1;
@@ -2593,8 +2610,9 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
 	lg->port_id[0] = al_pa;
 	lg->port_id[1] = area;
 	lg->port_id[2] = domain;
-	lg->vp_index = vha->vp_idx;
-	rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0,
+	qla_logio_set_vp_index(ha, lg, vha->vp_idx);
+
+	rval = qla2x00_issue_iocb_timeout(vha, lg_buf, lg_dma, 0,
 	    (ha->r_a_tov / 10 * 2) + 2);
 	if (rval != QLA_SUCCESS) {
 		ql_dbg(ql_dbg_mbx, vha, 0x1063,
@@ -2664,7 +2682,7 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
 						 */
 	}
 
-	dma_pool_free(ha->s_dma_pool, lg, lg_dma);
+	dma_pool_free(ha->s_dma_pool, lg_buf, lg_dma);
 
 	return rval;
 }
@@ -2835,6 +2853,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
     uint8_t area, uint8_t al_pa)
 {
 	int		rval;
+	void		*lg_buf;
 	struct logio_entry_24xx *lg;
 	dma_addr_t	lg_dma;
 	struct qla_hw_data *ha = vha->hw;
@@ -2843,12 +2862,13 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106d,
 	    "Entered %s.\n", __func__);
 
-	lg = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
-	if (lg == NULL) {
+	lg_buf = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
+	if (!lg_buf) {
 		ql_log(ql_log_warn, vha, 0x106e,
 		    "Failed to allocate logout IOCB.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
+	lg = lg_buf;
 
 	req = vha->req;
 	lg->entry_type = LOGINOUT_PORT_IOCB_TYPE;
@@ -2861,8 +2881,9 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
 	lg->port_id[0] = al_pa;
 	lg->port_id[1] = area;
 	lg->port_id[2] = domain;
-	lg->vp_index = vha->vp_idx;
-	rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0,
+	qla_logio_set_vp_index(ha, lg, vha->vp_idx);
+
+	rval = qla2x00_issue_iocb_timeout(vha, lg_buf, lg_dma, 0,
 	    (ha->r_a_tov / 10 * 2) + 2);
 	if (rval != QLA_SUCCESS) {
 		ql_dbg(ql_dbg_mbx, vha, 0x106f,
@@ -2884,7 +2905,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
 		    "Done %s.\n", __func__);
 	}
 
-	dma_pool_free(ha->s_dma_pool, lg, lg_dma);
+	dma_pool_free(ha->s_dma_pool, lg_buf, lg_dma);
 
 	return rval;
 }
@@ -3079,7 +3100,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha)
 	mcp->out_mb = MBX_0;
 	mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
 	if (IS_QLA81XX(ha) || IS_QLA83XX(ha) ||
-	    IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	    IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+	    IS_QLA29XX(ha))
 		mcp->in_mb |= MBX_12;
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = 0;
@@ -3304,7 +3326,7 @@ qla24xx_abort_command(srb_t *sp)
 {
 	int		rval;
 	unsigned long   flags = 0;
-
+	void		*abt_buf;
 	struct abort_entry_24xx *abt;
 	dma_addr_t	abt_dma;
 	uint32_t	handle;
@@ -3336,28 +3358,42 @@ qla24xx_abort_command(srb_t *sp)
 		return QLA_ERR_NOT_FOUND;
 	}
 
-	abt = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma);
-	if (abt == NULL) {
+	abt_buf = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma);
+	if (abt_buf == NULL) {
 		ql_log(ql_log_warn, vha, 0x108d,
 		    "Failed to allocate abort IOCB.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
+	abt = abt_buf;
 
+	/*
+	 * abort_entry_24xx_ext overlays abort_entry_24xx through
+	 * req_que_no (offsets 0-17), and entry_status (offset 3) and
+	 * comp_status (offset 8) sit at identical offsets in both, so
+	 * the common header writes and the completion-status reads are
+	 * stride-agnostic and go through the 24xx view.  Only port_id
+	 * (24xx-only) and vp_index width / offset diverge per stride.
+	 */
 	abt->entry_type = ABORT_IOCB_TYPE;
 	abt->entry_count = 1;
 	abt->handle = make_handle(req->id, abt->handle);
 	abt->nport_handle = cpu_to_le16(fcport->loop_id);
 	abt->handle_to_abort = make_handle(req->id, handle);
-	abt->port_id[0] = fcport->d_id.b.al_pa;
-	abt->port_id[1] = fcport->d_id.b.area;
-	abt->port_id[2] = fcport->d_id.b.domain;
-	abt->vp_index = fcport->vha->vp_idx;
-
 	abt->req_que_no = cpu_to_le16(req->id);
+	if (IS_QLA29XX(ha)) {
+		((struct abort_entry_24xx_ext *)abt)->vp_index =
+		    cpu_to_le16(fcport->vha->vp_idx);
+	} else {
+		abt->port_id[0] = fcport->d_id.b.al_pa;
+		abt->port_id[1] = fcport->d_id.b.area;
+		abt->port_id[2] = fcport->d_id.b.domain;
+		abt->vp_index = fcport->vha->vp_idx;
+	}
+
 	/* Need to pass original sp */
-	qla_nvme_abort_set_option(abt, sp);
+	qla_nvme_abort_set_option(abt_buf, sp);
 
-	rval = qla2x00_issue_iocb(vha, abt, abt_dma, 0);
+	rval = qla2x00_issue_iocb(vha, abt_buf, abt_dma, 0);
 	if (rval != QLA_SUCCESS) {
 		ql_dbg(ql_dbg_mbx, vha, 0x108e,
 		    "Failed to issue IOCB (%x).\n", rval);
@@ -3366,11 +3402,11 @@ qla24xx_abort_command(srb_t *sp)
 		    "Failed to complete IOCB -- error status (%x).\n",
 		    abt->entry_status);
 		rval = QLA_FUNCTION_FAILED;
-	} else if (abt->nport_handle != cpu_to_le16(0)) {
+	} else if (abt->comp_status != cpu_to_le16(0)) {
 		ql_dbg(ql_dbg_mbx, vha, 0x1090,
 		    "Failed to complete IOCB -- completion status (%x).\n",
-		    le16_to_cpu(abt->nport_handle));
-		if (abt->nport_handle == cpu_to_le16(CS_IOCB_ERROR))
+		    le16_to_cpu(abt->comp_status));
+		if (abt->comp_status == cpu_to_le16(CS_IOCB_ERROR))
 			rval = QLA_FUNCTION_PARAMETER_ERROR;
 		else
 			rval = QLA_FUNCTION_FAILED;
@@ -3378,12 +3414,13 @@ qla24xx_abort_command(srb_t *sp)
 		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1091,
 		    "Done %s.\n", __func__);
 	}
+
 	if (rval == QLA_SUCCESS)
-		qla_nvme_abort_process_comp_status(abt, sp);
+		qla_nvme_abort_process_comp_status(abt_buf, sp);
 
 	qla_wait_nvme_release_cmd_kref(sp);
 
-	dma_pool_free(ha->s_dma_pool, abt, abt_dma);
+	dma_pool_free(ha->s_dma_pool, abt_buf, abt_dma);
 
 	return rval;
 }
@@ -3391,7 +3428,9 @@ qla24xx_abort_command(srb_t *sp)
 struct tsk_mgmt_cmd {
 	union {
 		struct tsk_mgmt_entry tsk;
+		struct tsk_mgmt_entry_ext tsk_ext;
 		struct sts_entry_24xx sts;
+		struct sts_entry_24xx_ext sts_ext;
 	} p;
 };
 
@@ -3402,6 +3441,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
 	int		rval, rval2;
 	struct tsk_mgmt_cmd *tsk;
 	struct sts_entry_24xx *sts;
+	struct qla_sts_fwi2 sf;
 	dma_addr_t	tsk_dma;
 	scsi_qla_host_t *vha;
 	struct qla_hw_data *ha;
@@ -3428,16 +3468,25 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 
+	/*
+	 * tsk_mgmt_entry_ext overlays tsk_mgmt_entry through control_flags;
+	 * the common-header writes go through tsk->p.tsk and only port_id
+	 * (24xx-only) and vp_index width / offset diverge.
+	 */
 	tsk->p.tsk.entry_type = TSK_MGMT_IOCB_TYPE;
 	tsk->p.tsk.entry_count = 1;
 	tsk->p.tsk.handle = make_handle(req->id, tsk->p.tsk.handle);
 	tsk->p.tsk.nport_handle = cpu_to_le16(fcport->loop_id);
 	tsk->p.tsk.timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
 	tsk->p.tsk.control_flags = cpu_to_le32(type);
-	tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa;
-	tsk->p.tsk.port_id[1] = fcport->d_id.b.area;
-	tsk->p.tsk.port_id[2] = fcport->d_id.b.domain;
-	tsk->p.tsk.vp_index = fcport->vha->vp_idx;
+	if (IS_QLA29XX(ha)) {
+		tsk->p.tsk_ext.vp_index = cpu_to_le16(fcport->vha->vp_idx);
+	} else {
+		tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa;
+		tsk->p.tsk.port_id[1] = fcport->d_id.b.area;
+		tsk->p.tsk.port_id[2] = fcport->d_id.b.domain;
+		tsk->p.tsk.vp_index = fcport->vha->vp_idx;
+	}
 	if (type == TCF_LUN_RESET) {
 		int_to_scsilun(l, &tsk->p.tsk.lun);
 		host_to_fcp_swap((uint8_t *)&tsk->p.tsk.lun,
@@ -3459,18 +3508,20 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
 		    "Failed to complete IOCB -- completion status (%x).\n",
 		    le16_to_cpu(sts->comp_status));
 		rval = QLA_FUNCTION_FAILED;
-	} else if (le16_to_cpu(sts->scsi_status) &
-	    SS_RESPONSE_INFO_LEN_VALID) {
-		if (le32_to_cpu(sts->rsp_data_len) < 4) {
-			ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1097,
-			    "Ignoring inconsistent data length -- not enough "
-			    "response info (%d).\n",
-			    le32_to_cpu(sts->rsp_data_len));
-		} else if (sts->data[3]) {
-			ql_dbg(ql_dbg_mbx, vha, 0x1098,
-			    "Failed to complete IOCB -- response (%x).\n",
-			    sts->data[3]);
-			rval = QLA_FUNCTION_FAILED;
+	} else {
+		qla_sts_fwi2_extract(ha, sts, &sf);
+		if (sf.scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
+			if (sf.rsp_data_len < 4) {
+				ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha,
+				    0x1097,
+				    "Ignoring inconsistent data length -- not enough response info (%d).\n",
+				    sf.rsp_data_len);
+			} else if (sf.data[3]) {
+				ql_dbg(ql_dbg_mbx, vha, 0x1098,
+				    "Failed to complete IOCB -- response (%x).\n",
+				    sf.data[3]);
+				rval = QLA_FUNCTION_FAILED;
+			}
 		}
 	}
 
@@ -3551,7 +3602,8 @@ qla2x00_write_serdes_word(scsi_qla_host_t *vha, uint16_t addr, uint16_t data)
 	mbx_cmd_t *mcp = &mc;
 
 	if (!IS_QLA25XX(vha->hw) && !IS_QLA2031(vha->hw) &&
-	    !IS_QLA27XX(vha->hw) && !IS_QLA28XX(vha->hw))
+	    !IS_QLA27XX(vha->hw) && !IS_QLA28XX(vha->hw) &&
+	    !IS_QLA29XX(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1182,
@@ -3590,7 +3642,8 @@ qla2x00_read_serdes_word(scsi_qla_host_t *vha, uint16_t addr, uint16_t *data)
 	mbx_cmd_t *mcp = &mc;
 
 	if (!IS_QLA25XX(vha->hw) && !IS_QLA2031(vha->hw) &&
-	    !IS_QLA27XX(vha->hw) && !IS_QLA28XX(vha->hw))
+	    !IS_QLA27XX(vha->hw) && !IS_QLA28XX(vha->hw) &&
+	    !IS_QLA29XX(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1185,
@@ -3860,7 +3913,7 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma,
 
 	if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw) &&
 	    !IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) &&
-	    !IS_QLA28XX(vha->hw))
+	    !IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
 	if (unlikely(pci_channel_offline(vha->hw->pdev)))
@@ -4035,15 +4088,18 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
 }
 
 void
-qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
-	struct vp_rpt_id_entry_24xx *rptid_entry)
+qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt)
 {
 	struct qla_hw_data *ha = vha->hw;
+	struct vp_rpt_id_entry_24xx *rptid_entry = pkt;
+	struct vp_rpt_id_entry_24xx_ext *rptid_entry_ext = pkt;
 	scsi_qla_host_t *vp = NULL;
 	unsigned long   flags;
 	int found;
 	port_id_t id;
 	struct fc_port *fcport;
+	u16 vp_idx;
+	u8 vp_status;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b6,
 	    "Entered %s.\n", __func__);
@@ -4051,6 +4107,16 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
 	if (rptid_entry->entry_status != 0)
 		return;
 
+	if (IS_QLA29XX(ha)) {
+		vp_idx = le16_to_cpu(rptid_entry_ext->vp_idx_status) &
+			 EXT_VP_STATUS_VP_INDEX_MASK;
+		vp_status = (le16_to_cpu(rptid_entry_ext->vp_idx_status) >>
+			     EXT_VP_STATUS_VP_STATUS_SHIFT) & 0x7f;
+	} else {
+		vp_idx = rptid_entry->vp_idx;
+		vp_status = rptid_entry->vp_status;
+	}
+
 	id.b.domain = rptid_entry->port_id[2];
 	id.b.area   = rptid_entry->port_id[1];
 	id.b.al_pa  = rptid_entry->port_id[0];
@@ -4073,9 +4139,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
 	} else if (rptid_entry->format == 1) {
 		/* fabric */
 		ql_dbg(ql_dbg_async, vha, 0x10b9,
-		    "Format 1: VP[%d] enabled - status %d - with "
-		    "port id %02x%02x%02x.\n", rptid_entry->vp_idx,
-			rptid_entry->vp_status,
+		    "Format 1: VP[%d] enabled - status %d - with port id %02x%02x%02x.\n",
+		    vp_idx, vp_status,
 		    rptid_entry->port_id[2], rptid_entry->port_id[1],
 		    rptid_entry->port_id[0]);
 		ql_dbg(ql_dbg_async, vha, 0x5075,
@@ -4170,10 +4235,11 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
 		ha->flags.gpsc_supported = 1;
 		ha->current_topology = ISP_CFG_F;
 		/* buffer to buffer credit flag */
-		vha->flags.bbcr_enable = (rptid_entry->u.f1.bbcr & 0xf) != 0;
+		vha->flags.bbcr_enable =
+		    (le16_to_cpu(rptid_entry->u.f1.bbcr) & 0xf) != 0;
 
-		if (rptid_entry->vp_idx == 0) {
-			if (rptid_entry->vp_status == VP_STAT_COMPL) {
+		if (vp_idx == 0) {
+			if (vp_status == VP_STAT_COMPL) {
 				/* FA-WWN is only for physical port */
 				if (qla_ini_mode_enabled(vha) &&
 				    ha->flags.fawwpn_enabled &&
@@ -4190,19 +4256,22 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
 			set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
 			set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
 		} else {
-			if (rptid_entry->vp_status != VP_STAT_COMPL &&
-				rptid_entry->vp_status != VP_STAT_ID_CHG) {
+			if (vp_status != VP_STAT_COMPL &&
+				vp_status != VP_STAT_ID_CHG) {
 				ql_dbg(ql_dbg_mbx, vha, 0x10ba,
 				    "Could not acquire ID for VP[%d].\n",
-				    rptid_entry->vp_idx);
+				    vp_idx);
 				return;
 			}
 
 			found = 0;
 			spin_lock_irqsave(&ha->vport_slock, flags);
 			list_for_each_entry(vp, &ha->vp_list, list) {
-				if (rptid_entry->vp_idx == vp->vp_idx) {
+				if (vp_idx == vp->vp_idx) {
+					if (test_bit(VPORT_DELETE, &vp->dpc_flags))
+						break;
 					found = 1;
+					atomic_inc(&vp->vref_count);
 					break;
 				}
 			}
@@ -4211,7 +4280,9 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
 			if (!found)
 				return;
 
+			spin_lock_irqsave(&ha->vport_slock, flags);
 			qla_update_host_map(vp, id);
+			spin_unlock_irqrestore(&ha->vport_slock, flags);
 
 			/*
 			 * Cannot configure here as we are still sitting on the
@@ -4220,6 +4291,10 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
 			set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
 			set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
 			set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
+
+			spin_lock_irqsave(&ha->vport_slock, flags);
+			atomic_dec(&vp->vref_count);
+			spin_unlock_irqrestore(&ha->vport_slock, flags);
 		}
 		set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
 		qla2xxx_wake_dpc(vha);
@@ -4313,6 +4388,13 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 
+	/*
+	 * vp_config_entry_24xx_ext overlays vp_config_entry_24xx for the
+	 * full 64-byte 24xx layout (the ext variant merely appends fields
+	 * at offset 64+ which this helper never touches), so the IOCB is
+	 * built and inspected through a single struct vp_config_entry_24xx
+	 * pointer regardless of adapter stride.
+	 */
 	vpmod->entry_type = VP_CONFIG_IOCB_TYPE;
 	vpmod->entry_count = 1;
 	vpmod->command = VCT_COMMAND_MOD_ENABLE_VPS;
@@ -4330,10 +4412,10 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
 	if (rval != QLA_SUCCESS) {
 		ql_dbg(ql_dbg_mbx, vha, 0x10bd,
 		    "Failed to issue VP config IOCB (%x).\n", rval);
-	} else if (vpmod->comp_status != 0) {
+	} else if (vpmod->entry_status != 0) {
 		ql_dbg(ql_dbg_mbx, vha, 0x10be,
 		    "Failed to complete IOCB -- error status (%x).\n",
-		    vpmod->comp_status);
+		    vpmod->entry_status);
 		rval = QLA_FUNCTION_FAILED;
 	} else if (vpmod->comp_status != cpu_to_le16(CS_COMPLETE)) {
 		ql_dbg(ql_dbg_mbx, vha, 0x10bf,
@@ -4341,7 +4423,6 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
 		    le16_to_cpu(vpmod->comp_status));
 		rval = QLA_FUNCTION_FAILED;
 	} else {
-		/* EMPTY */
 		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c0,
 		    "Done %s.\n", __func__);
 		fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING);
@@ -4584,7 +4665,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
 	mcp->mb[12] = req->qos;
 	mcp->mb[11] = req->vp_idx;
 	mcp->mb[13] = req->rid;
-	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+	    IS_QLA29XX(ha))
 		mcp->mb[15] = 0;
 
 	mcp->mb[4] = req->id;
@@ -4599,9 +4681,10 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
 	mcp->tov = MBX_TOV_SECONDS * 2;
 
 	if (IS_QLA81XX(ha) || IS_QLA83XX(ha) || IS_QLA27XX(ha) ||
-	    IS_QLA28XX(ha))
+	    IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		mcp->in_mb |= MBX_1;
-	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+	    IS_QLA29XX(ha)) {
 		mcp->out_mb |= MBX_15;
 		/* debug q create issue in SR-IOV */
 		mcp->in_mb |= MBX_9 | MBX_8 | MBX_7;
@@ -4610,7 +4693,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 	if (!(req->options & BIT_0)) {
 		wrt_reg_dword(req->req_q_in, 0);
-		if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+		if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+		    !IS_QLA29XX(ha))
 			wrt_reg_dword(req->req_q_out, 0);
 	}
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -4654,7 +4738,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 	mcp->mb[5] = rsp->length;
 	mcp->mb[14] = rsp->msix->entry;
 	mcp->mb[13] = rsp->rid;
-	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+	    IS_QLA29XX(ha))
 		mcp->mb[15] = 0;
 
 	mcp->mb[4] = rsp->id;
@@ -4671,7 +4756,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 	if (IS_QLA81XX(ha)) {
 		mcp->out_mb |= MBX_12|MBX_11|MBX_10;
 		mcp->in_mb |= MBX_1;
-	} else if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+	} else if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+		   IS_QLA29XX(ha)) {
 		mcp->out_mb |= MBX_15|MBX_12|MBX_11|MBX_10;
 		mcp->in_mb |= MBX_1;
 		/* debug q create issue in SR-IOV */
@@ -4681,7 +4767,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 	if (!(rsp->options & BIT_0)) {
 		wrt_reg_dword(rsp->rsp_q_out, 0);
-		if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+		if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+		    !IS_QLA29XX(ha))
 			wrt_reg_dword(rsp->rsp_q_in, 0);
 	}
 
@@ -5064,7 +5151,8 @@ qla25xx_set_els_cmds_supported(scsi_qla_host_t *vha)
 	struct qla_hw_data *ha = vha->hw;
 
 	if (!IS_QLA25XX(ha) && !IS_QLA2031(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+	    !IS_QLA29XX(ha))
 		return QLA_SUCCESS;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1197,
@@ -5469,10 +5557,10 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
 
 	mcp->in_mb = MBX_0;
 	if (IS_CNA_CAPABLE(ha) || IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) ||
-	    IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	    IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		mcp->in_mb |= MBX_1;
 	if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
-	    IS_QLA28XX(ha))
+	    IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		mcp->in_mb |= MBX_3;
 
 	mcp->tov = MBX_TOV_SECONDS;
@@ -5643,6 +5731,8 @@ qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode)
 	case PORT_SPEED_8GB:
 	case PORT_SPEED_16GB:
 	case PORT_SPEED_32GB:
+	case PORT_SPEED_64GB:
+	case PORT_SPEED_128GB:
 		val = ha->set_data_rate;
 		break;
 	default:
@@ -5659,7 +5749,7 @@ qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode)
 
 	mcp->out_mb = MBX_2|MBX_1|MBX_0;
 	mcp->in_mb = MBX_2|MBX_1|MBX_0;
-	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		mcp->in_mb |= MBX_4|MBX_3;
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = 0;
@@ -5697,7 +5787,7 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
 	mcp->mb[1] = QLA_GET_DATA_RATE;
 	mcp->out_mb = MBX_1|MBX_0;
 	mcp->in_mb = MBX_2|MBX_1|MBX_0;
-	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 		mcp->in_mb |= MBX_4|MBX_3;
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = 0;
@@ -5706,10 +5796,11 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
 		ql_dbg(ql_dbg_mbx, vha, 0x1107,
 		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
 	} else {
-		if (mcp->mb[1] != 0x7)
+		if (mcp->mb[1] != 0x7 || IS_QLA28XX(ha) || IS_QLA29XX(ha))
 			ha->link_data_rate = mcp->mb[1];
 
-		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+		    IS_QLA29XX(ha)) {
 			if (mcp->mb[4] & BIT_0)
 				ql_log(ql_log_info, vha, 0x11a2,
 				    "FEC=enabled (data rate).\n");
@@ -5717,8 +5808,6 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
 
 		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1108,
 		    "Done %s.\n", __func__);
-		if (mcp->mb[1] != 0x7)
-			ha->link_data_rate = mcp->mb[1];
 	}
 
 	return rval;
@@ -6490,23 +6579,21 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha,
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 	dma_addr_t dd_dma;
+	void *dd;
 
 	if (!IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) &&
-	    !IS_QLA28XX(vha->hw))
+	    !IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw))
 		return QLA_FUNCTION_FAILED;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x119f,
 	    "Entered %s.\n", __func__);
 
-	dd_dma = dma_map_single(&vha->hw->pdev->dev,
-	    dd_buf, size, DMA_FROM_DEVICE);
-	if (dma_mapping_error(&vha->hw->pdev->dev, dd_dma)) {
-		ql_log(ql_log_warn, vha, 0x1194, "Failed to map dma buffer.\n");
+	dd = dma_alloc_coherent(&vha->hw->pdev->dev, size, &dd_dma, GFP_KERNEL);
+	if (!dd) {
+		ql_log(ql_log_warn, vha, 0x1194, "Failed to allocate dma buffer.\n");
 		return QLA_MEMORY_ALLOC_FAILED;
 	}
 
-	memset(dd_buf, 0, size);
-
 	mcp->mb[0] = MBC_DPORT_DIAGNOSTICS;
 	mcp->mb[1] = options;
 	mcp->mb[2] = MSW(LSD(dd_dma));
@@ -6528,8 +6615,9 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha,
 		    "Done %s.\n", __func__);
 	}
 
-	dma_unmap_single(&vha->hw->pdev->dev, dd_dma,
-	    size, DMA_FROM_DEVICE);
+	memcpy(dd_buf, dd, size);
+
+	dma_free_coherent(&vha->hw->pdev->dev, size, dd, dd_dma);
 
 	return rval;
 }
@@ -7254,3 +7342,192 @@ int qla_mpipt_validate_fw(scsi_qla_host_t *vha, u16 img_idx, uint16_t *state)
 
 	return rval;
 }
+
+int qla29xx_flash_block_read(scsi_qla_host_t *vha, dma_addr_t req_dma,
+			     uint32_t flash_addr, uint32_t flash_size,
+			     uint16_t reg_code, uint16_t opt)
+{
+	mbx_cmd_t mc;
+	mbx_cmd_t *mcp = &mc;
+	int rval = 0;
+
+	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1067,
+			"Entered %s options 0x%x.\n", __func__, opt);
+
+	memset(mcp->mb, 0, sizeof(mcp->mb));
+
+	if (!is_flash_read(opt)) {
+		ql_log(ql_log_info, vha, 0x1068,
+				"%s: Invalid flash option 0x%x.\n", __func__, opt);
+		return -EINVAL;
+	}
+
+	mcp->mb[0] = MBC_RD_WR_FLASH;
+
+	/* mailbox option field :
+	 *      TIM img or Img  : BIT_15 (1/0)
+	 *       Last seg img   : BIT_11 (1)
+	 *      First seg img   : BIT_10  (1)
+	 *      dword or block  : BIT_9  (1/0)
+	 *     flash MBR update : BIT_8  (1/0)
+	 * Secure or non-secure : BIT_7  (1/0)
+	 *        Write or Read : BIT_6  (1/0)
+	 *       Last block img : BIT_5  (1)
+	 *      First block img : BIT_4  (1)
+	 *         Request type : (BIT3 - BIT_0)
+	 *                        - Normal
+	 *                        - Normal, Force Sema
+	 *                        - Initialize
+	 *                        - Initialize, Force sema
+	 *                        - Abort secured update
+	 */
+	mcp->mb[1] = opt;
+	mcp->mb[2] = reg_code;
+
+	mcp->mb[3] = MSW(flash_size);
+	mcp->mb[4] = LSW(flash_size);
+
+	mcp->mb[5] = MSW(req_dma);
+	mcp->mb[6] = LSW(req_dma);
+	mcp->mb[7] = MSW(MSD(req_dma));
+	mcp->mb[8] = LSW(MSD(req_dma));
+
+	mcp->mb[9] = MSW(flash_addr);
+	mcp->mb[10] = LSW(flash_addr);
+
+	mcp->out_mb =
+		MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+	mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+	mcp->tov = MBX_TOV_SECONDS;
+	mcp->flags = 0;
+
+	rval = qla2x00_mailbox_command(vha, mcp);
+	if (rval != QLA_SUCCESS) {
+		ql_dbg(ql_dbg_mbx, vha, 0x103f,
+				"Failed=%x mb=(0x%x,0x%x,0x%x,0x%x).\n",
+				rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3]);
+	} else {
+		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1043,
+				"Done %s mb=(0x%x,0x%x,0x%x).\n", __func__,
+				mcp->mb[0], mcp->mb[1],  mcp->mb[2]);
+	}
+
+	return rval;
+}
+
+int qla29xx_flash_block_write(scsi_qla_host_t *vha, dma_addr_t req_dma,
+			      uint32_t flash_addr, uint32_t flash_size,
+			      uint16_t reg_code, uint16_t opt)
+{
+	mbx_cmd_t mc;
+	mbx_cmd_t *mcp = &mc;
+	int rval = 0;
+
+	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1069,
+			"Entered %s options 0x%x.\n", __func__, opt);
+
+	memset(mcp->mb, 0, sizeof(mcp->mb));
+
+	if (!is_flash_write(opt)) {
+		ql_log(ql_log_info, vha, 0x106a,
+				"%s: Invalid flash option 0x%x.\n", __func__, opt);
+		return -EINVAL;
+	}
+
+	mcp->mb[0] = MBC_RD_WR_FLASH;
+
+	/* mailbox option field :
+	 *      TIM img or Img  : BIT_15 (1/0)
+	 *       Last seg img   : BIT_11 (1)
+	 *      First seg img   : BIT_10  (1)
+	 *      dword or block  : BIT_9  (1/0)
+	 *     flash MBR update : BIT_8  (1/0)
+	 * Secure or non-secure : BIT_7  (1/0)
+	 *        Write or Read : BIT_6  (1/0)
+	 *       Last block img : BIT_5  (1)
+	 *      First block img : BIT_4  (1)
+	 *         Request type : (BIT3 - BIT_0)
+	 *                        - Normal
+	 *                        - Normal, Force Sema
+	 *                        - Initialize
+	 *                        - Initialize, Force sema
+	 *                        - Abort secured update
+	 */
+	mcp->mb[1] = opt;
+	mcp->mb[2] = reg_code;
+
+	mcp->mb[3] = MSW(flash_size);
+	mcp->mb[4] = LSW(flash_size);
+
+	mcp->mb[5] = MSW(req_dma);
+	mcp->mb[6] = LSW(req_dma);
+	mcp->mb[7] = MSW(MSD(req_dma));
+	mcp->mb[8] = LSW(MSD(req_dma));
+
+	mcp->mb[9] = MSW(flash_addr);
+	mcp->mb[10] = LSW(flash_addr);
+
+	mcp->out_mb =
+		MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+	mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+	mcp->tov = MBX_TOV_SECONDS;
+	mcp->flags = 0;
+
+	rval = qla2x00_mailbox_command(vha, mcp);
+	if (rval != QLA_SUCCESS) {
+		ql_dbg(ql_dbg_mbx, vha, 0x110a,
+				"Failed=%x mb=(0x%x,0x%x,0x%x,0x%x).\n",
+				rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3]);
+	} else {
+		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110b,
+				"Done %s mb=(0x%x,0x%x,0x%x).\n", __func__,
+				mcp->mb[0], mcp->mb[1],  mcp->mb[2]);
+	}
+
+	return rval;
+}
+
+int
+qla29xx_load_dump_mpi(scsi_qla_host_t *vha, uint16_t opt, uint32_t mpi_addr,
+		      uint32_t dlen, dma_addr_t req_dma)
+{
+	mbx_cmd_t mc;
+	mbx_cmd_t *mcp = &mc;
+	int rval = 0;
+
+	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0xffff,
+	       "Entered %s mpi_addr 0x%x len 0x%x opt 0x%x.\n",
+		__func__, mpi_addr, dlen, opt);
+
+	memset(mcp->mb, 0, sizeof(mcp->mb));
+
+	mcp->mb[0] = MBC_LOAD_DUMP_MPI_RAM;
+	mcp->mb[9] = opt;
+	mcp->mb[1] = LSW(mpi_addr);
+	mcp->mb[8] = MSW(mpi_addr);
+
+	mcp->mb[2] = MSW(req_dma);
+	mcp->mb[3] = LSW(req_dma);
+	mcp->mb[6] = MSW(MSD(req_dma));
+	mcp->mb[7] = LSW(MSD(req_dma));
+
+	mcp->mb[4] = MSW(dlen);
+	mcp->mb[5] = LSW(dlen);
+
+	mcp->out_mb = MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+	mcp->in_mb = MBX_1|MBX_0;
+	mcp->tov = MBX_TOV_SECONDS;
+	mcp->flags = 0;
+
+	rval = qla2x00_mailbox_command(vha, mcp);
+	if (rval != QLA_SUCCESS)
+		ql_dbg(ql_dbg_mbx, vha, 0x110a,
+		       "Failed=%x mb=(0x%x,0x%x).\n",
+			rval, mcp->mb[0], mcp->mb[1]);
+	else
+		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110b,
+		       "Done %s mb=(0x%x,0x%x,0x%x).\n", __func__,
+		       mcp->mb[0], mcp->mb[1],  mcp->mb[2]);
+
+	return rval;
+}
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index c563133f751e..4ad23d206add 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -574,16 +574,19 @@ qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req)
 {
 	struct qla_hw_data *ha = vha->hw;
 	uint16_t que_id = req->id;
+	size_t req_entry_size = qla_req_entry_size(ha);
 
-	dma_free_coherent(&ha->pdev->dev, (req->length + 1) *
-		sizeof(request_t), req->ring, req->dma);
+	if (req->ring)
+		dma_free_coherent(&ha->pdev->dev,
+				  (req->length + 1) * req_entry_size,
+				  req->ring, req->dma);
 	req->ring = NULL;
 	req->dma = 0;
 	if (que_id) {
+		mutex_lock(&ha->mq_lock);
 		ha->req_q_map[que_id] = NULL;
-		mutex_lock(&ha->vport_lock);
 		clear_bit(que_id, ha->req_qid_map);
-		mutex_unlock(&ha->vport_lock);
+		mutex_unlock(&ha->mq_lock);
 	}
 	kfree(req->outstanding_cmds);
 	kfree(req);
@@ -594,6 +597,7 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 {
 	struct qla_hw_data *ha = vha->hw;
 	uint16_t que_id = rsp->id;
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
 
 	if (rsp->msix && rsp->msix->have_irq) {
 		free_irq(rsp->msix->vector, rsp->msix->handle);
@@ -601,15 +605,22 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
 		rsp->msix->in_use = 0;
 		rsp->msix->handle = NULL;
 	}
-	dma_free_coherent(&ha->pdev->dev, (rsp->length + 1) *
-		sizeof(response_t), rsp->ring, rsp->dma);
+
+	/* Flush any queued response work before freeing the queue/qpair. */
+	if (rsp->qpair && ha->wq)
+		cancel_work_sync(&rsp->qpair->q_work);
+
+	if (rsp->ring)
+		dma_free_coherent(&ha->pdev->dev,
+				  (rsp->length + 1) * rsp_entry_size,
+				  rsp->ring, rsp->dma);
 	rsp->ring = NULL;
 	rsp->dma = 0;
 	if (que_id) {
+		mutex_lock(&ha->mq_lock);
 		ha->rsp_q_map[que_id] = NULL;
-		mutex_lock(&ha->vport_lock);
 		clear_bit(que_id, ha->rsp_qid_map);
-		mutex_unlock(&ha->vport_lock);
+		mutex_unlock(&ha->mq_lock);
 	}
 	kfree(rsp);
 }
@@ -706,6 +717,7 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
 	uint16_t que_id = 0;
 	device_reg_t *reg;
 	uint32_t cnt;
+	size_t req_entry_size = qla_req_entry_size(ha);
 
 	req = kzalloc_obj(struct req_que);
 	if (req == NULL) {
@@ -716,13 +728,17 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
 
 	req->length = REQUEST_ENTRY_CNT_24XX;
 	req->ring = dma_alloc_coherent(&ha->pdev->dev,
-			(req->length + 1) * sizeof(request_t),
+			(req->length + 1) * req_entry_size,
 			&req->dma, GFP_KERNEL);
 	if (req->ring == NULL) {
 		ql_log(ql_log_fatal, base_vha, 0x00da,
 		    "Failed to allocate memory for request_ring.\n");
 		goto que_failed;
 	}
+	if (IS_QLA29XX(ha)) {
+		req->ring_ext = (struct request_ext *)req->ring;
+		req->ring_ext_ptr = req->ring_ext;
+	}
 
 	ret = qla2x00_alloc_outstanding_cmds(ha, req);
 	if (ret != QLA_SUCCESS)
@@ -768,7 +784,15 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
 		req->outstanding_cmds[cnt] = NULL;
 	req->current_outstanding_cmd = 1;
 
+	/*
+	 * Re-anchor both the 24xx (ring_ptr) and 29xx (ring_ext_ptr) views
+	 * at the start of the ring.  Keeping them reset together here
+	 * guarantees they stay in sync with ring_index=0 regardless of any
+	 * prior allocator/fast-path advances against this queue.
+	 */
 	req->ring_ptr = req->ring;
+	if (IS_QLA29XX(ha))
+		req->ring_ext_ptr = req->ring_ext;
 	req->ring_index = 0;
 	req->cnt = req->length;
 	req->id = que_id;
@@ -776,7 +800,15 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
 	req->req_q_in = &reg->isp25mq.req_q_in;
 	req->req_q_out = &reg->isp25mq.req_q_out;
 	req->max_q_depth = ha->req_q_map[0]->max_q_depth;
-	req->out_ptr = (uint16_t *)(req->ring + req->length);
+	/*
+	 * out_ptr sits in the scratch slot immediately after the ring.  Use
+	 * the 29xx-stride pointer when the ring is 128-byte-per-entry so the
+	 * pointer arithmetic resolves to the correct byte offset.
+	 */
+	if (IS_QLA29XX(ha))
+		req->out_ptr = (uint16_t *)(req->ring_ext + req->length);
+	else
+		req->out_ptr = (uint16_t *)(req->ring + req->length);
 	mutex_unlock(&ha->mq_lock);
 	ql_dbg(ql_dbg_multiq, base_vha, 0xc004,
 	    "ring_ptr=%p ring_index=%d, "
@@ -794,9 +826,6 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
 		if (ret != QLA_SUCCESS) {
 			ql_log(ql_log_fatal, base_vha, 0x00df,
 			    "%s failed.\n", __func__);
-			mutex_lock(&ha->mq_lock);
-			clear_bit(que_id, ha->req_qid_map);
-			mutex_unlock(&ha->mq_lock);
 			goto que_failed;
 		}
 		vha->flags.qpairs_req_created = 1;
@@ -833,6 +862,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
 	struct scsi_qla_host *vha = pci_get_drvdata(ha->pdev);
 	uint16_t que_id = 0;
 	device_reg_t *reg;
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
 
 	rsp = kzalloc_obj(struct rsp_que);
 	if (rsp == NULL) {
@@ -843,13 +873,17 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
 
 	rsp->length = RESPONSE_ENTRY_CNT_MQ;
 	rsp->ring = dma_alloc_coherent(&ha->pdev->dev,
-			(rsp->length + 1) * sizeof(response_t),
+			(rsp->length + 1) * rsp_entry_size,
 			&rsp->dma, GFP_KERNEL);
 	if (rsp->ring == NULL) {
 		ql_log(ql_log_warn, base_vha, 0x00e1,
 		    "Failed to allocate memory for response ring.\n");
 		goto que_failed;
 	}
+	if (IS_QLA29XX(ha)) {
+		rsp->ring_ext = (struct response_ext *)rsp->ring;
+		rsp->ring_ext_ptr = rsp->ring_ext;
+	}
 
 	mutex_lock(&ha->mq_lock);
 	que_id = find_first_zero_bit(ha->rsp_qid_map, ha->max_rsp_queues);
@@ -888,7 +922,10 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
 	reg = ISP_QUE_REG(ha, que_id);
 	rsp->rsp_q_in = &reg->isp25mq.rsp_q_in;
 	rsp->rsp_q_out = &reg->isp25mq.rsp_q_out;
-	rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length);
+	if (IS_QLA29XX(ha))
+		rsp->in_ptr = (uint16_t *)(rsp->ring_ext + rsp->length);
+	else
+		rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length);
 	mutex_unlock(&ha->mq_lock);
 	ql_dbg(ql_dbg_multiq, base_vha, 0xc00b,
 	    "options=%x id=%d rsp_q_in=%p rsp_q_out=%p\n",
@@ -908,9 +945,6 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
 		if (ret != QLA_SUCCESS) {
 			ql_log(ql_log_fatal, base_vha, 0x00e7,
 			    "%s failed.\n", __func__);
-			mutex_lock(&ha->mq_lock);
-			clear_bit(que_id, ha->rsp_qid_map);
-			mutex_unlock(&ha->mq_lock);
 			goto que_failed;
 		}
 		vha->flags.qpairs_rsp_created = 1;
@@ -957,6 +991,14 @@ int qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
 	if (vp_index == 0 || vp_index >= ha->max_npiv_vports)
 		return QLA_PARAMETER_ERROR;
 
+	/*
+	 * The VP_CTRL IOCB selects the target VP through a fixed 128-bit
+	 * (16-byte) vp_idx_map bitmap, so vp_index must fit within it even
+	 * if firmware advertises more NPIV vports.
+	 */
+	if (vp_index > VP_CTRL_IDX_MAP_BITS)
+		return QLA_PARAMETER_ERROR;
+
 	/* ref: INIT */
 	sp = qla2x00_get_sp(base_vha, NULL, GFP_KERNEL);
 	if (!sp)
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 2531e71c39dc..beccece1e7d9 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -257,7 +257,9 @@ static void qla_nvme_release_lsrsp_cmd_kref(struct kref *kref)
 
 	fd_rsp = uctx->fd_rsp;
 
+	spin_lock_irqsave(&uctx->fcport->unsol_ctx_lock, flags);
 	list_del(&uctx->elem);
+	spin_unlock_irqrestore(&uctx->fcport->unsol_ctx_lock, flags);
 
 	fd_rsp->done(fd_rsp);
 	kfree(uctx);
@@ -374,6 +376,7 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
 	srb_t *sp;
 	int rval = QLA_FUNCTION_FAILED;
 	uint8_t cnt = 0;
+	unsigned long flags;
 
 	if (!fcport || fcport->deleted)
 		goto out;
@@ -440,7 +443,14 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport,
 	a.vp_idx = vha->vp_idx;
 	a.nport_handle = uctx->nport_handle;
 	a.xchg_address = uctx->exchange_address;
-	qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
+	if (ha->flags.fw_started) {
+		spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags);
+		qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true);
+		spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags);
+	}
+	spin_lock_irqsave(&uctx->fcport->unsol_ctx_lock, flags);
+	list_del(&uctx->elem);
+	spin_unlock_irqrestore(&uctx->fcport->unsol_ctx_lock, flags);
 	kfree(uctx);
 	return rval;
 }
@@ -463,8 +473,8 @@ static void qla_nvme_ls_abort(struct nvme_fc_local_port *lport,
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
-	schedule_work(&priv->abort_work);
+	if (!schedule_work(&priv->abort_work))
+		kref_put(&priv->sp->cmd_kref, priv->sp->put_fn);
 }
 
 static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
@@ -501,6 +511,7 @@ static int qla_nvme_ls_req(struct nvme_fc_local_port *lport,
 	priv->sp = sp;
 	kref_init(&sp->cmd_kref);
 	spin_lock_init(&priv->cmd_lock);
+	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	nvme = &sp->u.iocb_cmd;
 	priv->fd = fd;
 	nvme->u.nvme.desc = fd;
@@ -545,8 +556,8 @@ static void qla_nvme_fcp_abort(struct nvme_fc_local_port *lport,
 	}
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
-	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
-	schedule_work(&priv->abort_work);
+	if (!schedule_work(&priv->abort_work))
+		kref_put(&priv->sp->cmd_kref, priv->sp->put_fn);
 }
 
 static inline int qla2x00_start_nvme_mq(srb_t *sp)
@@ -554,7 +565,8 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 	unsigned long   flags;
 	uint32_t        *clr_ptr;
 	uint32_t        handle;
-	struct cmd_nvme *cmd_pkt;
+	struct cmd_nvme *cmd_pkt = NULL;
+	struct cmd_nvme_ext *cmd_pkt_ext = NULL;
 	uint16_t        cnt, i;
 	uint16_t        req_cnt;
 	uint16_t        tot_dsds;
@@ -584,7 +596,10 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 		rval = -EBUSY;
 		goto queuing_error;
 	}
-	req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+	if (IS_QLA29XX(ha))
+		req_cnt = qla29xx_calc_iocbs(vha, tot_dsds, NUM_NVME_DSDS);
+	else
+		req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
 
 	sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH;
 	sp->iores.exch_cnt = 1;
@@ -629,19 +644,62 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 	sp->handle = handle;
 	req->cnt -= req_cnt;
 
-	cmd_pkt = (struct cmd_nvme *)req->ring_ptr;
+	/*
+	 * 29xx operates on the 128-byte extended IOCB ring; the header
+	 * layout of struct cmd_nvme is identical to the head of struct
+	 * cmd_nvme_ext through 'byte_count', so the common-field writes
+	 * below go through cmd_pkt regardless of stride.  Divergent tail
+	 * fields (port_id/vp_index, DSD array) are handled via
+	 * IS_QLA29XX(ha) branches.
+	 *
+	 * Enforce the layout contract at compile time so any future
+	 * change to either struct that breaks the common-header overlap
+	 * fails the build rather than silently corrupting IOCBs.
+	 */
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, entry_type) !=
+		     offsetof(struct cmd_nvme_ext, entry_type));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, handle) !=
+		     offsetof(struct cmd_nvme_ext, handle));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, nport_handle) !=
+		     offsetof(struct cmd_nvme_ext, nport_handle));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, timeout) !=
+		     offsetof(struct cmd_nvme_ext, timeout));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, dseg_count) !=
+		     offsetof(struct cmd_nvme_ext, dseg_count));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, nvme_rsp_dsd_len) !=
+		     offsetof(struct cmd_nvme_ext, nvme_rsp_dsd_len));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, rsvd) !=
+		     offsetof(struct cmd_nvme_ext, rsvd));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, control_flags) !=
+		     offsetof(struct cmd_nvme_ext, control_flags));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, nvme_cmnd_dseg_len) !=
+		     offsetof(struct cmd_nvme_ext, nvme_cmnd_dseg_len));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, nvme_cmnd_dseg_address) !=
+		     offsetof(struct cmd_nvme_ext, nvme_cmnd_dseg_address));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, nvme_rsp_dseg_address) !=
+		     offsetof(struct cmd_nvme_ext, nvme_rsp_dseg_address));
+	BUILD_BUG_ON(offsetof(struct cmd_nvme, byte_count) !=
+		     offsetof(struct cmd_nvme_ext, byte_count));
+	BUILD_BUG_ON(sizeof_field(struct cmd_nvme, byte_count) !=
+		     sizeof_field(struct cmd_nvme_ext, byte_count));
+
+	if (IS_QLA29XX(ha))
+		cmd_pkt = (struct cmd_nvme *)req->ring_ext_ptr;
+	else
+		cmd_pkt = (struct cmd_nvme *)req->ring_ptr;
+	cmd_pkt_ext = (struct cmd_nvme_ext *)cmd_pkt;
 	cmd_pkt->handle = make_handle(req->id, handle);
 
 	/* Zero out remaining portion of packet. */
 	clr_ptr = (uint32_t *)cmd_pkt + 2;
-	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+	if (IS_QLA29XX(ha))
+		memset(clr_ptr, 0, REQUEST_ENTRY_SIZE_EXT - 8);
+	else
+		memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
 
 	cmd_pkt->entry_status = 0;
-
-	/* Update entry type to indicate Command NVME IOCB */
 	cmd_pkt->entry_type = COMMAND_NVME;
 
-	/* No data transfer how do we check buffer len == 0?? */
 	if (fd->io_dir == NVMEFC_FCP_READ) {
 		cmd_pkt->control_flags = cpu_to_le16(CF_READ_DATA);
 		qpair->counters.input_bytes += fd->payload_length;
@@ -666,18 +724,23 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 	if (sp->fcport->edif.enable && fd->io_dir != 0)
 		cmd_pkt->control_flags |= cpu_to_le16(CF_EN_EDIF);
 
-	/* Set BIT_13 of control flags for Async event */
 	if (vha->flags.nvme2_enabled &&
-	    cmd->sqe.common.opcode == nvme_admin_async_event) {
+	    cmd->sqe.common.opcode == nvme_admin_async_event)
 		cmd_pkt->control_flags |= cpu_to_le16(CF_ADMIN_ASYNC_EVENT);
-	}
 
-	/* Set NPORT-ID */
 	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
-	cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
-	cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
-	cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
-	cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
+	if (IS_QLA29XX(ha)) {
+		/*
+		 * 29xx extended NVMe IOCB has no port_id[] field; vp_index is
+		 * a 9-bit __le16 (see CMD_EXT_VP_INDEX_MASK).
+		 */
+		cmd_pkt_ext->vp_index = cpu_to_le16(sp->fcport->vha->vp_idx);
+	} else {
+		cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+		cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+		cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+		cmd_pkt->vp_index = sp->fcport->vha->vp_idx;
+	}
 
 	/* NVME RSP IU */
 	cmd_pkt->nvme_rsp_dsd_len = cpu_to_le16(fd->rsplen);
@@ -690,36 +753,51 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
 	cmd_pkt->byte_count = cpu_to_le32(fd->payload_length);
 
-	/* One DSD is available in the Command Type NVME IOCB */
-	avail_dsds = 1;
-	cur_dsd = &cmd_pkt->nvme_dsd;
+	/*
+	 * 24xx carries a single inline DSD in the NVMe command IOCB; 29xx
+	 * carries NUM_NVME_DSDS inline DSDs in the extended IOCB.
+	 */
+	if (IS_QLA29XX(ha)) {
+		avail_dsds = NUM_NVME_DSDS;
+		cur_dsd = &cmd_pkt_ext->nvme_dsd[0];
+	} else {
+		avail_dsds = 1;
+		cur_dsd = &cmd_pkt->nvme_dsd;
+	}
 	sgl = fd->first_sgl;
 
 	/* Load data segments */
 	for_each_sg(sgl, sg, tot_dsds, i) {
-		cont_a64_entry_t *cont_pkt;
-
 		/* Allocate additional continuation packets? */
 		if (avail_dsds == 0) {
-			/*
-			 * Five DSDs are available in the Continuation
-			 * Type 1 IOCB.
-			 */
-
-			/* Adjust ring index */
-			req->ring_index++;
-			if (req->ring_index == req->length) {
-				req->ring_index = 0;
-				req->ring_ptr = req->ring;
+			if (IS_QLA29XX(ha)) {
+				struct cont_a64_entry_ext *cont_pkt;
+
+				cont_pkt = qla2900_prep_cont_type1_iocb(vha,
+									req);
+				cur_dsd = cont_pkt->dsd;
+				avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
 			} else {
-				req->ring_ptr++;
+				cont_a64_entry_t *cont_pkt;
+
+				/*
+				 * Five DSDs are available in the 24xx
+				 * Continuation Type 1 IOCB.
+				 */
+				req->ring_index++;
+				if (req->ring_index == req->length) {
+					req->ring_index = 0;
+					req->ring_ptr = req->ring;
+				} else {
+					req->ring_ptr++;
+				}
+				cont_pkt = (cont_a64_entry_t *)req->ring_ptr;
+				put_unaligned_le32(CONTINUE_A64_TYPE,
+						   &cont_pkt->entry_type);
+
+				cur_dsd = cont_pkt->dsd;
+				avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
 			}
-			cont_pkt = (cont_a64_entry_t *)req->ring_ptr;
-			put_unaligned_le32(CONTINUE_A64_TYPE,
-					   &cont_pkt->entry_type);
-
-			cur_dsd = cont_pkt->dsd;
-			avail_dsds = ARRAY_SIZE(cont_pkt->dsd);
 		}
 
 		append_dsd64(&cur_dsd, sg);
@@ -731,13 +809,7 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 	wmb();
 
 	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else {
-		req->ring_ptr++;
-	}
+	qla_req_ring_advance(ha, req);
 
 	/* ignore nvme async cmd due to long timeout */
 	if (!nvme->u.nvme.aen_op)
@@ -746,7 +818,12 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp)
 	/* Set chip new ring index. */
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 
-	if (vha->flags.process_response_queue &&
+	/*
+	 * 29xx inline response drain would require the 128-byte response ring
+	 * view, which the 24xx qla24xx_process_response_queue() does not walk;
+	 * skip here and rely on the normal ISR path.
+	 */
+	if (!IS_QLA29XX(ha) && vha->flags.process_response_queue &&
 	    rsp->ring_ptr->signature != RESPONSE_PROCESSED)
 		qla24xx_process_response_queue(vha, rsp);
 
@@ -811,6 +888,7 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport,
 
 	kref_init(&sp->cmd_kref);
 	spin_lock_init(&priv->cmd_lock);
+	INIT_WORK(&priv->abort_work, qla_nvme_abort_work);
 	sp->priv = priv;
 	priv->sp = sp;
 	sp->type = SRB_NVME_CMD;
@@ -1001,36 +1079,69 @@ int qla_nvme_register_hba(struct scsi_qla_host *vha)
 	return ret;
 }
 
-void qla_nvme_abort_set_option(struct abort_entry_24xx *abt, srb_t *orig_sp)
+void qla_nvme_abort_set_option(void *pkt, srb_t *orig_sp)
 {
 	struct qla_hw_data *ha;
+	struct abort_entry_24xx *abt = pkt;
 
 	if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp)))
 		return;
 
 	ha = orig_sp->fcport->vha->hw;
 
+	/*
+	 * abort_entry_24xx_ext overlays abort_entry_24xx through 'options'
+	 * (offset 10), so options writes are stride-agnostic.  The drv
+	 * union sits at offset 56 in the 24xx layout but offset 24 in the
+	 * ext layout, so the drv writes need a typed pointer.
+	 */
 	WARN_ON_ONCE(abt->options & cpu_to_le16(BIT_0));
-	/* Use Driver Specified Retry Count */
 	abt->options |= cpu_to_le16(AOF_ABTS_RTY_CNT);
-	abt->drv.abts_rty_cnt = cpu_to_le16(2);
-	/* Use specified response timeout */
 	abt->options |= cpu_to_le16(AOF_RSP_TIMEOUT);
-	/* set it to 2 * r_a_tov in secs */
-	abt->drv.rsp_timeout = cpu_to_le16(2 * (ha->r_a_tov / 10));
+	if (IS_QLA29XX(ha)) {
+		struct abort_entry_24xx_ext *abt_ext = pkt;
+
+		abt_ext->drv.abts_rty_cnt = cpu_to_le16(2);
+		abt_ext->drv.rsp_timeout =
+		    cpu_to_le16(2 * (ha->r_a_tov / 10));
+	} else {
+		abt->drv.abts_rty_cnt = cpu_to_le16(2);
+		abt->drv.rsp_timeout = cpu_to_le16(2 * (ha->r_a_tov / 10));
+	}
 }
 
-void qla_nvme_abort_process_comp_status(struct abort_entry_24xx *abt, srb_t *orig_sp)
+void qla_nvme_abort_process_comp_status(void *pkt, srb_t *orig_sp)
 {
 	u16	comp_status;
 	struct scsi_qla_host *vha;
+	u8	rjt_vendor_unique, rjt_reason_expl, rjt_reason_code;
+	struct abort_entry_24xx *abt = pkt;
 
 	if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp)))
 		return;
 
 	vha = orig_sp->fcport->vha;
 
+	/*
+	 * comp_status sits at offset 8 in both layouts (the
+	 * nport_handle/comp_status union), so the read is
+	 * stride-agnostic.  The fw union, like drv, lives at offset
+	 * 56 in the 24xx layout and offset 24 in the ext layout, so
+	 * those byte reads still need a typed pointer.
+	 */
 	comp_status = le16_to_cpu(abt->comp_status);
+	if (IS_QLA29XX(vha->hw)) {
+		struct abort_entry_24xx_ext *abt_ext = pkt;
+
+		rjt_vendor_unique = abt_ext->fw.ba_rjt_vendorUnique;
+		rjt_reason_expl = abt_ext->fw.ba_rjt_reasonCodeExpl;
+		rjt_reason_code = abt_ext->fw.ba_rjt_reasonCode;
+	} else {
+		rjt_vendor_unique = abt->fw.ba_rjt_vendorUnique;
+		rjt_reason_expl = abt->fw.ba_rjt_reasonCodeExpl;
+		rjt_reason_code = abt->fw.ba_rjt_reasonCode;
+	}
+
 	switch (comp_status) {
 	case CS_RESET:		/* reset event aborted */
 	case CS_ABORTED:	/* IOCB was cleaned */
@@ -1050,11 +1161,8 @@ void qla_nvme_abort_process_comp_status(struct abort_entry_24xx *abt, srb_t *ori
 	/* BA_RJT was received for the ABTS */
 	case CS_REJECT_RECEIVED:
 		ql_dbg(ql_dbg_async, vha, 0xf09e,
-		       "BA_RJT was received for the ABTS rjt_vendorUnique = %u",
-			abt->fw.ba_rjt_vendorUnique);
-		ql_dbg(ql_dbg_async + ql_dbg_mbx, vha, 0xf09e,
-		       "ba_rjt_reasonCodeExpl = %u, ba_rjt_reasonCode = %u\n",
-		       abt->fw.ba_rjt_reasonCodeExpl, abt->fw.ba_rjt_reasonCode);
+		    "BA_RJT was received for the ABTS rjt_vendorUnique=%u, ba_rjt_reasonCodeExpl=%u, ba_rjt_reasonCode=%u\n",
+		    rjt_vendor_unique, rjt_reason_expl, rjt_reason_code);
 		break;
 
 	case CS_COMPLETE:
@@ -1101,37 +1209,61 @@ static void qla_nvme_fc_format_rjt(void *buf, u8 ls_cmd, u8 reason,
 	rjt->rjt.vendor = vendor;
 }
 
+/*
+ * pt_ls4_request_ext overlays pt_ls4_request through exchange_address
+ * (offsets 0-27 are byte-identical), so the common-header writes go
+ * through one struct pt_ls4_request * view.  The ext layout has a wider
+ * __le16 vp_index and places tx_/rx_byte_count and dsd[] at different
+ * offsets, so those assignments diverge per stride.
+ */
 static void qla_nvme_lsrjt_pt_iocb(struct scsi_qla_host *vha,
-				   struct pt_ls4_request *lsrjt_iocb,
+				   void *lsrjt_iocb,
 				   struct qla_nvme_lsrjt_pt_arg *a)
 {
-	lsrjt_iocb->entry_type = PT_LS4_REQUEST;
-	lsrjt_iocb->entry_count = 1;
-	lsrjt_iocb->sys_define = 0;
-	lsrjt_iocb->entry_status = 0;
-	lsrjt_iocb->handle = QLA_SKIP_HANDLE;
-	lsrjt_iocb->nport_handle = a->nport_handle;
-	lsrjt_iocb->exchange_address = a->xchg_address;
-	lsrjt_iocb->vp_index = a->vp_idx;
-
-	lsrjt_iocb->control_flags = cpu_to_le16(a->control_flags);
-
-	put_unaligned_le64(a->tx_addr, &lsrjt_iocb->dsd[0].address);
-	lsrjt_iocb->dsd[0].length = cpu_to_le32(a->tx_byte_count);
-	lsrjt_iocb->tx_dseg_count = cpu_to_le16(1);
-	lsrjt_iocb->tx_byte_count = cpu_to_le32(a->tx_byte_count);
-
-	put_unaligned_le64(a->rx_addr, &lsrjt_iocb->dsd[1].address);
-	lsrjt_iocb->dsd[1].length = 0;
-	lsrjt_iocb->rx_dseg_count = 0;
-	lsrjt_iocb->rx_byte_count = 0;
+	struct qla_hw_data *ha = vha->hw;
+	struct pt_ls4_request *pkt = lsrjt_iocb;
+
+	pkt->entry_type = PT_LS4_REQUEST;
+	pkt->entry_count = 1;
+	pkt->sys_define = 0;
+	pkt->entry_status = 0;
+	pkt->handle = QLA_SKIP_HANDLE;
+	pkt->nport_handle = a->nport_handle;
+	pkt->exchange_address = a->xchg_address;
+	pkt->control_flags = cpu_to_le16(a->control_flags);
+	pkt->tx_dseg_count = cpu_to_le16(1);
+	pkt->rx_dseg_count = 0;
+
+	if (IS_QLA29XX(ha)) {
+		struct pt_ls4_request_ext *ext = lsrjt_iocb;
+
+		ext->vp_index = cpu_to_le16(a->vp_idx);
+		ext->tx_byte_count = cpu_to_le32(a->tx_byte_count);
+		ext->rx_byte_count = 0;
+		put_unaligned_le64(a->tx_addr, &ext->dsd[0].address);
+		ext->dsd[0].length = cpu_to_le32(a->tx_byte_count);
+		put_unaligned_le64(a->rx_addr, &ext->dsd[1].address);
+		ext->dsd[1].length = 0;
+	} else {
+		pkt->vp_index = a->vp_idx;
+		pkt->tx_byte_count = cpu_to_le32(a->tx_byte_count);
+		pkt->rx_byte_count = 0;
+		put_unaligned_le64(a->tx_addr, &pkt->dsd[0].address);
+		pkt->dsd[0].length = cpu_to_le32(a->tx_byte_count);
+		put_unaligned_le64(a->rx_addr, &pkt->dsd[1].address);
+		pkt->dsd[1].length = 0;
+	}
 }
 
+/*
+ * Allocates from and advances the request ring, so the caller must hold
+ * qp->qp_lock_ptr (the response-queue caller already holds it).
+ */
 static int
 qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, struct qla_qpair *qp,
 			struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate)
 {
-	struct pt_ls4_request *lsrjt_iocb;
+	void *lsrjt_iocb;
 
 	lsrjt_iocb = __qla2x00_alloc_iocbs(qp, NULL);
 	if (!lsrjt_iocb) {
@@ -1183,6 +1315,7 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
 {
 	struct qla_nvme_unsol_ctx *uctx = item->purls_context;
 	struct qla_nvme_lsrjt_pt_arg a;
+	unsigned long flags;
 	int ret = 1;
 
 #if (IS_ENABLED(CONFIG_NVME_FC))
@@ -1195,8 +1328,17 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item)
 		a.vp_idx = vha->vp_idx;
 		a.nport_handle = uctx->nport_handle;
 		a.xchg_address = uctx->exchange_address;
-		qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true);
+		if (vha->hw->flags.fw_started) {
+			spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr,
+					  flags);
+			qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a,
+						true);
+			spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr,
+					       flags);
+		}
+		spin_lock_irqsave(&uctx->fcport->unsol_ctx_lock, flags);
 		list_del(&uctx->elem);
+		spin_unlock_irqrestore(&uctx->fcport->unsol_ctx_lock, flags);
 		kfree(uctx);
 	}
 }
@@ -1238,6 +1380,7 @@ void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp)
 	struct purex_item *item;
 	port_id_t d_id = {0};
 	port_id_t id = {0};
+	unsigned long flags;
 	u8 *opcode;
 	bool xmt_reject = false;
 
@@ -1303,7 +1446,9 @@ void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp)
 	uctx->ox_id = p->ox_id;
 	qla_rport->uctx = uctx;
 	INIT_LIST_HEAD(&uctx->elem);
+	spin_lock_irqsave(&fcport->unsol_ctx_lock, flags);
 	list_add_tail(&uctx->elem, &fcport->unsol_ctx_head);
+	spin_unlock_irqrestore(&fcport->unsol_ctx_lock, flags);
 	item->purls_context = (void *)uctx;
 
 	ql_dbg(ql_dbg_unsol, vha, 0x2121,
diff --git a/drivers/scsi/qla2xxx/qla_nvme.h b/drivers/scsi/qla2xxx/qla_nvme.h
index a253ac55171b..e6a41d53cbe1 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.h
+++ b/drivers/scsi/qla2xxx/qla_nvme.h
@@ -144,7 +144,7 @@ struct pt_ls4_rx_unsol {
 int qla_nvme_register_hba(struct scsi_qla_host *);
 int  qla_nvme_register_remote(struct scsi_qla_host *, struct fc_port *);
 void qla_nvme_delete(struct scsi_qla_host *);
-void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *, struct pt_ls4_request *,
-    struct req_que *);
+void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha, void *pkt,
+			   struct req_que *req);
 void qla24xx_async_gffid_sp_done(struct srb *sp, int);
 #endif
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c
index 298c060c1292..2366cca51bce 100644
--- a/drivers/scsi/qla2xxx/qla_nx.c
+++ b/drivers/scsi/qla2xxx/qla_nx.c
@@ -2663,7 +2663,7 @@ qla82xx_write_flash_data(struct scsi_qla_host *vha, __le32 *dwptr,
 
 			ret = qla2x00_load_ram(vha, optrom_dma,
 			    (ha->flash_data_off | faddr),
-			    OPTROM_BURST_DWORDS);
+			    OPTROM_BURST_DWORDS, 0);
 			if (ret != QLA_SUCCESS) {
 				ql_log(ql_log_warn, vha, 0xb01e,
 				    "Unable to burst-write optrom segment "
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 72b1c28e4dae..c0efdbff5da7 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -429,7 +429,8 @@ static void qla_init_base_qpair(struct scsi_qla_host *vha, struct req_que *req,
 	qla_cpu_update(rsp->qpair, raw_smp_processor_id());
 	ha->base_qpair->pdev = ha->pdev;
 
-	if (IS_QLA27XX(ha) || IS_QLA83XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA27XX(ha) || IS_QLA83XX(ha) || IS_QLA28XX(ha) ||
+	    IS_QLA29XX(ha))
 		ha->base_qpair->reqq_start_iocbs = qla_83xx_start_iocbs;
 }
 
@@ -501,6 +502,8 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req,
 
 static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req)
 {
+	size_t req_entry_size = qla_req_entry_size(ha);
+
 	if (IS_QLAFX00(ha)) {
 		if (req && req->ring_fx00)
 			dma_free_coherent(&ha->pdev->dev,
@@ -508,8 +511,8 @@ static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req)
 			    req->ring_fx00, req->dma_fx00);
 	} else if (req && req->ring)
 		dma_free_coherent(&ha->pdev->dev,
-		(req->length + 1) * sizeof(request_t),
-		req->ring, req->dma);
+		    (req->length + 1) * req_entry_size,
+		    req->ring, req->dma);
 
 	if (req)
 		kfree(req->outstanding_cmds);
@@ -519,6 +522,8 @@ static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req)
 
 static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp)
 {
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
+
 	if (IS_QLAFX00(ha)) {
 		if (rsp && rsp->ring_fx00)
 			dma_free_coherent(&ha->pdev->dev,
@@ -526,8 +531,8 @@ static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp)
 			    rsp->ring_fx00, rsp->dma_fx00);
 	} else if (rsp && rsp->ring) {
 		dma_free_coherent(&ha->pdev->dev,
-		(rsp->length + 1) * sizeof(response_t),
-		rsp->ring, rsp->dma);
+		    (rsp->length + 1) * rsp_entry_size,
+		    rsp->ring, rsp->dma);
 	}
 	kfree(rsp);
 }
@@ -2127,7 +2132,7 @@ qla2x00_iospace_config(struct qla_hw_data *ha)
 		ha->msix_count = msix + 1;
 		/* Max queues are bounded by available msix vectors */
 		/* MB interrupt uses 1 vector */
-		ha->max_req_queues = ha->msix_count - 1;
+		ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
 		ha->max_rsp_queues = ha->max_req_queues;
 		/* Queue pairs is the max value minus the base queue pair */
 		ha->max_qpairs = ha->max_rsp_queues - 1;
@@ -2153,8 +2158,6 @@ qla2x00_iospace_config(struct qla_hw_data *ha)
 static int
 qla83xx_iospace_config(struct qla_hw_data *ha)
 {
-	uint16_t msix;
-
 	if (pci_request_selected_regions(ha->pdev, ha->bars,
 	    QLA2XXX_DRIVER_NAME)) {
 		ql_log_pci(ql_log_fatal, ha->pdev, 0x0117,
@@ -2203,20 +2206,28 @@ qla83xx_iospace_config(struct qla_hw_data *ha)
 	ha->msixbase = ioremap(pci_resource_start(ha->pdev, 2),
 			pci_resource_len(ha->pdev, 2));
 	if (ha->msixbase) {
+		int msix_cnt;
+
 		/* Read MSIX vector size of the board */
-		pci_read_config_word(ha->pdev,
-		    QLA_83XX_PCI_MSIX_CONTROL, &msix);
-		ha->msix_count = (msix & PCI_MSIX_FLAGS_QSIZE)  + 1;
+		msix_cnt = pci_msix_vec_count(ha->pdev);
+		if (msix_cnt <= 0) {
+			ql_log_pci(ql_log_warn, ha->pdev, 0x0120,
+				   "Failed to read MSI-X count (%d), falling back to base vectors.\n",
+				   msix_cnt);
+			goto mqiobase_exit;
+		}
+		ha->msix_count = msix_cnt;
+
 		/*
 		 * By default, driver uses at least two msix vectors
 		 * (default & rspq)
 		 */
 		if (ql2xmqsupport || ql2xnvmeenable) {
 			/* MB interrupt uses 1 vector */
-			ha->max_req_queues = ha->msix_count - 1;
+			ha->max_req_queues = qla_calc_queue_count(ha->msix_count);
 
 			/* ATIOQ needs 1 vector. That's 1 less QPair */
-			if (QLA_TGT_MODE_ENABLED())
+			if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1)
 				ha->max_req_queues--;
 
 			ha->max_rsp_queues = ha->max_req_queues;
@@ -2633,6 +2644,66 @@ static struct isp_operations qla27xx_isp_ops = {
 	.initialize_adapter	= qla2x00_initialize_adapter,
 };
 
+static void *
+qla29xx_read_optrom_stub(struct scsi_qla_host *vha, void *buf,
+			 uint32_t offset, uint32_t length)
+{
+	ql_dbg(ql_dbg_init, vha, 0x0191,
+	    "read_optrom not supported on 29xx, use read_optrom_region.\n");
+	return NULL;
+}
+
+static int
+qla29xx_write_optrom_stub(struct scsi_qla_host *vha, void *buf,
+			  uint32_t offset, uint32_t length)
+{
+	ql_dbg(ql_dbg_init, vha, 0x0192,
+	    "write_optrom not supported on 29xx, use write_optrom_region.\n");
+	return QLA_FUNCTION_FAILED;
+}
+
+static struct isp_operations qla29xx_isp_ops = {
+	.pci_config		= qla25xx_pci_config,
+	.reset_chip		= qla24xx_reset_chip,
+	.chip_diag		= qla24xx_chip_diag,
+	.config_rings		= qla24xx_config_rings,
+	.reset_adapter		= qla24xx_reset_adapter,
+	.nvram_config		= qla81xx_nvram_config,
+	.update_fw_options	= qla24xx_update_fw_options,
+	.load_risc		= qla29xx_load_risc,
+	.pci_info_str		= qla24xx_pci_info_str,
+	.fw_version_str		= qla24xx_fw_version_str,
+	.intr_handler		= qla24xx_intr_handler,
+	.enable_intrs		= qla24xx_enable_intrs,
+	.disable_intrs		= qla24xx_disable_intrs,
+	.abort_command		= qla24xx_abort_command,
+	.target_reset		= qla24xx_abort_target,
+	.lun_reset		= qla24xx_lun_reset,
+	.fabric_login		= qla24xx_login_fabric,
+	.fabric_logout		= qla24xx_fabric_logout,
+	.calc_req_entries	= NULL,
+	.build_iocbs		= NULL,
+	.prep_ms_iocb		= qla24xx_prep_ms_iocb,
+	.prep_ms_fdmi_iocb	= qla24xx_prep_ms_fdmi_iocb,
+	.read_nvram		= NULL,
+	.write_nvram		= NULL,
+	.fw_dump		= qla27xx_fwdump,
+	.mpi_fw_dump		= qla27xx_mpi_fwdump,
+	.beacon_on		= qla24xx_beacon_on,
+	.beacon_off		= qla24xx_beacon_off,
+	.beacon_blink		= qla83xx_beacon_blink,
+	.read_optrom		= qla29xx_read_optrom_stub,
+	.write_optrom		= qla29xx_write_optrom_stub,
+	.read_optrom_region	= qla29xx_read_optrom_data,
+	.write_optrom_region	= qla29xx_write_optrom_data,
+	.get_flash_version	= qla24xx_get_flash_version,
+	.start_scsi		= qla24xx_dif_start_scsi,
+	.start_scsi_mq		= qla2xxx_dif_start_scsi_mq,
+	.abort_isp		= qla2x00_abort_isp,
+	.iospace_config		= qla83xx_iospace_config,
+	.initialize_adapter	= qla2x00_initialize_adapter,
+};
+
 static inline void
 qla2x00_set_isp_flags(struct qla_hw_data *ha)
 {
@@ -2796,6 +2867,20 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha)
 		ha->device_type |= DT_T10_PI;
 		ha->fw_srisc_address = RISC_START_ADDRESS_2400;
 		break;
+	case PCI_DEVICE_ID_QLOGIC_ISP2099:
+	case PCI_DEVICE_ID_QLOGIC_ISP2299:
+	case PCI_DEVICE_ID_QLOGIC_ISP2091:
+	case PCI_DEVICE_ID_QLOGIC_ISP2291:
+		ha->isp_type |= DT_ISP2299;
+		ha->isp_type |= DT_ISP2099;
+		ha->isp_type |= DT_ISP2091;
+		ha->isp_type |= DT_ISP2291;
+		ha->device_type |= DT_ZIO_SUPPORTED;
+		ha->device_type |= DT_FWI2;
+		ha->device_type |= DT_IIDMA;
+		ha->device_type |= DT_T10_PI;
+		ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+		break;
 	}
 
 	if (IS_QLA82XX(ha))
@@ -2803,11 +2888,15 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha)
 	else {
 		/* Get adapter physical port no from interrupt pin register. */
 		pci_read_config_byte(ha->pdev, PCI_INTERRUPT_PIN, &ha->port_no);
-		if (IS_QLA25XX(ha) || IS_QLA2031(ha) ||
-		    IS_QLA27XX(ha) || IS_QLA28XX(ha))
+		if (ha->port_no == 0) {
+			/* None of INT[A|B|C|D] may be virtualized by vfio */
+			ha->port_no = PCI_FUNC(ha->pdev->devfn);
+		} else if (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
+			   IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 			ha->port_no--;
-		else
+		} else {
 			ha->port_no = !(ha->port_no & 1);
+		}
 	}
 
 	ql_dbg_pci(ql_dbg_init, ha->pdev, 0x000b,
@@ -2936,7 +3025,11 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2081 ||
 	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2281 ||
 	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2089 ||
-	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2289) {
+	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2289 ||
+	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2099 ||
+	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2299 ||
+	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2091 ||
+	    pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2291) {
 		bars = pci_select_bars(pdev, IORESOURCE_MEM);
 		mem_only = 1;
 		ql_dbg_pci(ql_dbg_init, pdev, 0x0007,
@@ -2998,7 +3091,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	/* Set EEH reset type to fundamental if required by hba */
 	if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha) ||
-	    IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+	    IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) ||
+	    IS_QLA29XX(ha))
 		pdev->needs_freset = 1;
 
 	ha->prev_topology = 0;
@@ -3195,6 +3289,23 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 		ha->flash_data_off = FARX_ACCESS_FLASH_DATA_28XX;
 		ha->nvram_conf_off = ~0;
 		ha->nvram_data_off = ~0;
+	} else if (IS_QLA29XX(ha)) {
+		ha->portnum = PCI_FUNC(ha->pdev->devfn);
+		ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
+		ha->mbx_count = MAILBOX_REGISTER_COUNT;
+		req_length = REQUEST_ENTRY_CNT_83XX;
+		rsp_length = RESPONSE_ENTRY_CNT_83XX;
+		ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
+		ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
+		ha->gid_list_info_size = 8;
+		ha->optrom_size = OPTROM_SIZE_28XX;
+		ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
+		ha->isp_ops = &qla29xx_isp_ops;
+		ha->flash_conf_off = ~0;
+		ha->flash_data_off = ~0;
+		ha->nvram_conf_off = ~0;
+		ha->nvram_data_off = ~0;
+		ha->flt_segment_length = QLA_SEGMENT_LENGTH;
 	}
 
 	ql_dbg_pci(ql_dbg_init, pdev, 0x001e,
@@ -3373,7 +3484,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 	rsp->rsp_q_in = &ha->iobase->isp24.rsp_q_in;
 	rsp->rsp_q_out = &ha->iobase->isp24.rsp_q_out;
 	if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha) ||
-	    IS_QLA28XX(ha)) {
+	    IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		req->req_q_in = &ha->mqiobase->isp25mq.req_q_in;
 		req->req_q_out = &ha->mqiobase->isp25mq.req_q_out;
 		rsp->rsp_q_in = &ha->mqiobase->isp25mq.rsp_q_in;
@@ -3614,6 +3725,14 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (test_bit(UNLOADING, &base_vha->dpc_flags))
 		return -ENODEV;
 
+	/*
+	 * FW dump can happens before sysfs nodes are created.  If sysfs nodes
+	 * are unavailable then udev script will not be able to read the fw dump.
+	 * Notify udev to read again, now that sysfs nodes are available.
+	 */
+	if (ha->fw_dumped || ha->mpi_fw_dumped)
+		qla2x00_post_uevent_work(base_vha, QLA_UEVENT_CODE_FW_DUMP);
+
 	return 0;
 
 probe_failed:
@@ -3733,7 +3852,7 @@ qla2x00_shutdown(struct pci_dev *pdev)
 		qla2x00_disable_eft_trace(vha);
 
 	if (IS_QLA25XX(ha) ||  IS_QLA2031(ha) || IS_QLA27XX(ha) ||
-	    IS_QLA28XX(ha)) {
+	    IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		if (ha->flags.fw_started)
 			qla2x00_abort_isp_cleanup(vha);
 	} else {
@@ -3898,7 +4017,7 @@ qla2x00_remove_one(struct pci_dev *pdev)
 		return;
 
 	if (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
-	    IS_QLA28XX(ha)) {
+	    IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		if (ha->flags.fw_started)
 			qla2x00_abort_isp_cleanup(base_vha);
 	} else if (!IS_QLAFX00(ha)) {
@@ -3933,8 +4052,6 @@ qla2x00_remove_one(struct pci_dev *pdev)
 
 	qla2x00_dfs_remove(base_vha);
 
-	qla84xx_put_chip(base_vha);
-
 	/* Disable timer */
 	if (base_vha->timer_active)
 		qla2x00_stop_timer(base_vha);
@@ -3959,6 +4076,8 @@ qla2x00_remove_one(struct pci_dev *pdev)
 
 	scsi_remove_host(base_vha->host);
 
+	qla84xx_put_chip(base_vha);
+
 	qla2x00_free_device(base_vha);
 
 	qla2x00_clear_drv_active(ha);
@@ -4147,6 +4266,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 {
 	char	name[16];
 	int rc;
+	size_t req_entry_size = qla_req_entry_size(ha);
+	size_t rsp_entry_size = qla_rsp_entry_size(ha);
 
 	if (QLA_TGT_MODE_ENABLED() || EDIF_CAP(ha)) {
 		ha->vp_map = kzalloc_objs(struct qla_vp_map,
@@ -4176,7 +4297,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	if (!ha->srb_mempool)
 		goto fail_free_gid_list;
 
-	if (IS_P3P_TYPE(ha) || IS_QLA27XX(ha) || (ql2xsecenable && IS_QLA28XX(ha))) {
+	if (IS_P3P_TYPE(ha) || IS_QLA27XX(ha) ||
+	    (ql2xsecenable && (IS_QLA28XX(ha) || IS_QLA29XX(ha)))) {
 		/* Allocate cache for CT6 Ctx. */
 		if (!ctx_cachep) {
 			ctx_cachep = kmem_cache_create("qla2xxx_ctx",
@@ -4210,7 +4332,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	    "init_cb=%p gid_list=%p, srb_mempool=%p s_dma_pool=%p.\n",
 	    ha->init_cb, ha->gid_list, ha->srb_mempool, ha->s_dma_pool);
 
-	if (IS_P3P_TYPE(ha) || ql2xenabledif || (IS_QLA28XX(ha) && ql2xsecenable)) {
+	if (IS_P3P_TYPE(ha) || ql2xenabledif ||
+	    ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && ql2xsecenable)) {
 		ha->dl_dma_pool = dma_pool_create(name, &ha->pdev->dev,
 			DSD_LIST_DMA_POOL_SIZE, 8, 0);
 		if (!ha->dl_dma_pool) {
@@ -4342,13 +4465,17 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	}
 	(*req)->length = req_len;
 	(*req)->ring = dma_alloc_coherent(&ha->pdev->dev,
-		((*req)->length + 1) * sizeof(request_t),
+		((*req)->length + 1) * req_entry_size,
 		&(*req)->dma, GFP_KERNEL);
 	if (!(*req)->ring) {
 		ql_log_pci(ql_log_fatal, ha->pdev, 0x0029,
 		    "Failed to allocate memory for req_ring.\n");
 		goto fail_req_ring;
 	}
+	if (IS_QLA29XX(ha)) {
+		(*req)->ring_ext = (struct request_ext *)(*req)->ring;
+		(*req)->ring_ext_ptr = (*req)->ring_ext;
+	}
 	/* Allocate memory for response ring */
 	*rsp = kzalloc_obj(struct rsp_que);
 	if (!*rsp) {
@@ -4359,13 +4486,17 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	(*rsp)->hw = ha;
 	(*rsp)->length = rsp_len;
 	(*rsp)->ring = dma_alloc_coherent(&ha->pdev->dev,
-		((*rsp)->length + 1) * sizeof(response_t),
+		((*rsp)->length + 1) * rsp_entry_size,
 		&(*rsp)->dma, GFP_KERNEL);
 	if (!(*rsp)->ring) {
 		ql_log_pci(ql_log_fatal, ha->pdev, 0x002b,
 		    "Failed to allocate memory for rsp_ring.\n");
 		goto fail_rsp_ring;
 	}
+	if (IS_QLA29XX(ha)) {
+		(*rsp)->ring_ext = (struct response_ext *)(*rsp)->ring;
+		(*rsp)->ring_ext_ptr = (*rsp)->ring_ext;
+	}
 	(*req)->rsp = *rsp;
 	(*rsp)->req = *req;
 	ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002c,
@@ -4387,7 +4518,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 
 	/* Get consistent memory allocated for EX-INIT-CB. */
 	if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
-	    IS_QLA28XX(ha)) {
+	    IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		ha->ex_init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
 		    &ha->ex_init_cb_dma);
 		if (!ha->ex_init_cb)
@@ -4397,7 +4528,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	}
 
 	/* Get consistent memory allocated for Special Features-CB. */
-	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) {
 		ha->sf_init_cb = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL,
 						&ha->sf_init_cb_dma);
 		if (!ha->sf_init_cb)
@@ -4446,6 +4577,14 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 		goto fail_flt_buffer;
 	}
 
+	ha->flt_data = vzalloc(sizeof(struct qla_flash_layout) +
+			(sizeof(struct qla_flt_region_data) * FLT_MAX_REGIONS));
+	if (!ha->flt_data) {
+		ql_dbg_pci(ql_dbg_init, ha->pdev, 0x001a,
+			   "Unable to allocate memory for mini FLT data.\n");
+		goto fail_flt;
+	}
+
 	/* allocate the purex dma pool */
 	ha->purex_dma_pool = dma_pool_create(name, &ha->pdev->dev,
 	    ELS_MAX_PAYLOAD, 8, 0);
@@ -4453,7 +4592,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	if (!ha->purex_dma_pool) {
 		ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011b,
 		    "Unable to allocate purex_dma_pool.\n");
-		goto fail_flt;
+		goto fail_flt_data;
 	}
 
 	ha->elsrej.size = sizeof(struct fc_els_ls_rjt) + 16;
@@ -4484,36 +4623,56 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 fail_lsrjt:
 	dma_free_coherent(&ha->pdev->dev, ha->elsrej.size,
 			  ha->elsrej.c, ha->elsrej.cdma);
+	ha->elsrej.c = NULL;
+	ha->elsrej.cdma = 0;
 fail_elsrej:
 	dma_pool_destroy(ha->purex_dma_pool);
+	ha->purex_dma_pool = NULL;
+fail_flt_data:
+	vfree(ha->flt_data);
+	ha->flt_data = NULL;
 fail_flt:
 	dma_free_coherent(&ha->pdev->dev, sizeof(struct qla_flt_header) + FLT_REGIONS_SIZE,
 	    ha->flt, ha->flt_dma);
+	ha->flt = NULL;
+	ha->flt_dma = 0;
 
 fail_flt_buffer:
 	dma_free_coherent(&ha->pdev->dev, SFP_DEV_SIZE,
 	    ha->sfp_data, ha->sfp_data_dma);
+	ha->sfp_data = NULL;
+	ha->sfp_data_dma = 0;
 fail_sfp_data:
 	kfree(ha->loop_id_map);
+	ha->loop_id_map = NULL;
 fail_loop_id_map:
 	dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma);
+	ha->async_pd = NULL;
+	ha->async_pd_dma = 0;
 fail_async_pd:
 	dma_pool_free(ha->s_dma_pool, ha->sf_init_cb, ha->sf_init_cb_dma);
+	ha->sf_init_cb = NULL;
+	ha->sf_init_cb_dma = 0;
 fail_sf_init_cb:
 	dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma);
+	ha->ex_init_cb = NULL;
+	ha->ex_init_cb_dma = 0;
 fail_ex_init_cb:
 	kfree(ha->npiv_info);
+	ha->npiv_info = NULL;
 fail_npiv_info:
-	dma_free_coherent(&ha->pdev->dev, ((*rsp)->length + 1) *
-		sizeof(response_t), (*rsp)->ring, (*rsp)->dma);
+	dma_free_coherent(&ha->pdev->dev,
+		((*rsp)->length + 1) * rsp_entry_size,
+		(*rsp)->ring, (*rsp)->dma);
 	(*rsp)->ring = NULL;
 	(*rsp)->dma = 0;
 fail_rsp_ring:
 	kfree(*rsp);
 	*rsp = NULL;
 fail_rsp:
-	dma_free_coherent(&ha->pdev->dev, ((*req)->length + 1) *
-		sizeof(request_t), (*req)->ring, (*req)->dma);
+	dma_free_coherent(&ha->pdev->dev,
+		((*req)->length + 1) * req_entry_size,
+		(*req)->ring, (*req)->dma);
 	(*req)->ring = NULL;
 	(*req)->dma = 0;
 fail_req_ring:
@@ -4551,12 +4710,12 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 	}
 
 fail_dif_bundl_dma_pool:
-	if (IS_QLA82XX(ha) || ql2xenabledif) {
+	if (IS_QLA82XX(ha) || IS_QLA29XX(ha) || ql2xenabledif) {
 		dma_pool_destroy(ha->fcp_cmnd_dma_pool);
 		ha->fcp_cmnd_dma_pool = NULL;
 	}
 fail_dl_dma_pool:
-	if (IS_QLA82XX(ha) || ql2xenabledif) {
+	if (IS_QLA82XX(ha) || IS_QLA29XX(ha) || ql2xenabledif) {
 		dma_pool_destroy(ha->dl_dma_pool);
 		ha->dl_dma_pool = NULL;
 	}
@@ -4566,6 +4725,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 fail_free_nvram:
 	kfree(ha->nvram);
 	ha->nvram = NULL;
+	ha->fiv = NULL;
 fail_free_ctx_mempool:
 	mempool_destroy(ha->ctx_mempool);
 	ha->ctx_mempool = NULL;
@@ -4921,6 +5081,10 @@ qla2x00_mem_free(struct qla_hw_data *ha)
 	ha->flt = NULL;
 	ha->flt_dma = 0;
 
+	if (ha->flt_data)
+		vfree(ha->flt_data);
+	ha->flt_data = NULL;
+
 	if (ha->ms_iocb)
 		dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
 	ha->ms_iocb = NULL;
@@ -5027,6 +5191,7 @@ qla2x00_mem_free(struct qla_hw_data *ha)
 	ha->optrom_buffer = NULL;
 	kfree(ha->nvram);
 	ha->nvram = NULL;
+	ha->fiv = NULL;
 	kfree(ha->npiv_info);
 	ha->npiv_info = NULL;
 	kfree(ha->swl);
@@ -6000,13 +6165,15 @@ qla83xx_idc_lock(scsi_qla_host_t *base_vha, uint16_t requester_id)
 }
 
 static bool
-qla25xx_rdp_rsp_reduce_size(struct scsi_qla_host *vha,
-	struct purex_entry_24xx *purex)
+qla25xx_rdp_rsp_reduce_size(struct scsi_qla_host *vha, void *pkt)
 {
+	struct purex_entry_24xx *purex = pkt;
 	char fwstr[16];
-	u32 sid = purex->s_id[2] << 16 | purex->s_id[1] << 8 | purex->s_id[0];
+	u32 sid;
 	struct port_database_24xx *pdb;
 
+	sid = purex->s_id[2] << 16 | purex->s_id[1] << 8 | purex->s_id[0];
+
 	/* Domain Controller is always logged-out. */
 	/* if RDP request is not from Domain Controller: */
 	if (sid != 0xfffc01)
@@ -6071,15 +6238,28 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
 	uint8_t *sfp = NULL;
 	uint16_t sfp_flags = 0;
 	uint rsp_payload_length = sizeof(*rsp_payload);
+	u16 vp_idx;
+	size_t purex_sz;
+	size_t rsp_els_sz;
+	void *rsp_els_pkt = NULL;
 	int rval;
 
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0180,
 	    "%s: Enter\n", __func__);
 
+	if (IS_QLA29XX(ha)) {
+		vp_idx = le16_to_cpu(
+		    ((struct purex_entry_24xx_ext *)purex)->vp_idx);
+		purex_sz = sizeof(struct purex_entry_24xx_ext);
+	} else {
+		vp_idx = purex->vp_idx;
+		purex_sz = sizeof(*purex);
+	}
+
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0181,
 	    "-------- ELS REQ -------\n");
 	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0182,
-	    purex, sizeof(*purex));
+	    purex, purex_sz);
 
 	if (qla25xx_rdp_rsp_reduce_size(vha, purex)) {
 		rsp_payload_length =
@@ -6089,13 +6269,19 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
 		    rsp_payload_length);
 	}
 
-	rsp_els = dma_alloc_coherent(&ha->pdev->dev, sizeof(*rsp_els),
+	if (IS_QLA29XX(ha))
+		rsp_els_sz = sizeof(struct els_entry_24xx_ext);
+	else
+		rsp_els_sz = sizeof(struct els_entry_24xx);
+
+	rsp_els_pkt = dma_alloc_coherent(&ha->pdev->dev, rsp_els_sz,
 	    &rsp_els_dma, GFP_KERNEL);
-	if (!rsp_els) {
+	if (!rsp_els_pkt) {
 		ql_log(ql_log_warn, vha, 0x0183,
-		    "Failed allocate dma buffer ELS RSP.\n");
+		    "Failed to allocate dma buffer ELS RSP.\n");
 		goto dealloc;
 	}
+	rsp_els = rsp_els_pkt;
 
 	rsp_payload = dma_alloc_coherent(&ha->pdev->dev, sizeof(*rsp_payload),
 	    &rsp_payload_dma, GFP_KERNEL);
@@ -6119,12 +6305,12 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
 	rsp_els->handle = 0;
 	rsp_els->nport_handle = purex->nport_handle;
 	rsp_els->tx_dsd_count = cpu_to_le16(1);
-	rsp_els->vp_index = purex->vp_idx;
-	rsp_els->sof_type = EST_SOFI3;
 	rsp_els->rx_xchg_address = purex->rx_xchg_addr;
 	rsp_els->rx_dsd_count = 0;
 	rsp_els->opcode = purex->els_frame_payload[0];
 
+	qla_els_set_vp_sof(vha, rsp_els_pkt, vp_idx);
+
 	rsp_els->d_id[0] = purex->s_id[0];
 	rsp_els->d_id[1] = purex->s_id[1];
 	rsp_els->d_id[2] = purex->s_id[2];
@@ -6424,13 +6610,13 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0184,
 	    "-------- ELS RSP -------\n");
 	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0185,
-	    rsp_els, sizeof(*rsp_els));
+	    rsp_els_pkt, rsp_els_sz);
 	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0186,
 	    "-------- ELS RSP PAYLOAD -------\n");
 	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0187,
 	    rsp_payload, rsp_payload_length);
 
-	rval = qla2x00_issue_iocb(vha, rsp_els, rsp_els_dma, 0);
+	rval = qla2x00_issue_iocb(vha, rsp_els_pkt, rsp_els_dma, 0);
 
 	if (rval) {
 		ql_log(ql_log_warn, vha, 0x0188,
@@ -6454,9 +6640,9 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
 	if (rsp_payload)
 		dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_payload),
 		    rsp_payload, rsp_payload_dma);
-	if (rsp_els)
-		dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_els),
-		    rsp_els, rsp_els_dma);
+	if (rsp_els_pkt)
+		dma_free_coherent(&ha->pdev->dev, rsp_els_sz,
+		    rsp_els_pkt, rsp_els_dma);
 }
 
 void
@@ -6828,8 +7014,6 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
 
 	qla2x00_dfs_remove(base_vha);
 
-	qla84xx_put_chip(base_vha);
-
 	if (base_vha->timer_active)
 		qla2x00_stop_timer(base_vha);
 
@@ -6847,6 +7031,8 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
 
 	scsi_remove_host(base_vha->host);
 
+	qla84xx_put_chip(base_vha);
+
 	base_vha->flags.init_done = 0;
 	qla25xx_delete_queues(base_vha);
 	qla2x00_free_fcports(base_vha);
@@ -7616,6 +7802,7 @@ qla2x00_timer(struct timer_list *t)
 #define FW_ISP8031	9
 #define FW_ISP27XX	10
 #define FW_ISP28XX	11
+#define FW_ISP29XX	12
 
 #define FW_FILE_ISP21XX	"ql2100_fw.bin"
 #define FW_FILE_ISP22XX	"ql2200_fw.bin"
@@ -7629,6 +7816,7 @@ qla2x00_timer(struct timer_list *t)
 #define FW_FILE_ISP8031	"ql8300_fw.bin"
 #define FW_FILE_ISP27XX	"ql2700_fw.bin"
 #define FW_FILE_ISP28XX	"ql2800_fw.bin"
+#define FW_FILE_ISP29XX	"ql2900_fw.bin"
 
 
 static DEFINE_MUTEX(qla_fw_lock);
@@ -7646,6 +7834,7 @@ static struct fw_blob qla_fw_blobs[] = {
 	{ .name = FW_FILE_ISP8031, },
 	{ .name = FW_FILE_ISP27XX, },
 	{ .name = FW_FILE_ISP28XX, },
+	{ .name = FW_FILE_ISP29XX, },
 	{ .name = NULL, },
 };
 
@@ -7679,6 +7868,8 @@ qla2x00_request_firmware(scsi_qla_host_t *vha)
 		blob = &qla_fw_blobs[FW_ISP27XX];
 	} else if (IS_QLA28XX(ha)) {
 		blob = &qla_fw_blobs[FW_ISP28XX];
+	} else if (IS_QLA29XX(ha)) {
+		blob = &qla_fw_blobs[FW_ISP29XX];
 	} else {
 		return NULL;
 	}
@@ -8155,6 +8346,10 @@ static const struct pci_device_id qla2xxx_pci_tbl[] = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2281) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2089) },
 	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2289) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2099) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2299) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2091) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2291) },
 	{ 0 },
 };
 MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl);
@@ -8190,17 +8385,21 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(ms_iocb_entry_t) != 64);
 	BUILD_BUG_ON(sizeof(request_t) != 64);
 	BUILD_BUG_ON(sizeof(struct abort_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct abort_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct abort_iocb_entry_fx00) != 64);
 	BUILD_BUG_ON(sizeof(struct abts_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct abts_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct access_chip_84xx) != 64);
 	BUILD_BUG_ON(sizeof(struct access_chip_rsp_84xx) != 64);
 	BUILD_BUG_ON(sizeof(struct cmd_bidir) != 64);
 	BUILD_BUG_ON(sizeof(struct cmd_nvme) != 64);
+	BUILD_BUG_ON(sizeof(struct cmd_nvme_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct cmd_type_6) != 64);
 	BUILD_BUG_ON(sizeof(struct cmd_type_7) != 64);
 	BUILD_BUG_ON(sizeof(struct cmd_type_7_fx00) != 64);
 	BUILD_BUG_ON(sizeof(struct cmd_type_crc_2) != 64);
 	BUILD_BUG_ON(sizeof(struct ct_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct ct_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct ct_fdmi1_hba_attributes) != 2604);
 	BUILD_BUG_ON(sizeof(struct ct_fdmi2_hba_attributes) != 4424);
 	BUILD_BUG_ON(sizeof(struct ct_fdmi2_port_attributes) != 4164);
@@ -8214,20 +8413,26 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct device_reg_82xx) != 1288);
 	BUILD_BUG_ON(sizeof(struct device_reg_fx00) != 216);
 	BUILD_BUG_ON(sizeof(struct els_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct els_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct els_sts_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct els_sts_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct fxdisc_entry_fx00) != 64);
 	BUILD_BUG_ON(sizeof(struct imm_ntfy_from_isp) != 64);
 	BUILD_BUG_ON(sizeof(struct init_cb_24xx) != 128);
 	BUILD_BUG_ON(sizeof(struct init_cb_81xx) != 128);
 	BUILD_BUG_ON(sizeof(struct logio_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct logio_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct mbx_entry) != 64);
 	BUILD_BUG_ON(sizeof(struct mid_init_cb_24xx) != 5252);
 	BUILD_BUG_ON(sizeof(struct mrk_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct mrk_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct nvram_24xx) != 512);
 	BUILD_BUG_ON(sizeof(struct nvram_81xx) != 512);
 	BUILD_BUG_ON(sizeof(struct pt_ls4_request) != 64);
+	BUILD_BUG_ON(sizeof(struct pt_ls4_request_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct pt_ls4_rx_unsol) != 64);
 	BUILD_BUG_ON(sizeof(struct purex_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct purex_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct qla2100_fw_dump) != 123634);
 	BUILD_BUG_ON(sizeof(struct qla2300_fw_dump) != 136100);
 	BUILD_BUG_ON(sizeof(struct qla24xx_fw_dump) != 37976);
@@ -8251,17 +8456,23 @@ qla2x00_module_init(void)
 	BUILD_BUG_ON(sizeof(struct rdp_rsp_payload) != 336);
 	BUILD_BUG_ON(sizeof(struct sns_cmd_pkt) != 2064);
 	BUILD_BUG_ON(sizeof(struct sts_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct sts_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct tsk_mgmt_entry) != 64);
+	BUILD_BUG_ON(sizeof(struct tsk_mgmt_entry_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct tsk_mgmt_entry_fx00) != 64);
 	BUILD_BUG_ON(sizeof(struct verify_chip_entry_84xx) != 64);
 	BUILD_BUG_ON(sizeof(struct verify_chip_rsp_84xx) != 52);
 	BUILD_BUG_ON(sizeof(struct vf_evfp_entry_24xx) != 56);
 	BUILD_BUG_ON(sizeof(struct vp_config_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct vp_config_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(struct vp_rpt_id_entry_24xx) != 64);
+	BUILD_BUG_ON(sizeof(struct vp_rpt_id_entry_24xx_ext) != 128);
 	BUILD_BUG_ON(sizeof(sts21_entry_t) != 64);
 	BUILD_BUG_ON(sizeof(sts22_entry_t) != 64);
 	BUILD_BUG_ON(sizeof(sts_cont_entry_t) != 64);
+	BUILD_BUG_ON(sizeof(struct sts_cont_entry_ext) != 128);
 	BUILD_BUG_ON(sizeof(sts_entry_t) != 64);
 	BUILD_BUG_ON(sizeof(sw_info_t) != 32);
 	BUILD_BUG_ON(sizeof(target_id_t) != 2);
@@ -8381,3 +8592,4 @@ MODULE_FIRMWARE(FW_FILE_ISP2300);
 MODULE_FIRMWARE(FW_FILE_ISP2322);
 MODULE_FIRMWARE(FW_FILE_ISP24XX);
 MODULE_FIRMWARE(FW_FILE_ISP25XX);
+MODULE_FIRMWARE(FW_FILE_ISP29XX);
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index b6c36a8a2d60..56ef2b4c7c26 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -10,6 +10,655 @@
 #include <linux/vmalloc.h>
 #include <linux/uaccess.h>
 
+/**
+ * qla29xx_get_flt_layout - Retrieve the flash layout table (FLT) for QLA29xx.
+ * @vha: Pointer to SCSI QLogic host structure.
+ *
+ * This function reads and validates the FLT structure from the flash memory.
+ * It extracts region information and updates the hardware data structure.
+ */
+static void
+qla29xx_get_flt_layout(scsi_qla_host_t *vha)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_flash_layout *flt_layout = ha->flt_data;
+	struct qla_flt_region_header *flt_header = &flt_layout->flt_header;
+	struct qla_flt_region_data *region = &flt_layout->region[0];
+	uint32_t flt_options = 0;
+	uint32_t flt_size;
+	uint32_t cnt, chksum;
+	uint16_t reg_cnt, i;
+	__le32 *wptr;
+	void *buf = NULL;
+
+	flt_size = sizeof(struct qla_flt_region_header);
+	wptr = (__force __le32 *)ha->flt_data;
+
+	buf = qla29xx_read_optrom_data(vha, FLT_REG_MINI_FLT, flt_options,
+				       ha->flt_data, 0, flt_size);
+	if (!buf) {
+		ql_log(ql_log_warn, vha, 0x007b,
+		    "Failed to read FLT information.\n");
+		goto exit_flt;
+	}
+
+	ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x0111,
+	       "Contents of Flash Layout (0x%x):\n", flt_size);
+	ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0112,
+		       ha->flt_data, flt_size);
+
+	/* check flt version checksum and other info */
+	if ((le32_to_cpu(*wptr) == 0xffffffff) ||
+	    flt_header->version != cpu_to_le32(FLT_HDR_VERSION)) {
+		ql_log(ql_log_warn, vha, 0x007c,
+		    "Unsupported FLT detected: version=0x%x length=0x%x signature=0x%x region_count=0x%x checksum=0x%x.\n",
+		    le32_to_cpu(flt_header->version),
+		    le32_to_cpu(flt_header->length),
+		    le32_to_cpu(flt_header->signature),
+		    le16_to_cpu(flt_header->region_count),
+		    le32_to_cpu(flt_header->checksum));
+		goto exit_flt;
+	}
+
+	reg_cnt = le16_to_cpu(flt_header->region_count);
+	if (reg_cnt > FLT_MAX_REGIONS)
+		goto exit_flt;
+
+	if (le16_to_cpu(flt_header->region_size) !=
+	    sizeof(struct qla_flt_region_data)) {
+		ql_log(ql_log_warn, vha, 0x0082,
+		    "Unsupported FLT region size 0x%x (expected 0x%zx).\n",
+		    le16_to_cpu(flt_header->region_size),
+		    sizeof(struct qla_flt_region_data));
+		goto exit_flt;
+	}
+
+	flt_size = sizeof(struct qla_flash_layout) +
+		   (reg_cnt * le16_to_cpu(flt_header->region_size));
+	if (flt_size > sizeof(struct qla_flash_layout) +
+	    sizeof(struct qla_flt_region_data) * FLT_DATA_MAX_REGIONS) {
+		ql_log(ql_log_warn, vha, 0x007d,
+		    "FLT size 0x%x exceeds buffer, region_size=0x%x.\n",
+		    flt_size, le16_to_cpu(flt_header->region_size));
+		goto exit_flt;
+	}
+
+	buf = qla29xx_read_optrom_data(vha, FLT_REG_MINI_FLT, flt_options,
+				       ha->flt_data, 0, flt_size);
+	if (!buf) {
+		ql_log(ql_log_warn, vha, 0x007b,
+		    "Failed to read FLT information.\n");
+		goto exit_flt;
+	}
+
+	cnt = le32_to_cpu(flt_header->length) / sizeof(*wptr);
+	if (cnt > flt_size / sizeof(*wptr)) {
+		ql_log(ql_log_warn, vha, 0x007e,
+		    "FLT length 0x%x exceeds read size 0x%x, rejecting.\n",
+		    le32_to_cpu(flt_header->length), flt_size);
+		goto exit_flt;
+	}
+	for (chksum = 0; cnt--; wptr++)
+		chksum += le32_to_cpu(*wptr);
+	if (chksum) {
+		ql_log(ql_log_fatal, vha, 0x007f,
+		    "Inconsistent FLT detected: version=0x%x length=0x%x signature=0x%x checksum=0x%x.\n",
+		    le32_to_cpu(flt_header->version),
+		    le32_to_cpu(flt_header->length),
+		    le32_to_cpu(flt_header->signature),
+		    le32_to_cpu(flt_header->checksum));
+		goto exit_flt;
+	}
+
+	ql_dbg(ql_dbg_init, vha, 0x007f,
+		"FLT detected: version=0x%08x length=0x%08x signature=0x%08x region_count=0x%04x region_len=0x%04x segment_len=0x%08x checksum=0x%08x.\n",
+		le32_to_cpu(flt_header->version),
+		le32_to_cpu(flt_header->length),
+		le32_to_cpu(flt_header->signature),
+		le16_to_cpu(flt_header->region_count),
+		le16_to_cpu(flt_header->region_size),
+		le32_to_cpu(flt_header->segment_size),
+		le32_to_cpu(flt_header->checksum));
+
+	for (i = 0; reg_cnt; i++, reg_cnt--) {
+		region = &flt_layout->region[i];
+		ql_dbg(ql_dbg_init, vha, 0x0080,
+		    "FLT[%03x]: len=0x%08x version=0x%08x attr=0x%02x.\n",
+		    le16_to_cpu(region->region_code),
+		    le32_to_cpu(region->image_length),
+		    le32_to_cpu(region->version),
+		    le32_to_cpu(region->attribute));
+		if (le16_to_cpu(region->region_code) == FLT_REG_FW_DUMP_TMPLT) {
+			ql_dbg(ql_dbg_init, vha, 0x0080,
+				"%s: %d: Found FW dump template", __func__, __LINE__);
+			ha->fw_dump_tmplt_len = le32_to_cpu(region->image_length);
+		}
+	}
+
+	if (le32_to_cpu(flt_header->segment_size) >= sizeof(uint32_t)) {
+		ha->flt_segment_length = le32_to_cpu(flt_header->segment_size);
+	} else {
+		ql_log(ql_log_warn, vha, 0x0081,
+		    "FLT segment size 0x%x too small, using default 0x%x.\n",
+		    le32_to_cpu(flt_header->segment_size), QLA_SEGMENT_LENGTH);
+		ha->flt_segment_length = QLA_SEGMENT_LENGTH;
+	}
+	ha->flags.valid_flt = true;
+	return;
+
+exit_flt:
+	ha->flt_segment_length = QLA_SEGMENT_LENGTH;
+}
+
+/**
+ * qla29xx_get_fdt_info - Retrieve flash descriptor table (FDT) information.
+ * @vha: Pointer to SCSI QLogic host structure.
+ *
+ * This function reads and validates the FDT structure from the flash memory.
+ * It extracts manufacturer and device-specific information and updates
+ * the hardware data structure.
+ */
+static void
+qla29xx_get_fdt_info(scsi_qla_host_t *vha)
+{
+#define FLASH_BLK_SIZE_4K	0x1000
+#define FLASH_BLK_SIZE_32K	0x8000
+#define FLASH_BLK_SIZE_64K	0x10000
+	struct qla_hw_data *ha = vha->hw;
+	struct req_que *req = ha->req_q_map[0];
+	uint16_t cnt, chksum;
+	__le16 *wptr = (__force __le16 *)req->ring;
+	struct qla_fdt_layout *fdt = (struct qla_fdt_layout *)req->ring;
+	uint32_t fdt_options = 0;
+	void *buf = NULL;
+
+	buf = qla29xx_read_optrom_data(vha, FLT_REG_FDT, fdt_options,
+					fdt, 0, sizeof(*fdt));
+	if (!buf) {
+		ql_log(ql_log_warn, vha, 0x0047,
+		    "Failed to read FLT information.\n");
+		return;
+	}
+
+	if (le16_to_cpu(*wptr) == 0xffff)
+		return;
+	if (memcmp(fdt->sig, "QLID", 4))
+		return;
+
+	for (cnt = 0, chksum = 0; cnt < sizeof(*fdt) >> 1; cnt++, wptr++)
+		chksum += le16_to_cpu(*wptr);
+	if (chksum) {
+		ql_dbg(ql_dbg_init, vha, 0x004c,
+			"Inconsistent FDT detected: checksum=0x%x id=%c version0x%x.\n",
+			chksum, fdt->sig[0], le16_to_cpu(fdt->version));
+		ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0113,
+		    fdt, sizeof(*fdt));
+		return;
+	}
+}
+
+/**
+ * qla29xx_get_flash_region - Retrieve flash region information for QLA29xx adapters.
+ * @vha: Pointer to SCSI QLogic host structure.
+ * @code: Region code to identify the flash region.
+ * @region: Pointer to store the retrieved flash region information.
+ *
+ * This function retrieves the flash region information for QLA29xx adapters
+ * based on the specified region code.
+ *
+ * Returns QLA_SUCCESS on success or QLA_FUNCTION_FAILED on failure.
+ */
+static int
+qla29xx_get_flash_region(struct scsi_qla_host *vha, uint32_t code,
+			 struct qla_flt_region_data *region)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_flash_layout *flt = ha->flt_data;
+	struct qla_flt_region_header *flt_hdr = &flt->flt_header;
+	struct qla_flt_region_data *flt_reg = &flt->region[0];
+	uint16_t cnt;
+	int rval = QLA_FUNCTION_FAILED;
+
+	if (!ha->flt_data || !ha->flags.valid_flt)
+		return QLA_FUNCTION_FAILED;
+
+	cnt = le16_to_cpu(flt_hdr->region_count);
+	if (cnt > FLT_MAX_REGIONS)
+		return QLA_FUNCTION_FAILED;
+	/*
+	 * flt_reg++ strides by sizeof(struct qla_flt_region_data). This is
+	 * safe because valid_flt is set only after qla29xx_get_flt_layout()
+	 * has verified the firmware region_size equals that struct size.
+	 */
+	for (; cnt; cnt--, flt_reg++) {
+		if (le16_to_cpu(flt_reg->region_code) == code) {
+			memcpy((uint8_t *)region, flt_reg,
+			    sizeof(struct qla_flt_region_data));
+			rval = QLA_SUCCESS;
+			break;
+		}
+	}
+
+	return rval;
+}
+
+/**
+ * set_segment_bits - Set segment-related bits in the options field.
+ * @options: Pointer to the options field.
+ * @segment_index: Index of the current segment.
+ * @total: Total number of segments.
+ *
+ * This function sets the appropriate bits in the options field to indicate
+ * whether the current segment is the first, last, or a single segment.
+ */
+static void set_segment_bits(uint16_t *options, int segment_index, int total)
+{
+	/* - Single segment complete image.
+	 * - 1st Segment of an image.
+	 * - Last segment of an image.
+	 */
+	if (total == 1)
+		*options |= (1 << 10) | (1 << 11);
+	else if (segment_index == 0)
+		*options |= (1 << 10);
+	else if (segment_index == total - 1)
+		*options |= (1 << 11);
+}
+
+/**
+ * set_chunk_bits - Set chunk-related bits in the options field.
+ * @options: Pointer to the options field.
+ * @count: Index of the current chunk.
+ * @total: Total number of chunks.
+ *
+ * This function sets the appropriate bits in the options field to indicate
+ * whether the current chunk is the first, last, or a single chunk.
+ */
+static void set_chunk_bits(uint16_t *options, int count, int total)
+{
+	/* - Single chunk complete segment
+	 * - 1st chunk of a segment
+	 * - Last chunk of a segment
+	 */
+	if (total == 1)
+		*options |= (1 << 4) | (1 << 5);
+	else if (count == 0)
+		*options |= (1 << 4);
+	else if (count == total - 1)
+		*options |= (1 << 5);
+}
+
+/**
+ * qla29xx_write_optrom_data - Write data to the optrom for QLA29xx adapters.
+ * @vha: Pointer to SCSI QLogic host structure.
+ * @reg_code: Region code to write to.
+ * @opts: Options for the write operation.
+ * @buf: Buffer containing the data to write.
+ * @offset: Offset within the region to start writing.
+ * @length: Length of data to write.
+ *
+ * This function writes data to the specified optrom region for QLA29xx adapters.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int
+qla29xx_write_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code,
+			 uint16_t opts, void *buf, uint32_t offset,
+			 uint32_t length)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_flt_region_data region;
+	dma_addr_t optrom_dma;
+	uint32_t faddr, left, burst;
+	uint32_t img_len, seg_dlen;
+	uint32_t region_len, region_dlen;
+	void *optrom;
+	uint8_t *pbuf;
+	int rval = -EINVAL;
+	uint16_t total_segments, segment_index = 0;
+	uint16_t chunk_index = 0;
+
+	memset(&region, 0, sizeof(region));
+
+	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+				    &optrom_dma, GFP_KERNEL);
+	if (!optrom) {
+		ql_log(ql_log_warn, vha, 0x0090,
+		    "Unable to allocate memory for optrom burst read (%x KB).\n",
+		    OPTROM_BURST_SIZE / 1024);
+		return -ENOMEM;
+	}
+
+	if (ha->flags.valid_flt && length == 0) {
+		/* Get image length and segment length from FLT */
+		rval = qla29xx_get_flash_region(vha, reg_code, &region);
+		if (rval != QLA_SUCCESS) {
+			ql_log(ql_log_warn, vha, 0x0092,
+				"Invalid address %x - not a region start address\n",
+				reg_code);
+			goto free_buf;
+		}
+		img_len = le32_to_cpu(region.image_length);
+	} else {
+		img_len = length;
+	}
+
+	seg_dlen = ha->flt_segment_length >> 2;
+	region_len = (length > 0) ? length : img_len;
+	region_dlen = (region_len >> 2);
+	total_segments = (region_dlen + seg_dlen - 1) / seg_dlen;
+
+	faddr = offset >> 2;
+	left = region_dlen;
+	burst = OPTROM_BURST_DWORDS;
+	pbuf = buf;
+
+	while (region_dlen > 0) {
+		uint32_t segment_size, total_chunks;
+		uint16_t options = 0;
+
+		segment_size = (region_dlen > seg_dlen) ? seg_dlen : region_dlen;
+		total_chunks = (segment_size + OPTROM_BURST_DWORDS - 1) /
+				OPTROM_BURST_DWORDS;
+
+		burst = OPTROM_BURST_DWORDS;
+		if (burst > left)
+			burst = left;
+		if (burst > segment_size - chunk_index * OPTROM_BURST_DWORDS)
+			burst = segment_size - chunk_index * OPTROM_BURST_DWORDS;
+
+		set_segment_bits(&options, segment_index, total_segments);
+		set_chunk_bits(&options, chunk_index, total_chunks);
+
+		/* flash block write operations */
+		SET_FW_BIT(options, BIT_6);
+		CLEAR_FW_BIT(options, BIT_9);
+
+		check_and_set_mbc_bits(opts, options, BIT_15, BIT_15);
+		check_and_set_mbc_bits(opts, options, BIT_7, BIT_7);
+
+		if (segment_index == total_segments - 1 &&
+		    chunk_index == total_chunks - 1)
+			check_and_set_mbc_bits(opts, options, BIT_8, BIT_8);
+
+		memcpy(optrom, pbuf, burst * 4);
+
+		/* faddr is offset relative to region code */
+		rval = qla29xx_flash_block_write(vha, optrom_dma,
+				faddr, burst, reg_code, options);
+		if (rval) {
+			ql_log(ql_log_warn, vha, 0x0095,
+			    "Unable to burst-write optrom segment (%x/%x/%llx).\n",
+			    rval, faddr, (unsigned long long)optrom_dma);
+
+			dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+			    optrom, optrom_dma);
+			goto exit_write;
+		}
+
+		left -= burst;
+		faddr += burst;
+		pbuf += burst * 4;
+		chunk_index++;
+		if (chunk_index >= total_chunks) {
+			chunk_index = 0;
+			segment_index++;
+			region_dlen -= segment_size;
+		}
+	}
+
+free_buf:
+	dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
+	    optrom_dma);
+	return rval;
+
+exit_write:
+	return rval;
+}
+
+/**
+ * qla29xx_read_optrom_data - Read data from the optrom for QLA29xx adapters.
+ * @vha: Pointer to SCSI QLogic host structure.
+ * @reg_code: Region code to read from.
+ * @opts: Options for the read operation.
+ * @buf: Buffer to store the read data.
+ * @offset: Offset within the region to start reading.
+ * @length: Length of data to read.
+ *
+ * This function reads data from the specified optrom region for QLA29xx adapters.
+ *
+ * Returns a pointer to the buffer on success or NULL on failure.
+ */
+void *
+qla29xx_read_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code,
+			 uint16_t opts, void *buf, uint32_t offset,
+			 uint32_t length)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_flt_region_data region;
+	dma_addr_t optrom_dma;
+	uint32_t faddr, left, burst;
+	uint32_t img_len, seg_dlen;
+	uint32_t region_len, region_dlen;
+	void *optrom;
+	uint8_t *pbuf;
+	uint16_t total_segments, segment_index = 0;
+	uint16_t chunk_index = 0;
+	int rval;
+
+	memset(&region, 0, sizeof(region));
+
+	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+				    &optrom_dma, GFP_KERNEL);
+	if (!optrom) {
+		ql_log(ql_log_warn, vha, 0x0093,
+		    "Unable to allocate memory for optrom burst read (%x KB).\n",
+		    OPTROM_BURST_SIZE / 1024);
+		return NULL;
+	}
+
+	if (ha->flags.valid_flt && length == 0) {
+		/* Get image length and segment length from FLT */
+		rval = qla29xx_get_flash_region(vha, reg_code, &region);
+		if (rval != QLA_SUCCESS) {
+			ql_log(ql_log_warn, vha, 0x7033,
+				"Invalid address %x - not a region start address\n",
+				reg_code);
+			goto free_buf;
+		}
+		img_len = le32_to_cpu(region.image_length);
+	} else {
+		img_len = length;
+	}
+
+	seg_dlen = ha->flt_segment_length >> 2;
+	region_len = (length > 0) ? length : img_len;
+	region_dlen = (region_len >> 2);
+	total_segments = (region_dlen + seg_dlen - 1) / seg_dlen;
+
+	faddr = offset >> 2;
+	left = region_dlen;
+	burst = OPTROM_BURST_DWORDS;
+	pbuf = buf;
+
+	ql_log(ql_log_info, vha, 0x0096,
+	       "Reg[0x%x]: options=0x%x length=0x%x offset=0x%x segments=%u\n",
+		reg_code, opts, region_len, offset, total_segments);
+
+	while (region_dlen > 0) {
+		uint32_t segment_size, total_chunks;
+		uint16_t options = 0;
+
+		segment_size = (region_dlen > seg_dlen) ? seg_dlen : region_dlen;
+		total_chunks = (segment_size + OPTROM_BURST_DWORDS - 1) /
+				OPTROM_BURST_DWORDS;
+
+		burst = OPTROM_BURST_DWORDS;
+		if (burst > left)
+			burst = left;
+		if (burst > segment_size - chunk_index * OPTROM_BURST_DWORDS)
+			burst = segment_size - chunk_index * OPTROM_BURST_DWORDS;
+
+		set_segment_bits(&options, segment_index, total_segments);
+		set_chunk_bits(&options, chunk_index, total_chunks);
+
+		/* flash block read operations */
+		CLEAR_FW_BIT(options, BIT_9);
+		CLEAR_FW_BIT(options, BIT_6);
+
+		options |= opts;
+
+		/* faddr is offset relative to region code */
+		rval = qla29xx_flash_block_read(vha, optrom_dma,
+				faddr, burst, reg_code, options);
+		if (rval) {
+			ql_log(ql_log_warn, vha, 0x0097,
+			    "Unable to burst-read optrom segment (%x/%x/%llx).\n",
+			    rval, faddr, (unsigned long long)optrom_dma);
+			goto free_buf;
+		}
+
+		memcpy(pbuf, optrom, burst * 4);
+
+		left -= burst;
+		faddr += burst;
+		pbuf += burst * 4;
+		chunk_index++;
+		if (chunk_index >= total_chunks) {
+			chunk_index = 0;
+			segment_index++;
+			region_dlen -= segment_size;
+		}
+	}
+
+	dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
+	    optrom_dma);
+	return buf;
+
+free_buf:
+	dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
+	    optrom_dma);
+	return NULL;
+}
+
+static void set_chunk_mpi_bits(uint16_t *options, int count, int total)
+{
+	/* - Single chunk complete segment/image
+	 * - 1st chunk of a segment/image
+	 * - Last chunk of a segment/image
+	 */
+	if (total == 1)
+		*options |= BIT_2 | BIT_1;
+	else if (count == 0)
+		*options |= BIT_1;
+	else if (count == total - 1)
+		*options |= BIT_2;
+}
+
+/**
+ * qla29xx_mpi_optrom_data - Dump/load MPI optrom data for 29xx.
+ * @vha: Pointer to SCSI QLogic host structure.
+ * @opts: Options for the operation.
+ * @buf: Buffer to read from/write to.
+ * @offset: MPI RAM address, in 32-bit words (MBC_LOAD_DUMP_MPI_RAM is
+ *	    word-addressed; not a byte offset).
+ * @length: Length of data, in bytes (converted internally to a word count).
+ * @op: Operation, either QLA29XX_MPI_OP_DUMP or QLA29XX_MPI_OP_LOAD.
+ *
+ * Returns:
+ * QLA_SUCCESS on success or an error code on failure.
+ */
+int
+qla29xx_mpi_optrom_data(struct scsi_qla_host *vha, uint16_t opts, void *buf,
+		       uint32_t offset, uint32_t length, enum qla29xx_mpi_optrom_op op)
+{
+	struct qla_hw_data *ha = vha->hw;
+	dma_addr_t optrom_dma;
+	void *optrom;
+	uint32_t mpi_addr, mpi_size, burst = 0;
+	uint32_t total_chunks = 0;
+	uint32_t *dcode, *fwcode;
+	uint32_t chunk_count = 0;
+	int rval = -EINVAL;
+
+	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+				    &optrom_dma, GFP_KERNEL);
+	if (!optrom) {
+		ql_log(ql_log_warn, vha, 0x0090,
+			"Unable to allocate memory for optrom burst read (%x KB).\n",
+			OPTROM_BURST_SIZE / 1024);
+		rval = -ENOMEM;
+		goto exit_mpi_op;
+	}
+
+	mpi_addr = offset;
+	dcode = (uint32_t *)optrom;
+	memset(dcode, 0, OPTROM_BURST_SIZE);
+
+	fwcode = (uint32_t *)buf;
+	mpi_size = length >> 2;
+	burst = OPTROM_BURST_SIZE >> 2;
+	total_chunks = (mpi_size + burst - 1) / burst;
+
+	while (mpi_size > 0) {
+		uint16_t options = 0;
+
+		if (burst > mpi_size)
+			burst = mpi_size;
+
+		set_chunk_mpi_bits(&options, chunk_count, total_chunks);
+
+		if (op == QLA29XX_MPI_OP_DUMP) {
+			options |= (BIT_0);
+			options |= opts;
+
+			ql_log(ql_log_info, vha, 0x008b,
+				"-> %s (DUMP): %#x <-(%#x words) (0x%x options) chunk (0x%x, 0x%x)\n",
+				__func__, mpi_addr, burst, options, chunk_count,
+				total_chunks);
+
+			memcpy(dcode, fwcode, burst * 4);
+
+			rval = qla29xx_load_dump_mpi(vha, options, mpi_addr, burst,
+						     optrom_dma);
+			if (rval) {
+				ql_log(ql_log_fatal, vha, 0x0098,
+					"Failed dump mpi firmware.\n");
+				goto free_buf;
+			}
+		} else if (op == QLA29XX_MPI_OP_LOAD) {
+			options |= opts;
+
+			ql_log(ql_log_info, vha, 0x008b,
+				"-> %s (LOAD): %#x <-(%#x words) (0x%x options) chunk (0x%x, 0x%x)\n",
+				__func__, mpi_addr, burst, options, chunk_count,
+				total_chunks);
+
+			rval = qla29xx_load_dump_mpi(vha, options, mpi_addr, burst,
+						     optrom_dma);
+			if (rval) {
+				ql_log(ql_log_fatal, vha, 0x0098,
+					"Failed load mpi firmware.\n");
+				goto free_buf;
+			}
+			memcpy(fwcode, dcode, burst * 4);
+		}
+
+		chunk_count++;
+		fwcode += burst;
+		mpi_addr += burst;
+		mpi_size -= burst;
+	}
+
+	rval = QLA_SUCCESS;
+free_buf:
+	dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
+			  optrom_dma);
+
+exit_mpi_op:
+	return rval;
+}
+
 /*
  * NVRAM support routines
  */
@@ -1117,10 +1766,18 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha)
 	uint32_t flt_addr;
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
-	    !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha) &&
-	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
-		return QLA_SUCCESS;
+	if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_CNA_CAPABLE(ha) &&
+	    !IS_QLA2031(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) &&
+	    !IS_QLA29XX(ha))
+		goto done;
+
+	if (IS_QLA29XX(ha)) {
+		mutex_lock(&ha->optrom_mutex);
+		qla29xx_get_flt_layout(vha);
+		qla29xx_get_fdt_info(vha);
+		mutex_unlock(&ha->optrom_mutex);
+		goto done;
+	}
 
 	if (IS_QLA28XX(ha) && !qla28xx_validate_mcu_signature(vha))
 		ha->flags.secure_mcu = 1;
@@ -1132,7 +1789,7 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha)
 	qla2xxx_get_flt_info(vha, flt_addr);
 	qla2xxx_get_fdt_info(vha);
 	qla2xxx_get_idc_param(vha);
-
+done:
 	return QLA_SUCCESS;
 }
 
@@ -1374,7 +2031,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, __le32 *dwptr, uint32_t faddr,
 			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
 			    "Write burst (%#lx dwords)...\n", dburst);
 			ret = qla2x00_load_ram(vha, optrom_dma,
-			    flash_data_addr(ha, faddr), dburst);
+			    flash_data_addr(ha, faddr), dburst, 0);
 			if (!ret) {
 				liter += dburst - 1;
 				faddr += dburst - 1;
@@ -2974,7 +3631,7 @@ qla28xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
 		ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
 		    "Write burst (%#lx dwords)...\n", dburst);
 		rval = qla2x00_load_ram(vha, optrom_dma,
-		    flash_data_addr(ha, faddr), dburst);
+		    flash_data_addr(ha, faddr), dburst, 0);
 		if (rval != QLA_SUCCESS) {
 			ql_log(ql_log_warn, vha, 0x7097,
 			    "Failed burst write at %x (%p/%#llx)...\n",
@@ -3448,6 +4105,86 @@ qla82xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 	return ret;
 }
 
+/*
+ * Read the boot BIOS/FCODE/EFI version for 29xx adapters.
+ *
+ * 29xx reads flash through an FLT region code plus a region-relative
+ * offset.  The firmware only honours such a read at the region base
+ * (offset 0) and rejects a non-zero offset with MBS_COMMAND_ERROR.  The
+ * PCI expansion ROM header and the PCI data structure it points to both
+ * live within the first dwords of FLT_REG_BOOT_CODE, so read one bounded
+ * block at offset 0 and parse both structures from memory instead of
+ * issuing a second read at the PCI-data-structure offset.
+ */
+#define QLA29XX_BOOT_PROBE_LEN	0x100
+
+static void
+qla29xx_get_boot_version(scsi_qla_host_t *vha, void *mbuf)
+{
+	struct qla_hw_data *ha = vha->hw;
+	uint8_t *bcode = mbuf;
+	uint32_t pcids;
+	uint8_t code_type;
+
+	if (!qla29xx_read_optrom_data(vha, FLT_REG_BOOT_CODE, 0, mbuf, 0,
+				      QLA29XX_BOOT_PROBE_LEN)) {
+		ql_log(ql_log_info, vha, 0x018e,
+		    "Unable to read PCI Data Structure.\n");
+		return;
+	}
+
+	/* Verify PCI expansion ROM header. */
+	if (memcmp(bcode, "\x55\xaa", 2)) {
+		ql_log(ql_log_info, vha, 0x0059,
+		    "No matching ROM signature.\n");
+		return;
+	}
+
+	/* Locate the PCI data structure within the buffer. */
+	pcids = (bcode[0x19] << 8) | bcode[0x18];
+	if (pcids + 0x16 > QLA29XX_BOOT_PROBE_LEN)
+		return;
+
+	bcode += pcids;
+
+	/* Validate signature of PCI data structure. */
+	if (memcmp(bcode, "PCIR", 4)) {
+		ql_log(ql_log_info, vha, 0x005a,
+		    "PCI data struct not found pcir_adr=%x.\n", pcids);
+		return;
+	}
+
+	code_type = bcode[0x14];
+	switch (code_type) {
+	case ROM_CODE_TYPE_BIOS:
+		/* Intel x86, PC-AT compatible. */
+		ha->bios_revision[0] = bcode[0x12];
+		ha->bios_revision[1] = bcode[0x13];
+		ql_dbg(ql_dbg_init, vha, 0x005b, "Read BIOS %d.%d.\n",
+		    ha->bios_revision[1], ha->bios_revision[0]);
+		break;
+	case ROM_CODE_TYPE_FCODE:
+		/* Open Firmware standard for PCI (FCode). */
+		ha->fcode_revision[0] = bcode[0x12];
+		ha->fcode_revision[1] = bcode[0x13];
+		ql_dbg(ql_dbg_init, vha, 0x005c, "Read FCODE %d.%d.\n",
+		    ha->fcode_revision[1], ha->fcode_revision[0]);
+		break;
+	case ROM_CODE_TYPE_EFI:
+		/* Extensible Firmware Interface (EFI). */
+		ha->efi_revision[0] = bcode[0x12];
+		ha->efi_revision[1] = bcode[0x13];
+		ql_dbg(ql_dbg_init, vha, 0x005d, "Read EFI %d.%d.\n",
+		    ha->efi_revision[1], ha->efi_revision[0]);
+		break;
+	default:
+		ql_log(ql_log_warn, vha, 0x005e,
+		    "Unrecognized code type %x at pcids %x.\n",
+		    code_type, pcids);
+		break;
+	}
+}
+
 int
 qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 {
@@ -3472,12 +4209,31 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 	memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
 	memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
 
+	/* ISP29xx: get FW version from FLT region metadata */
+	if (IS_QLA29XX(ha)) {
+		struct qla_flt_region_data region;
+
+		ret = qla29xx_get_flash_region(vha, FLT_REG_FW, &region);
+		if (ret != QLA_SUCCESS) {
+			ql_log(ql_log_warn, vha, 0x7033,
+					"Invalid region %x\n", FLT_REG_FW);
+			return ret;
+		}
+
+		ha->fw_revision[0] = (le32_to_cpu(region.version) >> 16) & 0xff;
+		ha->fw_revision[1] = (le32_to_cpu(region.version) >> 8) & 0xff;
+		ha->fw_revision[2] = le32_to_cpu(region.version) & 0xff;
+
+		/* BIOS/FCODE/EFI version from the boot-code region. */
+		qla29xx_get_boot_version(vha, mbuf);
+		return ret;
+	}
+
 	pcihdr = ha->flt_region_boot << 2;
 	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
 		qla27xx_get_active_image(vha, &active_regions);
-		if (active_regions.global == QLA27XX_SECONDARY_IMAGE) {
+		if (active_regions.global == QLA27XX_SECONDARY_IMAGE)
 			pcihdr = ha->flt_region_boot_sec << 2;
-		}
 	}
 
 	do {
@@ -3486,7 +4242,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 		if (ret) {
 			ql_log(ql_log_info, vha, 0x017d,
 			    "Unable to read PCI EXP Rom Header(%x).\n", ret);
-			return QLA_FUNCTION_FAILED;
+			break;
 		}
 
 		bcode = mbuf + (pcihdr % 4);
@@ -3494,7 +4250,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 			/* No signature */
 			ql_log(ql_log_fatal, vha, 0x0059,
 			    "No matching ROM signature.\n");
-			return QLA_FUNCTION_FAILED;
+			break;
 		}
 
 		/* Locate PCI data structure. */
@@ -3504,7 +4260,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 		if (ret) {
 			ql_log(ql_log_info, vha, 0x018e,
 			    "Unable to read PCI Data Structure (%x).\n", ret);
-			return QLA_FUNCTION_FAILED;
+			break;
 		}
 
 		bcode = mbuf + (pcihdr % 4);
@@ -3515,7 +4271,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
 			ql_log(ql_log_fatal, vha, 0x005a,
 			    "PCI data struct not found pcir_adr=%x.\n", pcids);
 			ql_dump_buffer(ql_dbg_init, vha, 0x0059, dcode, 32);
-			return QLA_FUNCTION_FAILED;
+			break;
 		}
 
 		/* Read version */
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
index e47da45e93a0..53a505df8da0 100644
--- a/drivers/scsi/qla2xxx/qla_target.c
+++ b/drivers/scsi/qla2xxx/qla_target.c
@@ -1738,8 +1738,8 @@ static int qlt_build_abts_resp_iocb(struct qla_tgt_mgmt_cmd *mcmd)
 		return -EAGAIN;
 	}
 
-	resp = (struct abts_resp_to_24xx *)qpair->req->ring_ptr;
-	memset(resp, 0, sizeof(*resp));
+	resp = (struct abts_resp_to_24xx *)qla_req_ring_slot(ha, qpair->req);
+	memset(resp, 0, qla_req_entry_size(ha));
 
 	h = qlt_make_handle(qpair);
 	if (unlikely(h == QLA_TGT_NULL_HANDLE)) {
@@ -2490,17 +2490,13 @@ static int qlt_check_reserve_free_req(struct qla_qpair *qpair,
 /*
  * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
  */
-static inline void *qlt_get_req_pkt(struct req_que *req)
+static inline void *qlt_get_req_pkt(struct qla_hw_data *ha,
+				     struct req_que *req)
 {
 	/* Adjust ring index. */
-	req->ring_index++;
-	if (req->ring_index == req->length) {
-		req->ring_index = 0;
-		req->ring_ptr = req->ring;
-	} else {
-		req->ring_ptr++;
-	}
-	return (cont_entry_t *)req->ring_ptr;
+	qla_req_ring_advance(ha, req);
+
+	return qla_req_ring_slot(ha, req);
 }
 
 /* ha->hardware_lock supposed to be held on entry */
@@ -2549,9 +2545,9 @@ static int qlt_24xx_build_ctio_pkt(struct qla_qpair *qpair,
 	uint16_t temp;
 	struct qla_tgt_cmd      *cmd = prm->cmd;
 
-	pkt = (struct ctio7_to_24xx *)qpair->req->ring_ptr;
+	pkt = (struct ctio7_to_24xx *)qla_req_ring_slot(qpair->hw, qpair->req);
 	prm->pkt = pkt;
-	memset(pkt, 0, sizeof(*pkt));
+	memset(pkt, 0, qla_req_entry_size(qpair->hw));
 
 	pkt->entry_type = CTIO_TYPE7;
 	pkt->entry_count = (uint8_t)prm->req_cnt;
@@ -2600,11 +2596,12 @@ static void qlt_load_cont_data_segments(struct qla_tgt_prm *prm)
 {
 	int cnt;
 	struct dsd64 *cur_dsd;
+	struct qla_hw_data *ha = prm->cmd->qpair->hw;
 
 	/* Build continuation packets */
 	while (prm->seg_cnt > 0) {
 		cont_a64_entry_t *cont_pkt64 =
-			(cont_a64_entry_t *)qlt_get_req_pkt(
+			(cont_a64_entry_t *)qlt_get_req_pkt(ha,
 			   prm->cmd->qpair->req);
 
 		/*
@@ -2614,7 +2611,7 @@ static void qlt_load_cont_data_segments(struct qla_tgt_prm *prm)
 		 * that.
 		 */
 
-		memset(cont_pkt64, 0, sizeof(*cont_pkt64));
+		memset(cont_pkt64, 0, qla_req_entry_size(ha));
 
 		cont_pkt64->entry_count = 1;
 		cont_pkt64->sys_define = 0;
@@ -3012,9 +3009,9 @@ qlt_build_ctio_crc2_pkt(struct qla_qpair *qpair, struct qla_tgt_prm *prm)
 
 	ha = vha->hw;
 
-	pkt = (struct ctio_crc2_to_fw *)qpair->req->ring_ptr;
+	pkt = (struct ctio_crc2_to_fw *)qla_req_ring_slot(ha, qpair->req);
 	prm->pkt = pkt;
-	memset(pkt, 0, sizeof(*pkt));
+	memset(pkt, 0, qla_req_entry_size(ha));
 
 	ql_dbg_qp(ql_dbg_tgt, cmd->qpair, 0xe071,
 		"qla_target(%d):%s: se_cmd[%p] CRC2 prot_op[0x%x] cmd prot sg:cnt[%p:%x] lba[%llu]\n",
@@ -3306,7 +3303,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
 			 */
 			struct ctio7_to_24xx *ctio =
 				(struct ctio7_to_24xx *)qlt_get_req_pkt(
-				    qpair->req);
+				    vha->hw, qpair->req);
 
 			ql_dbg_qp(ql_dbg_tgt, qpair, 0x305e,
 			    "Building additional status packet 0x%p.\n",
@@ -3316,6 +3313,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
 			 * T10Dif: ctio_crc2_to_fw overlay ontop of
 			 * ctio7_to_24xx
 			 */
+			memset(ctio, 0, qla_req_entry_size(vha->hw));
 			memcpy(ctio, pkt, sizeof(*ctio));
 			/* reset back to CTIO7 */
 			ctio->entry_count = 1;
diff --git a/drivers/scsi/qla2xxx/qla_tmpl.c b/drivers/scsi/qla2xxx/qla_tmpl.c
index b0a74b036cf4..fd3984127497 100644
--- a/drivers/scsi/qla2xxx/qla_tmpl.c
+++ b/drivers/scsi/qla2xxx/qla_tmpl.c
@@ -306,6 +306,12 @@ qla27xx_fwdt_entry_t262(struct scsi_qla_host *vha,
 		goto done;
 	}
 
+	if (vha->hw->flags.t262_fail) {
+		ql_dbg(ql_dbg_misc, vha, 0xd045,
+		    "%s: failed previously\n", __func__);
+		qla27xx_skip_entry(ent, buf);
+		goto done;
+	}
 	dwords = end - start + 1;
 	if (buf) {
 		buf += *len;
@@ -314,7 +320,12 @@ qla27xx_fwdt_entry_t262(struct scsi_qla_host *vha,
 			ql_dbg(ql_dbg_async, vha, 0xffff,
 			    "%s: dump ram MB failed. Area %xh start %lxh end %lxh\n",
 			    __func__, area, start, end);
-			return INVALID_ENTRY;
+
+			if (rc == QLA_FUNCTION_TIMEOUT)
+				vha->hw->flags.t262_fail = 1;
+
+			qla27xx_skip_entry(ent, buf);
+			goto done;
 		}
 	}
 	*len += dwords * sizeof(uint32_t);
@@ -536,13 +547,12 @@ qla27xx_fwdt_entry_t269(struct scsi_qla_host *vha,
 {
 	ql_dbg(ql_dbg_misc, vha, 0xd20d,
 	    "%s: scratch [%lx]\n", __func__, *len);
-	qla27xx_insert32(0xaaaaaaaa, buf, len);
-	qla27xx_insert32(0xbbbbbbbb, buf, len);
-	qla27xx_insert32(0xcccccccc, buf, len);
-	qla27xx_insert32(0xdddddddd, buf, len);
-	qla27xx_insert32(*len + sizeof(uint32_t), buf, len);
+
+	/* The data format is based on entry type t260. */
+	qla27xx_insert32(offsetof(struct device_reg_24xx, mailbox0), buf, len);
+	qla27xx_insertbuf(vha->hw->mbregs, sizeof(vha->hw->mbregs), buf, len);
 	if (buf)
-		ent->t269.scratch_size = 5 * sizeof(uint32_t);
+		ent->t269.scratch_size = sizeof(uint32_t) + sizeof(vha->hw->mbregs);
 
 	return qla27xx_next_entry(ent);
 }
@@ -589,17 +599,37 @@ qla27xx_fwdt_entry_t272(struct scsi_qla_host *vha,
 {
 	ulong dwords = le32_to_cpu(ent->t272.count);
 	ulong start = le32_to_cpu(ent->t272.addr);
+	int rc;
 
 	ql_dbg(ql_dbg_misc, vha, 0xd210,
 	    "%s: rdremram [%lx]\n", __func__, *len);
+
+	if (vha->hw->flags.t272_fail) {
+		ql_dbg(ql_dbg_misc, vha, 0xd04f,
+		    "%s: failed previously\n", __func__);
+		qla27xx_skip_entry(ent, buf);
+		goto done;
+	}
+
 	if (buf) {
 		ql_dbg(ql_dbg_misc, vha, 0xd02c,
 		    "%s: @%lx -> (%lx dwords)\n", __func__, start, dwords);
 		buf += *len;
-		qla27xx_dump_mpi_ram(vha->hw, start, buf, dwords, &buf);
+		rc = qla27xx_dump_mpi_ram(vha->hw, start, buf, dwords, &buf);
+		if (rc != QLA_SUCCESS) {
+			ql_log(ql_log_warn, vha, 0xd01b,
+			    "%s: dump mpi MB failed. Start %lxh dwords %lxh\n",
+			    __func__, start, dwords);
+
+			if (rc == QLA_FUNCTION_TIMEOUT)
+				vha->hw->flags.t272_fail = 1;
+
+			qla27xx_skip_entry(ent, buf);
+			goto done;
+		}
 	}
 	*len += dwords * sizeof(uint32_t);
-
+done:
 	return qla27xx_next_entry(ent);
 }
 
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index 9564beafdab7..ef9dc88221c9 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -6,9 +6,9 @@
 /*
  * Driver version
  */
-#define QLA2XXX_VERSION      "10.02.10.100-k"
+#define QLA2XXX_VERSION      "12.00.00.2607b2"
 
-#define QLA_DRIVER_MAJOR_VER	10
-#define QLA_DRIVER_MINOR_VER	02
-#define QLA_DRIVER_PATCH_VER	10
-#define QLA_DRIVER_BETA_VER	100
+#define QLA_DRIVER_MAJOR_VER	12
+#define QLA_DRIVER_MINOR_VER	00
+#define QLA_DRIVER_PATCH_VER	00
+#define QLA_DRIVER_BETA_VER	2607
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 22e2e3223440..686cef2406b3 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -1187,8 +1187,10 @@ blk_status_t scsi_alloc_sgtables(struct scsi_cmnd *cmd)
 	if (blk_rq_bytes(rq) & rq->q->limits.dma_pad_mask) {
 		unsigned int pad_len =
 			(rq->q->limits.dma_pad_mask & ~blk_rq_bytes(rq)) + 1;
+		unsigned int data_len = last_sg->length;
 
 		last_sg->length += pad_len;
+		sg_zero_buffer(last_sg, 1, pad_len, data_len);
 		cmd->extra_len += pad_len;
 	}
 
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 599e75f33334..a1b21ea14e54 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -958,6 +958,21 @@ static unsigned char sd_setup_protect_cmnd(struct scsi_cmnd *scmd,
 	return protect;
 }
 
+static void sd_uninit_command(struct scsi_cmnd *cmd)
+{
+	struct request *rq = scsi_cmd_to_rq(cmd);
+	struct scsi_device *sdp = cmd->device;
+
+	if (!(rq->rq_flags & RQF_SPECIAL_PAYLOAD))
+		return;
+
+	if (sdp->sector_size > PAGE_SIZE)
+		mempool_free(rq->special_vec.bv_page, sd_large_page_pool);
+	else
+		mempool_free(rq->special_vec.bv_page, sd_page_pool);
+	rq->rq_flags &= ~RQF_SPECIAL_PAYLOAD;
+}
+
 static void *sd_set_special_bvec(struct scsi_cmnd *cmd, unsigned int data_len)
 {
 	struct page *page;
@@ -990,6 +1005,7 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd)
 	u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq));
 	unsigned int data_len = 24;
 	char *buf;
+	blk_status_t ret;
 
 	buf = sd_set_special_bvec(cmd, data_len);
 	if (!buf)
@@ -1008,7 +1024,10 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd)
 	cmd->transfersize = data_len;
 	rq->timeout = SD_TIMEOUT;
 
-	return scsi_alloc_sgtables(cmd);
+	ret = scsi_alloc_sgtables(cmd);
+	if (ret != BLK_STS_OK)
+		sd_uninit_command(cmd);
+	return ret;
 }
 
 static void sd_config_atomic(struct scsi_disk *sdkp, struct queue_limits *lim)
@@ -1079,6 +1098,7 @@ static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd,
 	u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq));
 	u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq));
 	u32 data_len = sdp->sector_size;
+	blk_status_t ret;
 
 	if (!sd_set_special_bvec(cmd, data_len))
 		return BLK_STS_RESOURCE;
@@ -1094,7 +1114,10 @@ static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd,
 	cmd->transfersize = data_len;
 	rq->timeout = unmap ? SD_TIMEOUT : SD_WRITE_SAME_TIMEOUT;
 
-	return scsi_alloc_sgtables(cmd);
+	ret = scsi_alloc_sgtables(cmd);
+	if (ret != BLK_STS_OK)
+		sd_uninit_command(cmd);
+	return ret;
 }
 
 static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd,
@@ -1106,6 +1129,7 @@ static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd,
 	u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq));
 	u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq));
 	u32 data_len = sdp->sector_size;
+	blk_status_t ret;
 
 	if (!sd_set_special_bvec(cmd, data_len))
 		return BLK_STS_RESOURCE;
@@ -1121,7 +1145,10 @@ static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd,
 	cmd->transfersize = data_len;
 	rq->timeout = unmap ? SD_TIMEOUT : SD_WRITE_SAME_TIMEOUT;
 
-	return scsi_alloc_sgtables(cmd);
+	ret = scsi_alloc_sgtables(cmd);
+	if (ret != BLK_STS_OK)
+		sd_uninit_command(cmd);
+	return ret;
 }
 
 static blk_status_t sd_setup_write_zeroes_cmnd(struct scsi_cmnd *cmd)
@@ -1550,20 +1577,6 @@ static blk_status_t sd_init_command(struct scsi_cmnd *cmd)
 	}
 }
 
-static void sd_uninit_command(struct scsi_cmnd *SCpnt)
-{
-	struct request *rq = scsi_cmd_to_rq(SCpnt);
-	struct scsi_device *sdp = SCpnt->device;
-	unsigned sector_size = sdp->sector_size;
-
-	if (rq->rq_flags & RQF_SPECIAL_PAYLOAD) {
-		if (sector_size > PAGE_SIZE)
-			mempool_free(rq->special_vec.bv_page, sd_large_page_pool);
-		else
-			mempool_free(rq->special_vec.bv_page, sd_page_pool);
-	}
-}
-
 static bool sd_need_revalidate(struct gendisk *disk, struct scsi_disk *sdkp)
 {
 	if (sdkp->device->removable || sdkp->write_prot) {
@@ -2406,8 +2419,8 @@ static int sd_done(struct scsi_cmnd *SCpnt)
 	}
 	sdkp->medium_access_timed_out = 0;
 
-	if (!scsi_status_is_check_condition(result) &&
-	    (!sense_valid || sense_deferred))
+	if (!scsi_status_is_check_condition(result) ||
+	    !sense_valid || sense_deferred)
 		goto out;
 
 	switch (sshdr.sense_key) {
@@ -4089,7 +4102,9 @@ static int sd_probe(struct scsi_device *sdp)
 	if (sdp->sector_size > PAGE_SIZE) {
 		if (sd_large_pool_create()) {
 			error = -ENOMEM;
-			goto out_free_index;
+			device_unregister(&sdkp->disk_dev);
+			put_disk(gd);
+			goto out;
 		}
 	}
 
diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c
index 5ec583dc2e7d..140e8a6addbe 100644
--- a/drivers/scsi/smartpqi/smartpqi_init.c
+++ b/drivers/scsi/smartpqi/smartpqi_init.c
@@ -34,11 +34,11 @@
 #define BUILD_TIMESTAMP
 #endif
 
-#define DRIVER_VERSION		"2.1.36-026"
+#define DRIVER_VERSION		"2.1.42-011"
 #define DRIVER_MAJOR		2
 #define DRIVER_MINOR		1
-#define DRIVER_RELEASE		36
-#define DRIVER_REVISION		26
+#define DRIVER_RELEASE		42
+#define DRIVER_REVISION		11
 
 #define DRIVER_NAME		"Microchip SmartPQI Driver (v" \
 				DRIVER_VERSION BUILD_TIMESTAMP ")"
@@ -66,6 +66,12 @@ static struct pqi_cmd_priv *pqi_cmd_priv(struct scsi_cmnd *cmd)
 	return scsi_cmd_priv(cmd);
 }
 
+static int pqi_init_cmd_priv(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
+{
+	memset(pqi_cmd_priv(cmd), 0, sizeof(struct pqi_cmd_priv));
+	return 0;
+}
+
 static void pqi_verify_structures(void);
 static void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info,
 	enum pqi_ctrl_shutdown_reason ctrl_shutdown_reason);
@@ -5958,6 +5964,17 @@ void pqi_prep_for_scsi_done(struct scsi_cmnd *scmd)
 	struct pqi_scsi_dev *device;
 	struct completion *wait;
 
+	/*
+	 * Clear the AIO-retry marker on final completion so the tag
+	 * starts clean on its next dispatch.  On DID_IMM_RETRY leave
+	 * it intact: pqi_aio_io_complete() sets DID_IMM_RETRY and
+	 * bumps the marker to steer the requeue onto the RAID path,
+	 * and pqi_process_raid_io_error() consumes the non-zero
+	 * marker to offline a misbehaving drive.
+	 */
+	if (host_byte(scmd->result) != DID_IMM_RETRY)
+		pqi_cmd_priv(scmd)->this_residual = 0;
+
 	if (!scmd->device) {
 		set_host_byte(scmd, DID_NO_CONNECT);
 		return;
@@ -6918,6 +6935,144 @@ static int pqi_passthru_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *arg)
 	return rc;
 }
 
+static int pqi_big_passthru_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *arg)
+{
+	int rc;
+	char *kernel_buffer = NULL;
+	u16 iu_length;
+	size_t sense_data_length;
+	BIG_IOCTL_Command_struct iocommand;
+	struct pqi_raid_path_request request;
+	struct pqi_raid_error_info pqi_error_info;
+	struct ciss_error_info ciss_error_info;
+
+	if (pqi_ctrl_offline(ctrl_info))
+		return -ENXIO;
+	if (pqi_ofa_in_progress(ctrl_info) && pqi_ctrl_blocked(ctrl_info))
+		return -EBUSY;
+	if (!arg)
+		return -EINVAL;
+	if (!capable(CAP_SYS_RAWIO))
+		return -EPERM;
+	if (copy_from_user(&iocommand, arg, sizeof(iocommand)))
+		return -EFAULT;
+	if (iocommand.buf_size < 1 &&
+		iocommand.Request.Type.Direction != XFER_NONE)
+		return -EINVAL;
+	if (iocommand.Request.CDBLen > sizeof(request.cdb))
+		return -EINVAL;
+	if (iocommand.Request.Type.Type != TYPE_CMD)
+		return -EINVAL;
+
+	switch (iocommand.Request.Type.Direction) {
+	case XFER_NONE:
+	case XFER_WRITE:
+	case XFER_READ:
+	case XFER_READ | XFER_WRITE:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (iocommand.buf_size > 0) {
+		kernel_buffer = kmalloc(iocommand.buf_size, GFP_KERNEL);
+		if (!kernel_buffer)
+			return -ENOMEM;
+		if (iocommand.Request.Type.Direction & XFER_WRITE) {
+			if (copy_from_user(kernel_buffer, iocommand.buf,
+				iocommand.buf_size)) {
+				rc = -EFAULT;
+				goto out;
+			}
+		} else {
+			memset(kernel_buffer, 0, iocommand.buf_size);
+		}
+	}
+
+	memset(&request, 0, sizeof(request));
+
+	request.header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO;
+	iu_length = offsetof(struct pqi_raid_path_request, sg_descriptors) -
+		PQI_REQUEST_HEADER_LENGTH;
+	memcpy(request.lun_number, iocommand.LUN_info.LunAddrBytes, sizeof(request.lun_number));
+	memcpy(request.cdb, iocommand.Request.CDB, iocommand.Request.CDBLen);
+	request.additional_cdb_bytes_usage = SOP_ADDITIONAL_CDB_BYTES_0;
+
+	switch (iocommand.Request.Type.Direction) {
+	case XFER_NONE:
+		request.data_direction = SOP_NO_DIRECTION_FLAG;
+		break;
+	case XFER_WRITE:
+		request.data_direction = SOP_WRITE_FLAG;
+		break;
+	case XFER_READ:
+		request.data_direction = SOP_READ_FLAG;
+		break;
+	case XFER_READ | XFER_WRITE:
+		request.data_direction = SOP_BIDIRECTIONAL;
+		break;
+	}
+
+	request.task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE;
+
+	if (iocommand.buf_size > 0) {
+		put_unaligned_le32(iocommand.buf_size, &request.buffer_length);
+
+		rc = pqi_map_single(ctrl_info->pci_dev,
+			&request.sg_descriptors[0], kernel_buffer,
+			iocommand.buf_size, DMA_BIDIRECTIONAL);
+		if (rc)
+			goto out;
+
+		iu_length += sizeof(request.sg_descriptors[0]);
+	}
+
+	put_unaligned_le16(iu_length, &request.header.iu_length);
+
+	if (ctrl_info->raid_iu_timeout_supported)
+		put_unaligned_le32(iocommand.Request.Timeout, &request.timeout);
+
+	rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header,
+		PQI_SYNC_FLAGS_INTERRUPTABLE, &pqi_error_info);
+
+	if (iocommand.buf_size > 0)
+		pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, DMA_BIDIRECTIONAL);
+
+	memset(&iocommand.error_info, 0, sizeof(iocommand.error_info));
+
+	if (rc == 0) {
+		pqi_error_info_to_ciss(&pqi_error_info, &ciss_error_info);
+		iocommand.error_info.ScsiStatus = ciss_error_info.scsi_status;
+		iocommand.error_info.CommandStatus = ciss_error_info.command_status;
+		sense_data_length = ciss_error_info.sense_data_length;
+		if (sense_data_length) {
+			if (sense_data_length > sizeof(iocommand.error_info.SenseInfo))
+				sense_data_length = sizeof(iocommand.error_info.SenseInfo);
+			memcpy(iocommand.error_info.SenseInfo,
+				pqi_error_info.data, sense_data_length);
+			iocommand.error_info.SenseLen = sense_data_length;
+		}
+	}
+
+	if (copy_to_user(arg, &iocommand, sizeof(iocommand))) {
+		rc = -EFAULT;
+		goto out;
+	}
+
+	if (rc == 0 && iocommand.buf_size > 0 &&
+		(iocommand.Request.Type.Direction & XFER_READ)) {
+		if (copy_to_user(iocommand.buf, kernel_buffer,
+			iocommand.buf_size)) {
+			rc = -EFAULT;
+		}
+	}
+
+out:
+	kfree(kernel_buffer);
+
+	return rc;
+}
+
 static int pqi_ioctl(struct scsi_device *sdev, unsigned int cmd,
 		     void __user *arg)
 {
@@ -6941,6 +7096,12 @@ static int pqi_ioctl(struct scsi_device *sdev, unsigned int cmd,
 	case CCISS_PASSTHRU:
 		rc = pqi_passthru_ioctl(ctrl_info, arg);
 		break;
+	case CCISS_BIG_PASSTHRU:
+		rc = pqi_big_passthru_ioctl(ctrl_info, arg);
+		break;
+	case CCISS_BIG_PASSTHRU_SUPPORTED:
+		rc = 0;
+		break;
 	default:
 		rc = -EINVAL;
 		break;
@@ -7612,6 +7773,7 @@ static const struct scsi_host_template pqi_driver_template = {
 	.sdev_groups = pqi_sdev_groups,
 	.shost_groups = pqi_shost_groups,
 	.cmd_size = sizeof(struct pqi_cmd_priv),
+	.init_cmd_priv = pqi_init_cmd_priv,
 };
 
 static int pqi_register_scsi(struct pqi_ctrl_info *ctrl_info)
@@ -10152,10 +10314,30 @@ static const struct pci_device_id pqi_pci_id_table[] = {
 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
 			       0x207d, 0x4240)
 	},
+	{
+		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+			       0x207d, 0x4246)
+	},
+	{
+		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+			       0x207d, 0x4256)
+	},
+	{
+		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+			       0x207d, 0x4356)
+	},
 	{
 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
 			       0x207d, 0x4840)
 	},
+	{
+		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+			       0x207d, 0x4940)
+	},
+	{
+		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+			       0x207d, 0x4a46)
+	},
 	{
 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
 			       PCI_VENDOR_ID_ADVANTECH, 0x8312)
@@ -10340,6 +10522,18 @@ static const struct pci_device_id pqi_pci_id_table[] = {
 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
 			       0x1cf2, 0x544f)
 	},
+	{
+		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+			       0x1cf2, 0x5451)
+	},
+	{
+		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+			       0x1cf2, 0x5452)
+	},
+	{
+		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+			       0x1cf2, 0x5453)
+	},
 	{
 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
 			       0x1cf2, 0x54da)
@@ -10380,6 +10574,10 @@ static const struct pci_device_id pqi_pci_id_table[] = {
 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
 			       0x1f3f, 0x0610)
 	},
+	{
+		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
+			       0x1f3f, 0x0670)
+	},
 	{
 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f,
 			       PCI_VENDOR_ID_LENOVO, 0x0220)
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 571ea549152b..58b6ae96d03f 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -520,6 +520,36 @@ static void storvsc_host_scan(struct work_struct *work)
 	scsi_scan_host(host);
 }
 
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+static int storvsc_user_scan(struct Scsi_Host *host,
+			     unsigned int channel,
+			     unsigned int id,
+			     u64 lun)
+{
+	unsigned int first_channel, last_channel;
+	unsigned int first_id, end_id;
+	unsigned int ch, target;
+
+	if ((channel != SCAN_WILD_CARD && channel > host->max_channel) ||
+	    (id != SCAN_WILD_CARD && id >= host->max_id) ||
+	    (lun != SCAN_WILD_CARD && lun >= host->max_lun))
+		return -EINVAL;
+
+	first_channel = channel == SCAN_WILD_CARD ? 0 : channel;
+	last_channel = channel == SCAN_WILD_CARD ? host->max_channel : channel;
+	first_id = id == SCAN_WILD_CARD ? 0 : id;
+	end_id = id == SCAN_WILD_CARD ? host->max_id : id + 1;
+
+	for (ch = first_channel; ch <= last_channel; ch++) {
+		for (target = first_id; target < end_id; target++)
+			scsi_scan_target(&host->shost_gendev, ch, target, lun,
+					 SCSI_SCAN_MANUAL);
+	}
+
+	return 0;
+}
+#endif
+
 static void storvsc_remove_lun(struct work_struct *work)
 {
 	struct storvsc_scan_work *wrk;
@@ -2232,6 +2262,8 @@ static int __init storvsc_drv_init(void)
 	fc_transport_template = fc_attach_transport(&fc_transport_functions);
 	if (!fc_transport_template)
 		return -ENODEV;
+
+	fc_transport_template->user_scan = storvsc_user_scan;
 #endif
 
 	ret = vmbus_driver_register(&storvsc_drv);
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index 5fdaa71f0652..35731b18c519 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -122,10 +122,11 @@ static void virtscsi_complete_cmd(struct virtio_scsi *vscsi, void *buf)
 	struct virtio_scsi_cmd *cmd = buf;
 	struct scsi_cmnd *sc = cmd->sc;
 	struct virtio_scsi_cmd_resp *resp = &cmd->resp.cmd;
+	unsigned sense_len = virtio32_to_cpu(vscsi->vdev, resp->sense_len);
 
 	dev_dbg(&sc->device->sdev_gendev,
 		"cmd %p response %u status %#02x sense_len %u\n",
-		sc, resp->response, resp->status, resp->sense_len);
+		sc, resp->response, resp->status, sense_len);
 
 	sc->result = resp->status;
 	virtscsi_compute_resid(sc, virtio32_to_cpu(vscsi->vdev, resp->resid));
@@ -166,13 +167,10 @@ static void virtscsi_complete_cmd(struct virtio_scsi *vscsi, void *buf)
 		break;
 	}
 
-	WARN_ON(virtio32_to_cpu(vscsi->vdev, resp->sense_len) >
-		VIRTIO_SCSI_SENSE_SIZE);
+	WARN_ON(sense_len > VIRTIO_SCSI_SENSE_SIZE);
 	if (resp->sense_len) {
 		memcpy(sc->sense_buffer, resp->sense,
-		       min_t(u32,
-			     virtio32_to_cpu(vscsi->vdev, resp->sense_len),
-			     VIRTIO_SCSI_SENSE_SIZE));
+		       min_t(u32, sense_len, VIRTIO_SCSI_SENSE_SIZE));
 	}
 
 	scsi_done(sc);
@@ -288,8 +286,9 @@ static void virtscsi_handle_transport_reset(struct virtio_scsi *vscsi,
 	struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev);
 	unsigned int target = event->lun[1];
 	unsigned int lun = (event->lun[2] << 8) | event->lun[3];
+	unsigned int reason = virtio32_to_cpu(vscsi->vdev, event->reason);
 
-	switch (virtio32_to_cpu(vscsi->vdev, event->reason)) {
+	switch (reason) {
 	case VIRTIO_SCSI_EVT_RESET_RESCAN:
 		if (lun == 0) {
 			scsi_scan_target(&shost->shost_gendev, 0, target,
@@ -309,7 +308,7 @@ static void virtscsi_handle_transport_reset(struct virtio_scsi *vscsi,
 		}
 		break;
 	default:
-		pr_info("Unsupported virtio scsi event reason %x\n", event->reason);
+		pr_info("Unsupported virtio scsi event reason %x\n", reason);
 	}
 }
 
@@ -409,7 +408,8 @@ static void virtscsi_handle_event(struct work_struct *work)
 		virtscsi_handle_param_change(vscsi, event);
 		break;
 	default:
-		pr_err("Unsupported virtio scsi event %x\n", event->event);
+		pr_err("Unsupported virtio scsi event %x\n",
+		       virtio32_to_cpu(vscsi->vdev, event->event));
 	}
 	virtscsi_kick_event(vscsi, event_node);
 }
diff --git a/drivers/scsi/zorro7xx.c b/drivers/scsi/zorro7xx.c
index 6aca9897b231..21c769dc1ecb 100644
--- a/drivers/scsi/zorro7xx.c
+++ b/drivers/scsi/zorro7xx.c
@@ -50,25 +50,25 @@ static struct zorro_driver_data {
 static struct zorro_device_id zorro7xx_zorro_tbl[] = {
 	{
 		.id = ZORRO_PROD_PHASE5_BLIZZARD_603E_PLUS,
-		.driver_data = (unsigned long)&zorro7xx_driver_data[0],
+		.driver_data_ptr = &zorro7xx_driver_data[0],
 	},
 	{
 		.id = ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE_40xx,
-		.driver_data = (unsigned long)&zorro7xx_driver_data[1],
+		.driver_data_ptr = &zorro7xx_driver_data[1],
 	},
 	{
 		.id = ZORRO_PROD_CBM_A4091_1,
-		.driver_data = (unsigned long)&zorro7xx_driver_data[2],
+		.driver_data_ptr = &zorro7xx_driver_data[2],
 	},
 	{
 		.id = ZORRO_PROD_CBM_A4091_2,
-		.driver_data = (unsigned long)&zorro7xx_driver_data[2],
+		.driver_data_ptr = &zorro7xx_driver_data[2],
 	},
 	{
 		.id = ZORRO_PROD_GVP_GFORCE_040_060,
-		.driver_data = (unsigned long)&zorro7xx_driver_data[3],
+		.driver_data_ptr = &zorro7xx_driver_data[3],
 	},
-	{ 0 }
+	{ }
 };
 MODULE_DEVICE_TABLE(zorro, zorro7xx_zorro_tbl);
 
@@ -77,11 +77,11 @@ static int zorro7xx_init_one(struct zorro_dev *z,
 {
 	struct Scsi_Host *host;
 	struct NCR_700_Host_Parameters *hostdata;
-	struct zorro_driver_data *zdd;
+	const struct zorro_driver_data *zdd;
 	unsigned long board, ioaddr;
 
 	board = zorro_resource_start(z);
-	zdd = (struct zorro_driver_data *)ent->driver_data;
+	zdd = ent->driver_data_ptr;
 
 	if (zdd->absolute) {
 		ioaddr = zdd->offset;
diff --git a/drivers/scsi/zorro_esp.c b/drivers/scsi/zorro_esp.c
index 1622285c9aec..178d46140674 100644
--- a/drivers/scsi/zorro_esp.c
+++ b/drivers/scsi/zorro_esp.c
@@ -706,7 +706,7 @@ static const struct zorro_device_id zorro_esp_zorro_tbl[] = {
 		.id = ZORRO_ID(PHASE5, 0x19, 0),
 		.driver_data = ZORRO_CYBERII,
 	},
-	{ 0 }
+	{ }
 };
 MODULE_DEVICE_TABLE(zorro, zorro_esp_zorro_tbl);
 
diff --git a/drivers/ufs/core/ufs-debugfs.c b/drivers/ufs/core/ufs-debugfs.c
index e3dd81d6fe82..be527209540d 100644
--- a/drivers/ufs/core/ufs-debugfs.c
+++ b/drivers/ufs/core/ufs-debugfs.c
@@ -165,7 +165,7 @@ static ssize_t ufs_saved_err_write(struct file *file, const char __user *buf,
 	char val_str[16] = { };
 	int val, ret;
 
-	if (count > sizeof(val_str))
+	if (count >= sizeof(val_str))
 		return -EINVAL;
 	if (copy_from_user(val_str, buf, count))
 		return -EFAULT;
diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c
index 13b60a2d06db..8106d55f4041 100644
--- a/drivers/ufs/core/ufs-mcq.c
+++ b/drivers/ufs/core/ufs-mcq.c
@@ -296,14 +296,20 @@ static int ufshcd_mcq_get_tag(struct ufs_hba *hba, struct cq_entry *cqe)
 	if (hba->ufs_version >= ufshci_version(4, 1))
 		return cqe->task_tag;
 
-	/* sizeof(struct utp_transfer_cmd_desc) must be a multiple of 128 */
+	/* Both UCD types must have a size that is a multiple of 128 bytes */
 	BUILD_BUG_ON(sizeof(struct utp_transfer_cmd_desc) & GENMASK(6, 0));
+	BUILD_BUG_ON(sizeof(struct utp_devman_cmd_desc) & GENMASK(6, 0));
 
 	/* Bits 63:7 UCD base address, 6:5 are reserved, 4:0 is SQ ID */
-	addr = (le64_to_cpu(cqe->command_desc_base_addr) & CQE_UCD_BA) -
-		hba->ucdl_dma_addr;
+	addr = le64_to_cpu(cqe->command_desc_base_addr) & CQE_UCD_BA;
 
-	return div_u64(addr, ufshcd_get_ucd_size(hba));
+	/* The devman UCD is outside the pool; return its reserved tag. */
+	if (unlikely(addr == hba->devman_ucd_dma_addr))
+		return hba->dev_cmd.tag;
+
+	/* Pool entries follow the reserved tags. */
+	return div_u64(addr - hba->ucdl_dma_addr, ufshcd_get_ucd_size(hba)) +
+		UFSHCD_NUM_RESERVED;
 }
 
 static void ufshcd_mcq_process_cqe(struct ufs_hba *hba,
diff --git a/drivers/ufs/core/ufs-rpmb.c b/drivers/ufs/core/ufs-rpmb.c
index ffad049872b9..53f66b274aca 100644
--- a/drivers/ufs/core/ufs-rpmb.c
+++ b/drivers/ufs/core/ufs-rpmb.c
@@ -69,7 +69,11 @@ static int ufs_rpmb_route_frames(struct device *dev, u8 *req, unsigned int req_l
 
 	hba = ufs_rpmb->hba;
 
-	req_type = be16_to_cpu(frm_out->req_resp);
+	/* req_resp is at the end of an RPMB frame. */
+	if (req_len < sizeof(*frm_out))
+		return -EINVAL;
+
+	req_type = get_unaligned_be16(&frm_out->req_resp);
 
 	switch (req_type) {
 	case RPMB_PROGRAM_KEY:
@@ -107,7 +111,7 @@ static int ufs_rpmb_route_frames(struct device *dev, u8 *req, unsigned int req_l
 		struct rpmb_frame *frm_resp = (struct rpmb_frame *)resp;
 
 		memset(frm_resp, 0, sizeof(*frm_resp));
-		frm_resp->req_resp = cpu_to_be16(RPMB_RESULT_READ);
+		put_unaligned_be16(RPMB_RESULT_READ, &frm_resp->req_resp);
 		ret = ufs_sec_submit(hba, protocol_id, resp, resp_len, true);
 		if (ret) {
 			dev_err(dev, "Result read request failed with ret=%d\n", ret);
diff --git a/drivers/ufs/core/ufs-sysfs.c b/drivers/ufs/core/ufs-sysfs.c
index d9dc4cc3452e..63e670d1a9d9 100644
--- a/drivers/ufs/core/ufs-sysfs.c
+++ b/drivers/ufs/core/ufs-sysfs.c
@@ -54,6 +54,7 @@ static const char *ufs_hs_gear_to_string(enum ufs_hs_gear_tag gear)
 	case UFS_HS_G3:	return "HS_GEAR3";
 	case UFS_HS_G4:	return "HS_GEAR4";
 	case UFS_HS_G5:	return "HS_GEAR5";
+	case UFS_HS_G6:	return "HS_GEAR6";
 	default:	return "UNKNOWN";
 	}
 }
diff --git a/drivers/ufs/core/ufs-txeq.c b/drivers/ufs/core/ufs-txeq.c
index aa64f2bf4f1e..fa5f539632eb 100644
--- a/drivers/ufs/core/ufs-txeq.c
+++ b/drivers/ufs/core/ufs-txeq.c
@@ -10,6 +10,7 @@
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
+#include <linux/sched/mm.h>
 #include <ufs/ufshcd.h>
 #include <ufs/unipro.h>
 #include "ufshcd-priv.h"
@@ -481,7 +482,8 @@ static void ufshcd_evaluate_tx_eqtr_fom(struct ufs_hba *hba,
  * @h_iter: host TX EQTR iterator data structure
  * @d_iter: device TX EQTR iterator data structure
  *
- * Returns 0 on success, negative error code otherwise
+ * Returns 0 on success, negative error code if get_rx_fom vops fails.
+ * RX_FOM DME get failures are logged and treated as 0 FOM for that lane.
  */
 static int ufshcd_get_rx_fom(struct ufs_hba *hba,
 			     struct ufs_pa_layer_attr *pwr_mode,
@@ -496,8 +498,12 @@ static int ufshcd_get_rx_fom(struct ufs_hba *hba,
 		ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB_SEL(RX_FOM,
 					  UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)),
 					  &fom);
-		if (ret)
-			return ret;
+		if (ret) {
+			h_iter->fom[lane] = 0;
+			dev_dbg(hba->dev, "Failed to get FOM for Host TX Lane %d: %d\n",
+				 lane, ret);
+			continue;
+		}
 
 		h_iter->fom[lane] = (u8)fom;
 	}
@@ -507,8 +513,12 @@ static int ufshcd_get_rx_fom(struct ufs_hba *hba,
 		ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(RX_FOM,
 				     UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)),
 				     &fom);
-		if (ret)
-			return ret;
+		if (ret) {
+			d_iter->fom[lane] = 0;
+			dev_dbg(hba->dev, "Failed to get FOM for Device TX Lane %d: %d\n",
+				 lane, ret);
+			continue;
+		}
 
 		d_iter->fom[lane] = (u8)fom;
 	}
@@ -1216,14 +1226,26 @@ static int ufshcd_tx_eqtr(struct ufs_hba *hba,
 			  struct ufs_pa_layer_attr *pwr_mode)
 {
 	struct ufs_pa_layer_attr old_pwr_info;
+	unsigned int noio_flag;
+	int notify_ret;
 	int ret;
 
+	/*
+	 * ufshcd_tx_eqtr() is called from a power-mode-change context where
+	 * I/O is suspended. Use memalloc_noio_save() to propagate GFP_NOIO
+	 * to all allocations in the call tree instead of tagging each call
+	 * site individually.
+	 */
+	noio_flag = memalloc_noio_save();
+
 	if (!params->eqtr_record) {
 		params->eqtr_record = devm_kzalloc(hba->dev,
 						   sizeof(*params->eqtr_record),
 						   GFP_KERNEL);
-		if (!params->eqtr_record)
-			return -ENOMEM;
+		if (!params->eqtr_record) {
+			ret = -ENOMEM;
+			goto out_noio_restore;
+		}
 	}
 
 	memcpy(&old_pwr_info, &hba->pwr_info, sizeof(struct ufs_pa_layer_attr));
@@ -1231,23 +1253,31 @@ static int ufshcd_tx_eqtr(struct ufs_hba *hba,
 	ret = ufshcd_tx_eqtr_prepare(hba, pwr_mode);
 	if (ret) {
 		dev_err(hba->dev, "Failed to prepare TX EQTR: %d\n", ret);
-		goto out;
+		goto out_unprepare;
 	}
 
 	ret = ufshcd_vops_tx_eqtr_notify(hba, PRE_CHANGE, pwr_mode);
-	if (ret)
-		goto out;
+	if (ret) {
+		dev_err(hba->dev, "TX EQTR PRE_CHANGE notify failed: %d\n", ret);
+		goto out_unprepare;
+	}
 
 	ret = __ufshcd_tx_eqtr(hba, params, pwr_mode);
-	if (ret)
-		goto out;
 
-	ret = ufshcd_vops_tx_eqtr_notify(hba, POST_CHANGE, pwr_mode);
+	notify_ret = ufshcd_vops_tx_eqtr_notify(hba, POST_CHANGE, pwr_mode);
+	if (notify_ret)
+		dev_err(hba->dev, "TX EQTR POST_CHANGE notify failed: %d\n", notify_ret);
 
-out:
+	if (!ret)
+		ret = notify_ret;
+
+out_unprepare:
 	if (ret)
 		ufshcd_tx_eqtr_unprepare(hba, &old_pwr_info);
 
+out_noio_restore:
+	memalloc_noio_restore(noio_flag);
+
 	return ret;
 }
 
@@ -1301,7 +1331,13 @@ int ufshcd_config_tx_eq_settings(struct ufs_hba *hba,
 	}
 
 	params = &hba->tx_eq_params[gear - 1];
-	if (!params->is_valid || force_tx_eqtr) {
+	/*
+	 * TX EQTR must run for the following cases:
+	 * 1. TX EQ settings are invalid.
+	 * 2. TX EQ settings are from Device Tree.
+	 * 3. TX EQTR procedure is forced.
+	 */
+	if (!params->is_valid || params->from_dt || force_tx_eqtr) {
 		int ret;
 
 		ret = ufshcd_tx_eqtr(hba, params, pwr_mode);
@@ -1314,6 +1350,8 @@ int ufshcd_config_tx_eq_settings(struct ufs_hba *hba,
 		/* Mark TX Equalization settings as valid */
 		params->is_valid = true;
 		params->is_trained = true;
+		/* TX EQTR succeeds, clear from_dt flag */
+		params->from_dt = false;
 		params->is_applied = false;
 	}
 
@@ -1499,6 +1537,11 @@ static void ufshcd_extract_tx_eq_settings_attrs(struct ufs_hba *hba, u8 gear)
 	}
 
 	params->is_valid = true;
+	/*
+	 * Optimal TX EQ settings are retrieved from UFS device attributes,
+	 * clear from_dt flag to avoid TX EQTR procedure.
+	 */
+	params->from_dt = false;
 }
 
 void ufshcd_retrieve_tx_eq_settings(struct ufs_hba *hba)
diff --git a/drivers/ufs/core/ufs_bsg.c b/drivers/ufs/core/ufs_bsg.c
index 58b506eac6dc..7a66917b3fc0 100644
--- a/drivers/ufs/core/ufs_bsg.c
+++ b/drivers/ufs/core/ufs_bsg.c
@@ -13,37 +13,31 @@
 #include <ufs/ufshcd.h>
 #include "ufshcd-priv.h"
 
-static int ufs_bsg_get_query_desc_size(struct ufs_hba *hba, int *desc_len,
-				       struct utp_upiu_query *qr)
-{
-	int desc_size = be16_to_cpu(qr->length);
-
-	if (desc_size <= 0)
-		return -EINVAL;
-
-	*desc_len = min_t(int, QUERY_DESC_MAX_SIZE, desc_size);
-
-	return 0;
-}
-
 static int ufs_bsg_alloc_desc_buffer(struct ufs_hba *hba, struct bsg_job *job,
-				     uint8_t **desc_buff, int *desc_len,
+				     u8 **desc_buff, u16 *desc_len,
 				     enum query_opcode desc_op)
 {
 	struct ufs_bsg_request *bsg_request = job->request;
 	struct utp_upiu_query *qr;
+	u16 max_desc_len;
 	u8 *descp;
 
 	if (desc_op != UPIU_QUERY_OPCODE_WRITE_DESC &&
-	    desc_op != UPIU_QUERY_OPCODE_READ_DESC)
+	    desc_op != UPIU_QUERY_OPCODE_READ_DESC &&
+	    desc_op != UPIU_QUERY_OPCODE_AGGREGATED_READ)
 		goto out;
 
 	qr = &bsg_request->upiu_req.qr;
-	if (ufs_bsg_get_query_desc_size(hba, desc_len, qr)) {
+	*desc_len = be16_to_cpu(qr->length);
+	if (*desc_len == 0) {
 		dev_err(hba->dev, "Illegal desc size\n");
 		return -EINVAL;
 	}
 
+	max_desc_len = desc_op == UPIU_QUERY_OPCODE_AGGREGATED_READ ?
+		       QUERY_AGGREGATED_MAX_SIZE : QUERY_DESC_MAX_SIZE;
+	*desc_len = min(*desc_len, max_desc_len);
+
 	if (*desc_len > job->request_payload.payload_len) {
 		dev_err(hba->dev, "Illegal desc size\n");
 		return -EINVAL;
@@ -108,6 +102,9 @@ static int ufs_bsg_exec_advanced_rpmb_req(struct ufs_hba *hba, struct bsg_job *j
 		if (!payload->payload_len || !payload->sg_cnt)
 			return -EINVAL;
 
+		if (payload->sg_cnt > UFSHCD_DEVMAN_SG_ENTRIES)
+			return -EINVAL;
+
 		sg_cnt = dma_map_sg(hba->host->dma_dev, payload->sg_list, payload->sg_cnt, dir);
 		if (unlikely(!sg_cnt))
 			return -ENOMEM;
@@ -136,8 +133,9 @@ static int ufs_bsg_request(struct bsg_job *job)
 	struct ufs_hba *hba = shost_priv(dev_to_shost(job->dev->parent));
 	struct uic_command uc = {};
 	int msgcode;
-	uint8_t *buff = NULL;
-	int desc_len = 0;
+	u8 *buff = NULL;
+	u16 desc_len = 0;
+	int buff_len;
 	enum query_opcode desc_op = UPIU_QUERY_OPCODE_NOP;
 	int ret;
 	bool rpmb = false;
@@ -156,12 +154,15 @@ static int ufs_bsg_request(struct bsg_job *job)
 		fallthrough;
 	case UPIU_TRANSACTION_NOP_OUT:
 	case UPIU_TRANSACTION_TASK_REQ:
+		buff_len = desc_len;
 		ret = ufshcd_exec_raw_upiu_cmd(hba, &bsg_request->upiu_req,
 					       &bsg_reply->upiu_rsp, msgcode,
-					       buff, &desc_len, desc_op);
+					       buff, &buff_len, desc_op);
+		desc_len = buff_len;
 		if (ret)
 			dev_err(hba->dev, "exe raw upiu: error code %d\n", ret);
-		else if (desc_op == UPIU_QUERY_OPCODE_READ_DESC && desc_len) {
+		else if ((desc_op == UPIU_QUERY_OPCODE_READ_DESC ||
+			  desc_op == UPIU_QUERY_OPCODE_AGGREGATED_READ) && desc_len) {
 			bsg_reply->reply_payload_rcv_len =
 				sg_copy_from_buffer(job->request_payload.sg_list,
 						    job->request_payload.sg_cnt,
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index d3044a3089b5..a51e071916cf 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -2506,7 +2506,10 @@ static inline int ufshcd_hba_capabilities(struct ufs_hba *hba)
 	hba->nutmrs =
 	((hba->capabilities & MASK_TASK_MANAGEMENT_REQUEST_SLOTS) >> 16) + 1;
 
-	hba->nortt = FIELD_GET(MASK_NUMBER_OUTSTANDING_RTT, hba->capabilities) + 1;
+	if (hba->vops && hba->vops->get_hba_nortt)
+		hba->nortt = hba->vops->get_hba_nortt(hba);
+	else
+		hba->nortt = FIELD_GET(MASK_NUMBER_OUTSTANDING_RTT, hba->capabilities) + 1;
 
 	/* Read crypto capabilities */
 	err = ufshcd_hba_init_crypto_capabilities(hba);
@@ -2944,23 +2947,44 @@ static void ufshcd_comp_scsi_upiu(struct ufs_hba *hba, struct scsi_cmnd *cmd)
 static void ufshcd_init_lrb(struct ufs_hba *hba, struct scsi_cmnd *cmd)
 {
 	const int i = scsi_cmd_to_rq(cmd)->tag;
-	struct utp_transfer_cmd_desc *cmd_descp =
-		(void *)hba->ucdl_base_addr + i * ufshcd_get_ucd_size(hba);
 	struct utp_transfer_req_desc *utrdlp = hba->utrdl_base_addr;
-	dma_addr_t cmd_desc_element_addr =
-		hba->ucdl_dma_addr + i * ufshcd_get_ucd_size(hba);
 	u16 response_offset = le16_to_cpu(utrdlp[i].response_upiu_offset);
 	u16 prdt_offset = le16_to_cpu(utrdlp[i].prd_table_offset);
 	struct ufshcd_lrb *lrb = scsi_cmd_priv(cmd);
+	u8 *command_upiu, *response_upiu, *prd_table;
+	dma_addr_t cmd_desc_element_addr;
+
+	/* The reserved tag uses a dedicated UCD outside the pool. */
+	if (unlikely(blk_mq_is_reserved_rq(scsi_cmd_to_rq(cmd)))) {
+		struct utp_devman_cmd_desc *cmd_descp = hba->devman_ucd_base_addr;
+
+		cmd_desc_element_addr = hba->devman_ucd_dma_addr;
+		command_upiu = cmd_descp->command_upiu;
+		response_upiu = cmd_descp->response_upiu;
+		prd_table = cmd_descp->prd_table;
+	} else {
+		int slot = i - UFSHCD_NUM_RESERVED;
+		struct utp_transfer_cmd_desc *cmd_descp;
+
+		/* Non-reserved tags start at UFSHCD_NUM_RESERVED, so slot >= 0. */
+		WARN_ON_ONCE(slot < 0);
+		cmd_descp = (void *)hba->ucdl_base_addr + slot * ufshcd_get_ucd_size(hba);
+
+		cmd_desc_element_addr =
+			hba->ucdl_dma_addr + slot * ufshcd_get_ucd_size(hba);
+		command_upiu = cmd_descp->command_upiu;
+		response_upiu = cmd_descp->response_upiu;
+		prd_table = cmd_descp->prd_table;
+	}
 
 	lrb->utr_descriptor_ptr = utrdlp + i;
 	lrb->utrd_dma_addr =
 		hba->utrdl_dma_addr + i * sizeof(struct utp_transfer_req_desc);
-	lrb->ucd_req_ptr = (struct utp_upiu_req *)cmd_descp->command_upiu;
+	lrb->ucd_req_ptr = (struct utp_upiu_req *)command_upiu;
 	lrb->ucd_req_dma_addr = cmd_desc_element_addr;
-	lrb->ucd_rsp_ptr = (struct utp_upiu_rsp *)cmd_descp->response_upiu;
+	lrb->ucd_rsp_ptr = (struct utp_upiu_rsp *)response_upiu;
 	lrb->ucd_rsp_dma_addr = cmd_desc_element_addr + response_offset;
-	lrb->ucd_prdt_ptr = (struct ufshcd_sg_entry *)cmd_descp->prd_table;
+	lrb->ucd_prdt_ptr = (struct ufshcd_sg_entry *)prd_table;
 	lrb->ucd_prdt_dma_addr = cmd_desc_element_addr + prdt_offset;
 }
 
@@ -3157,6 +3181,7 @@ static void ufshcd_setup_dev_cmd(struct ufs_hba *hba, struct scsi_cmnd *cmd,
 	__ufshcd_setup_cmd(hba, cmd, lun, tag);
 	lrbp->intr_cmd = true; /* No interrupt aggregation */
 	hba->dev_cmd.type = cmd_type;
+	hba->dev_cmd.tag = tag;
 }
 
 /*
@@ -3865,7 +3890,7 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u
 {
 	struct uc_string_id *uc_str;
 	u8 *str;
-	int ret;
+	int ret, uc_len;
 
 	if (!buf)
 		return -EINVAL;
@@ -3890,11 +3915,19 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u
 		goto out;
 	}
 
+	uc_len = uc_str->len - QUERY_DESC_HDR_SIZE;
+	if (uc_len % sizeof(*uc_str->uc)) {
+		dev_err(hba->dev, "String Desc has an odd UTF-16 payload length\n");
+		str = NULL;
+		ret = -EINVAL;
+		goto out;
+	}
+
 	if (fmt == SD_ASCII_STD) {
 		ssize_t ascii_len;
 		int i;
-		/* remove header and divide by 2 to move from UTF16 to UTF8 */
-		ascii_len = (uc_str->len - QUERY_DESC_HDR_SIZE) / 2 + 1;
+		/* Allow up to three UTF-8 bytes per UTF-16 code unit plus a NUL. */
+		ascii_len = uc_len / sizeof(*uc_str->uc) * 3 + 1;
 		str = kzalloc(ascii_len, GFP_KERNEL);
 		if (!str) {
 			ret = -ENOMEM;
@@ -3906,7 +3939,7 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u
 		 * we need to convert to utf-8 so it can be displayed
 		 */
 		ret = utf16s_to_utf8s(uc_str->uc,
-				      uc_str->len - QUERY_DESC_HDR_SIZE,
+				      uc_len / sizeof(*uc_str->uc),
 				      UTF16_BIG_ENDIAN, str, ascii_len - 1);
 
 		/* replace non-printable or non-ASCII characters with spaces */
@@ -3916,11 +3949,17 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u
 		str[ret++] = '\0';
 
 	} else {
-		str = kmemdup(uc_str->uc, uc_str->len, GFP_KERNEL);
+		/*
+		 * Keep the bLength-sized raw output for the RPMB device ID ABI.
+		 * The two bytes beyond the UTF-16 payload are explicitly zeroed
+		 * instead of being read past the descriptor buffer.
+		 */
+		str = kzalloc(uc_str->len, GFP_KERNEL);
 		if (!str) {
 			ret = -ENOMEM;
 			goto out;
 		}
+		memcpy(str, uc_str->uc, uc_len);
 		ret = uc_str->len;
 	}
 out:
@@ -3998,8 +4037,8 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba)
 {
 	size_t utmrdl_size, utrdl_size, ucdl_size;
 
-	/* Allocate memory for UTP command descriptors */
-	ucdl_size = ufshcd_get_ucd_size(hba) * hba->nutrs;
+	/* The reserved tag uses the dedicated UCD below, not this pool. */
+	ucdl_size = ufshcd_get_ucd_size(hba) * (hba->nutrs - UFSHCD_NUM_RESERVED);
 	hba->ucdl_base_addr = dmam_alloc_coherent(hba->dev,
 						  ucdl_size,
 						  &hba->ucdl_dma_addr,
@@ -4015,6 +4054,21 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba)
 		goto out;
 	}
 
+	/* Dedicated UCD for the reserved tag; allocate once (survives MCQ re-init). */
+	if (!hba->devman_ucd_base_addr) {
+		hba->devman_ucd_base_addr =
+			dmam_alloc_coherent(hba->dev,
+					    ufshcd_get_devman_ucd_size(hba),
+					    &hba->devman_ucd_dma_addr,
+					    GFP_KERNEL);
+		if (!hba->devman_ucd_base_addr ||
+		    WARN_ON(hba->devman_ucd_dma_addr & (128 - 1))) {
+			dev_err(hba->dev,
+				"Devman Command Descriptor Memory allocation failed\n");
+			goto out;
+		}
+	}
+
 	/*
 	 * Allocate memory for UTP Transfer descriptors
 	 * UFSHCI requires 1KB alignment of UTRD
@@ -4081,23 +4135,38 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba)
 	dma_addr_t cmd_desc_element_addr;
 	u16 response_offset;
 	u16 prdt_offset;
+	u16 response_len;
 	int cmd_desc_size;
 	int i;
 
 	utrdlp = hba->utrdl_base_addr;
 
-	response_offset =
-		offsetof(struct utp_transfer_cmd_desc, response_upiu);
-	prdt_offset =
-		offsetof(struct utp_transfer_cmd_desc, prd_table);
-
 	cmd_desc_size = ufshcd_get_ucd_size(hba);
 	cmd_desc_dma_addr = hba->ucdl_dma_addr;
 
 	for (i = 0; i < hba->nutrs; i++) {
+		/*
+		 * Reserved tags (low end) use the dedicated devman UCD with a
+		 * larger response area; other tags index the pool at i - RESERVED.
+		 */
+		if (i < UFSHCD_NUM_RESERVED) {
+			cmd_desc_element_addr = hba->devman_ucd_dma_addr;
+			response_offset = offsetof(struct utp_devman_cmd_desc,
+						   response_upiu);
+			prdt_offset = offsetof(struct utp_devman_cmd_desc,
+					       prd_table);
+			response_len = ALIGNED_DEVMAN_RSP_SIZE;
+		} else {
+			cmd_desc_element_addr = cmd_desc_dma_addr +
+				cmd_desc_size * (i - UFSHCD_NUM_RESERVED);
+			response_offset = offsetof(struct utp_transfer_cmd_desc,
+						   response_upiu);
+			prdt_offset = offsetof(struct utp_transfer_cmd_desc,
+					       prd_table);
+			response_len = ALIGNED_UPIU_SIZE;
+		}
+
 		/* Configure UTRD with command descriptor base address */
-		cmd_desc_element_addr =
-				(cmd_desc_dma_addr + (cmd_desc_size * i));
 		utrdlp[i].command_desc_base_addr =
 				cpu_to_le64(cmd_desc_element_addr);
 
@@ -4108,14 +4177,14 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba)
 			utrdlp[i].prd_table_offset =
 				cpu_to_le16(prdt_offset);
 			utrdlp[i].response_upiu_length =
-				cpu_to_le16(ALIGNED_UPIU_SIZE);
+				cpu_to_le16(response_len);
 		} else {
 			utrdlp[i].response_upiu_offset =
 				cpu_to_le16(response_offset >> 2);
 			utrdlp[i].prd_table_offset =
 				cpu_to_le16(prdt_offset >> 2);
 			utrdlp[i].response_upiu_length =
-				cpu_to_le16(ALIGNED_UPIU_SIZE >> 2);
+				cpu_to_le16(response_len >> 2);
 		}
 	}
 }
@@ -4715,7 +4784,9 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba)
 	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
 			&pwr_info->lane_tx);
 
-	if (!pwr_info->lane_rx || !pwr_info->lane_tx) {
+	if (!pwr_info->lane_rx || !pwr_info->lane_tx ||
+	    pwr_info->lane_rx > UFS_MAX_LANES ||
+	    pwr_info->lane_tx > UFS_MAX_LANES) {
 		dev_err(hba->dev, "%s: invalid connected lanes value. rx=%d, tx=%d\n",
 				__func__,
 				pwr_info->lane_rx,
@@ -5846,8 +5917,8 @@ void ufshcd_compl_one_cqe(struct ufs_hba *hba, int task_tag,
 	struct ufshcd_lrb *lrbp = scsi_cmd_priv(cmd);
 	enum utp_ocs ocs;
 
-	if (WARN_ONCE(!cmd, "cqe->command_desc_base_addr = %#llx\n",
-		      le64_to_cpu(cqe->command_desc_base_addr)))
+	if (WARN_ONCE(!cmd, "invalid completion tag %d, cqe->command_desc_base_addr = %#llx\n",
+		      task_tag, cqe ? le64_to_cpu(cqe->command_desc_base_addr) : 0ULL))
 		return;
 
 	if (hba->monitor.enabled) {
@@ -6415,8 +6486,8 @@ static bool ufshcd_wb_curr_buff_threshold_check(struct ufs_hba *hba,
 	}
 
 	if (!cur_buf) {
-		dev_info(hba->dev, "dCurWBBuf: %d WB disabled until free-space is available\n",
-			 cur_buf);
+		dev_warn_once(hba->dev, "dCurWBBuf: %d WB disabled until free-space is available\n",
+			      cur_buf);
 		return false;
 	}
 	/* Let it continue to flush when available buffer exceeds threshold */
@@ -7638,7 +7709,8 @@ static int ufshcd_issue_devman_upiu_cmd(struct ufs_hba *hba,
 
 	/* just copy the upiu response as it is */
 	memcpy(rsp_upiu, lrbp->ucd_rsp_ptr, sizeof(*rsp_upiu));
-	if (desc_buff && desc_op == UPIU_QUERY_OPCODE_READ_DESC) {
+	if (desc_buff && (desc_op == UPIU_QUERY_OPCODE_READ_DESC ||
+			  desc_op == UPIU_QUERY_OPCODE_AGGREGATED_READ)) {
 		u8 *descp = (u8 *)lrbp->ucd_rsp_ptr + sizeof(*rsp_upiu);
 		u16 resp_len = be16_to_cpu(lrbp->ucd_rsp_ptr->header
 					   .data_segment_length);
@@ -7810,10 +7882,7 @@ int ufshcd_advanced_rpmb_req_handler(struct ufs_hba *hba, struct utp_upiu_req *r
 		 * Message is 02h
 		 */
 		if (ehs_len == 2 && rsp_ehs) {
-			/*
-			 * ucd_rsp_ptr points to a buffer with a length of 512 bytes
-			 * (ALIGNED_UPIU_SIZE = 512), and the EHS data just starts from byte32
-			 */
+			/* EHS data starts from byte32 of the devman UCD response area. */
 			ehs_data = (u8 *)lrbp->ucd_rsp_ptr + EHS_OFFSET_IN_RESPONSE;
 			memcpy(rsp_ehs, ehs_data, ehs_len * 32);
 		}
@@ -8611,8 +8680,6 @@ static void ufshcd_set_rtt(struct ufs_hba *hba)
 	struct ufs_dev_info *dev_info = &hba->dev_info;
 	u32 rtt = 0;
 	u32 dev_rtt = 0;
-	int host_rtt_cap = hba->vops && hba->vops->max_num_rtt ?
-			   hba->vops->max_num_rtt : hba->nortt;
 
 	/* RTT override makes sense only for UFS-4.0 and above */
 	if (dev_info->wspecversion < 0x400)
@@ -8628,7 +8695,7 @@ static void ufshcd_set_rtt(struct ufs_hba *hba)
 	if (dev_rtt != DEFAULT_MAX_NUM_RTT)
 		return;
 
-	rtt = min_t(int, dev_info->rtt_cap, host_rtt_cap);
+	rtt = min_t(int, dev_info->rtt_cap, hba->nortt);
 
 	if (rtt == dev_rtt)
 		return;
@@ -9239,7 +9306,7 @@ static void ufshcd_release_sdb_queue(struct ufs_hba *hba, int nutrs)
 {
 	size_t ucdl_size, utrdl_size;
 
-	ucdl_size = ufshcd_get_ucd_size(hba) * nutrs;
+	ucdl_size = ufshcd_get_ucd_size(hba) * (nutrs - UFSHCD_NUM_RESERVED);
 	dmam_free_coherent(hba->dev, ucdl_size, hba->ucdl_base_addr,
 			   hba->ucdl_dma_addr);
 
diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c
index 3991a51263a6..814c1b7343b9 100644
--- a/drivers/ufs/host/ufs-mediatek.c
+++ b/drivers/ufs/host/ufs-mediatek.c
@@ -2183,6 +2183,16 @@ static int ufs_mtk_clk_scale_notify(struct ufs_hba *hba, bool scale_up,
 	return 0;
 }
 
+static int ufs_mtk_get_hba_nortt(struct ufs_hba *hba)
+{
+	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
+
+	if (host->legacy_ip_ver || host->ip_ver < IP_VER_MT6995_B0)
+		return MTK_MAX_NUM_RTT_LEGACY;
+
+	return FIELD_GET(MASK_NUMBER_OUTSTANDING_RTT, hba->capabilities) + 1;
+}
+
 static int ufs_mtk_get_hba_mac(struct ufs_hba *hba)
 {
 	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
@@ -2266,10 +2276,8 @@ static int ufs_mtk_config_mcq_irq(struct ufs_hba *hba)
 
 		dev_dbg(hba->dev, "request irq %d intr %s\n", irq, ret ? "failed" : "");
 
-		if (ret) {
-			dev_err(hba->dev, "Cannot request irq %d\n", ret);
+		if (ret)
 			return ret;
-		}
 	}
 	host->is_mcq_intr_enabled = true;
 
@@ -2322,7 +2330,6 @@ static void ufs_mtk_config_scsi_dev(struct scsi_device *sdev)
  */
 static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = {
 	.name                = "mediatek.ufshci",
-	.max_num_rtt         = MTK_MAX_NUM_RTT,
 	.init                = ufs_mtk_init,
 	.get_ufs_hci_version = ufs_mtk_get_ufs_hci_version,
 	.setup_clocks        = ufs_mtk_setup_clocks,
@@ -2339,6 +2346,7 @@ static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = {
 	.event_notify        = ufs_mtk_event_notify,
 	.config_scaling_param = ufs_mtk_config_scaling_param,
 	.clk_scale_notify    = ufs_mtk_clk_scale_notify,
+	.get_hba_nortt       = ufs_mtk_get_hba_nortt,
 	/* mcq vops */
 	.get_hba_mac         = ufs_mtk_get_hba_mac,
 	.op_runtime_config   = ufs_mtk_op_runtime_config,
diff --git a/drivers/ufs/host/ufs-mediatek.h b/drivers/ufs/host/ufs-mediatek.h
index 8547a6f04990..73cdc726f290 100644
--- a/drivers/ufs/host/ufs-mediatek.h
+++ b/drivers/ufs/host/ufs-mediatek.h
@@ -203,8 +203,8 @@ struct ufs_mtk_host {
 /* MTK delay of autosuspend: 500 ms */
 #define MTK_RPM_AUTOSUSPEND_DELAY_MS 500
 
-/* MTK RTT support number */
-#define MTK_MAX_NUM_RTT 2
+/* MTK RTT support number for platforms before MT6995 B0 */
+#define MTK_MAX_NUM_RTT_LEGACY 2
 
 /* UFSHCI MTK ip version value */
 enum {
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 291c43448764..62396212a0a7 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -2431,8 +2431,6 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba)
 		ret = devm_request_irq(hba->dev, qi[idx].irq, ufs_qcom_mcq_esi_handler,
 				       IRQF_SHARED, "qcom-mcq-esi", qi + idx);
 		if (ret) {
-			dev_err(hba->dev, "%s: Failed to request IRQ for %d, err = %d\n",
-				__func__, qi[idx].irq, ret);
 			/* Free previously allocated IRQs */
 			for (int j = 0; j < idx; j++)
 				devm_free_irq(hba->dev, qi[j].irq, qi + j);
@@ -2794,7 +2792,7 @@ static int ufs_qcom_get_rx_fom(struct ufs_hba *hba,
 	if (ret) {
 		dev_err(hba->dev, "%s: Failed to apply TX EQ settings for HS-G%u: %d\n",
 			__func__, gear, ret);
-		return ret;
+		goto link_recover_and_restore;
 	}
 
 	/* Force PMC to target HS Gear to use new TX Equalization settings. */
@@ -2802,16 +2800,15 @@ static int ufs_qcom_get_rx_fom(struct ufs_hba *hba,
 	if (ret) {
 		dev_err(hba->dev, "%s: Failed to change power mode to HS-G%u, Rate-%s: %d\n",
 			__func__, gear, ufs_hs_rate_to_str(rate), ret);
-		return ret;
+		goto link_recover_and_restore;
 	}
 
 	ret = ufs_qcom_host_sw_rx_fom(hba, pwr_mode->lane_rx, fom);
-	if (ret) {
+	if (ret)
 		dev_err(hba->dev, "Failed to get SW FOM of TX (PreShoot: %u, DeEmphasis: %u): %d\n",
 			d_iter->preshoot, d_iter->deemphasis, ret);
-		return ret;
-	}
 
+link_recover_and_restore:
 	/* Restore Device's TX Equalization settings. */
 	ret = ufshcd_apply_tx_eq_settings(hba, &hba->tx_eq_params[gear - 1], gear);
 	if (ret) {
diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
index c2dafb583cf5..5ac7afe75934 100644
--- a/drivers/ufs/host/ufshcd-pltfrm.c
+++ b/drivers/ufs/host/ufshcd-pltfrm.c
@@ -210,6 +210,158 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
 	}
 }
 
+static int ufshcd_parse_tx_precode_enable(struct ufs_hba *hba,
+					  bool host_precode_en[UFS_MAX_LANES],
+					  bool device_precode_en[UFS_MAX_LANES])
+{
+	const char *prop_name = "tx-precode-enable-g6";
+	u32 num_elems = 2 * hba->lanes_per_direction;
+	const u32 lpd = hba->lanes_per_direction;
+	u32 precode[UFS_MAX_LANES * 2];
+	struct device *dev = hba->dev;
+	int count, err, i;
+
+	count = of_property_count_u32_elems(dev->of_node, prop_name);
+	if (count == -EINVAL || count == -ENODATA)
+		return 0;
+
+	if (count < 0)
+		return count;
+
+	if (count != num_elems) {
+		dev_err(dev, "Property %s has invalid count (%d), expecting %u\n",
+			prop_name, count, num_elems);
+		return -EINVAL;
+	}
+
+	err = of_property_read_u32_array(dev->of_node, prop_name, precode, num_elems);
+	if (err) {
+		dev_err(dev, "Failed to read %s property, %d\n", prop_name, err);
+		return err;
+	}
+
+	for (i = 0; i < num_elems; i++) {
+		if (precode[i] > 1) {
+			dev_err(dev, "Invalid TX precode value (%u) in %s property\n",
+				precode[i], prop_name);
+			return -EINVAL;
+		}
+	}
+
+	for (i = 0; i < lpd; i++) {
+		host_precode_en[i] = precode[i * 2];
+		device_precode_en[i] = precode[i * 2 + 1];
+	}
+
+	return 0;
+}
+
+static int ufshcd_parse_tx_eq_value_array(struct ufs_hba *hba,
+					  const char *prop_name,
+					  const u32 max_value,
+					  u32 values[UFS_MAX_LANES * 2])
+{
+	u32 num_elems = 2 * hba->lanes_per_direction;
+	struct device *dev = hba->dev;
+	int count, err, i;
+
+	count = of_property_count_u32_elems(dev->of_node, prop_name);
+	if (count < 0)
+		return count;
+
+	if (count != num_elems) {
+		dev_err(dev, "Property %s has invalid count (%d), expecting %u\n",
+			prop_name, count, num_elems);
+		return -EINVAL;
+	}
+
+	err = of_property_read_u32_array(dev->of_node, prop_name, values, num_elems);
+	if (err) {
+		dev_err(dev, "Failed to read %s property, %d\n", prop_name, err);
+		return err;
+	}
+
+	for (i = 0; i < num_elems; i++) {
+		if (values[i] >= max_value) {
+			dev_err(dev, "Invalid TX EQ value (%u) in %s property\n",
+				values[i], prop_name);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * ufshcd_parse_tx_eq_settings_for_gear - Parse static TX EQ DT settings for one gear
+ * @hba: per adapter instance
+ * @gear: target HS gear
+ *
+ * Reads the txeq-preshoot-gN, txeq-deemphasis-gN, and (for G6)
+ * tx-precode-enable-g6 device-tree properties.
+ * If all present values are valid, stores them as static TX Equalization
+ * settings for the given gear.
+ */
+static void ufshcd_parse_tx_eq_settings_for_gear(struct ufs_hba *hba, int gear)
+{
+	bool device_precode_en[UFS_MAX_LANES] = { false };
+	bool host_precode_en[UFS_MAX_LANES] = { false };
+	const u32 lpd = hba->lanes_per_direction;
+	struct ufshcd_tx_eq_params *params;
+	u32 deemphasis[UFS_MAX_LANES * 2];
+	u32 preshoot[UFS_MAX_LANES * 2];
+	char prop_name[MAX_PROP_SIZE];
+	int err, lane;
+
+	snprintf(prop_name, MAX_PROP_SIZE, "txeq-preshoot-g%d", gear);
+	err = ufshcd_parse_tx_eq_value_array(hba, prop_name, TX_HS_NUM_PRESHOOT, preshoot);
+	if (err)
+		return;
+
+	snprintf(prop_name, MAX_PROP_SIZE, "txeq-deemphasis-g%d", gear);
+	err = ufshcd_parse_tx_eq_value_array(hba, prop_name, TX_HS_NUM_DEEMPHASIS, deemphasis);
+	if (err)
+		return;
+
+	if (gear == UFS_HS_G6) {
+		err = ufshcd_parse_tx_precode_enable(hba, host_precode_en, device_precode_en);
+		if (err)
+			return;
+	}
+
+	params = &hba->tx_eq_params[gear - 1];
+	for (lane = 0; lane < lpd; lane++) {
+		params->host[lane].preshoot = preshoot[lane * 2];
+		params->host[lane].deemphasis = deemphasis[lane * 2];
+		params->host[lane].precode_en = host_precode_en[lane];
+
+		params->device[lane].preshoot = preshoot[lane * 2 + 1];
+		params->device[lane].deemphasis = deemphasis[lane * 2 + 1];
+		params->device[lane].precode_en = device_precode_en[lane];
+	}
+
+	params->is_valid = true;
+	params->from_dt = true;
+}
+
+static void ufshcd_parse_static_tx_eq_settings(struct ufs_hba *hba)
+{
+	const u32 lpd = hba->lanes_per_direction;
+	int gear;
+
+	if (!lpd)
+		return;
+
+	if (lpd > UFS_MAX_LANES) {
+		dev_warn(hba->dev, "lanes_per_direction (%u) exceeds UFS_MAX_LANES (%u)\n",
+			 lpd, UFS_MAX_LANES);
+		return;
+	}
+
+	for (gear = UFS_HS_G1; gear <= UFS_HS_GEAR_MAX; gear++)
+		ufshcd_parse_tx_eq_settings_for_gear(hba, gear);
+}
+
 /**
  * ufshcd_parse_clock_min_max_freq  - Parse MIN and MAX clocks freq
  * @hba: per adapter instance
@@ -528,6 +680,8 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
 
 	ufshcd_init_lanes_per_dir(hba);
 
+	ufshcd_parse_static_tx_eq_settings(hba);
+
 	err = ufshcd_parse_operating_points(hba);
 	if (err) {
 		dev_err(dev, "%s: OPP parse failed %d\n", __func__, err);
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 3b0c9a251a2e..2673a1bd82c4 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -640,7 +640,11 @@ struct mdio_device_id {
 
 struct zorro_device_id {
 	__u32 id;			/* Device ID or ZORRO_WILDCARD */
-	kernel_ulong_t driver_data;	/* Data private to the driver */
+	union {
+		/* Data private to the driver */
+		kernel_ulong_t driver_data;
+		const void *driver_data_ptr;
+	};
 };
 
 #define ZORRO_WILDCARD			(0xffffffff)	/* not official */
diff --git a/include/uapi/linux/cciss_ioctl.h b/include/uapi/linux/cciss_ioctl.h
index 562230199461..8295f1bf3bf3 100644
--- a/include/uapi/linux/cciss_ioctl.h
+++ b/include/uapi/linux/cciss_ioctl.h
@@ -85,5 +85,6 @@ typedef struct _LogvolInfo_struct{
 #define CCISS_RESCANDISK   _IO(CCISS_IOC_MAGIC, 16)
 #define CCISS_GETLUNINFO   _IOR(CCISS_IOC_MAGIC, 17, LogvolInfo_struct)
 #define CCISS_BIG_PASSTHRU _IOWR(CCISS_IOC_MAGIC, 18, BIG_IOCTL_Command_struct)
+#define CCISS_BIG_PASSTHRU_SUPPORTED _IO(CCISS_IOC_MAGIC, 19)
 
 #endif /* _UAPICCISS_IOCTLH */
diff --git a/include/ufs/ufs.h b/include/ufs/ufs.h
index 0d48e137d66d..afbb32654fab 100644
--- a/include/ufs/ufs.h
+++ b/include/ufs/ufs.h
@@ -25,6 +25,11 @@ static_assert(sizeof(struct utp_upiu_query) == 20);
 
 #define GENERAL_UPIU_REQUEST_SIZE (sizeof(struct utp_upiu_req))
 #define QUERY_DESC_MAX_SIZE       255
+/*
+ * Max aggregated read data segment: the devman response area
+ * (ALIGNED_DEVMAN_RSP_SIZE) minus the fixed UPIU header it follows.
+ */
+#define QUERY_AGGREGATED_MAX_SIZE (4096 - GENERAL_UPIU_REQUEST_SIZE)
 #define QUERY_DESC_MIN_SIZE       2
 #define QUERY_DESC_HDR_SIZE       2
 #define QUERY_OSF_SIZE            (GENERAL_UPIU_REQUEST_SIZE - \
@@ -464,6 +469,7 @@ enum query_opcode {
 	UPIU_QUERY_OPCODE_SET_FLAG	= 0x6,
 	UPIU_QUERY_OPCODE_CLEAR_FLAG	= 0x7,
 	UPIU_QUERY_OPCODE_TOGGLE_FLAG	= 0x8,
+	UPIU_QUERY_OPCODE_AGGREGATED_READ = 0x9,
 };
 
 /* bRefClkFreq attribute values */
diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h
index 248d0a5bef40..dfd302f2dc7c 100644
--- a/include/ufs/ufshcd.h
+++ b/include/ufs/ufshcd.h
@@ -163,7 +163,6 @@ struct ufs_pm_lvl_states {
  * @ucd_req_dma_addr: UPIU request dma address for debug
  * @scsi_status: SCSI status of the command
  * @command_type: SCSI, UFS, Query.
- * @task_tag: Task tag of the command
  * @lun: LUN of the command
  * @intr_cmd: Interrupt command (doesn't participate in interrupt aggregation)
  * @req_abort_skip: skip request abort task flag
@@ -212,8 +211,7 @@ struct ufs_query_req {
 };
 
 /**
- * struct ufs_query_resp - UPIU QUERY
- * @response: device response code
+ * struct ufs_query_res - UPIU QUERY
  * @upiu_res: query response data
  */
 struct ufs_query_res {
@@ -237,11 +235,13 @@ struct ufs_query {
  * @type: device management command type - Query, NOP OUT
  * @lock: lock to allow one command at a time
  * @query: Device management query information
+ * @tag: tag of the reserved request in use
  */
 struct ufs_dev_cmd {
 	enum dev_cmd_type type;
 	struct mutex lock;
 	struct ufs_query query;
+	u8 tag;
 };
 
 /**
@@ -359,6 +359,7 @@ struct ufshcd_tx_eqtr_record {
  * @is_valid: True if parameter contains valid TX Equalization settings
  * @is_applied: True if settings have been applied to UniPro of both sides
  * @is_trained: True if parameters obtained from TX EQTR procedure
+ * @from_dt: True if settings are from Device Tree
  */
 struct ufshcd_tx_eq_params {
 	struct ufshcd_tx_eq_settings host[UFS_MAX_LANES];
@@ -367,12 +368,12 @@ struct ufshcd_tx_eq_params {
 	bool is_valid;
 	bool is_applied;
 	bool is_trained;
+	bool from_dt;
 };
 
 /**
  * struct ufs_hba_variant_ops - variant specific callbacks
  * @name: variant name
- * @max_num_rtt: maximum RTT supported by the host
  * @init: called when the driver is initialized
  * @exit: called to cleanup everything done in init
  * @set_dma_mask: For setting another DMA mask than indicated by the 64AS
@@ -417,10 +418,11 @@ struct ufshcd_tx_eq_params {
  * @get_rx_fom: called to get Figure of Merit (FOM) value.
  * @tx_eqtr_notify: called before and after TX Equalization Training procedure
  *	to allow platform vendor specific configs to take place.
+ * @get_hba_nortt: called to get maximum number of outstanding RTTs supported by
+ *	the controller.
  */
 struct ufs_hba_variant_ops {
 	const char *name;
-	int	max_num_rtt;
 	int	(*init)(struct ufs_hba *);
 	void    (*exit)(struct ufs_hba *);
 	u32	(*get_ufs_hci_version)(struct ufs_hba *);
@@ -479,6 +481,7 @@ struct ufs_hba_variant_ops {
 	int	(*tx_eqtr_notify)(struct ufs_hba *hba,
 				  enum ufs_notify_change_status status,
 				  struct ufs_pa_layer_attr *pwr_mode);
+	int	(*get_hba_nortt)(struct ufs_hba *hba);
 };
 
 /* clock gating state  */
@@ -552,6 +555,7 @@ struct ufs_clk_gating {
  * @is_initialized: Indicates whether clock scaling is initialized or not
  * @is_busy_started: tracks if busy period has started or not
  * @is_suspended: tracks if devfreq is suspended or not
+ * @suspend_on_no_request: suspend clock scaling only when no request
  */
 struct ufs_clk_scaling {
 	struct workqueue_struct *workq;
@@ -952,9 +956,13 @@ enum ufshcd_mcq_opr {
  * @ucdl_base_addr: UFS Command Descriptor base address
  * @utrdl_base_addr: UTP Transfer Request Descriptor base address
  * @utmrdl_base_addr: UTP Task Management Descriptor base address
+ * @devman_ucd_base_addr: UFS Command Descriptor base address for the reserved
+ *	device management tag (has a larger response area)
  * @ucdl_dma_addr: UFS Command Descriptor DMA address
  * @utrdl_dma_addr: UTRDL DMA address
  * @utmrdl_dma_addr: UTMRDL DMA address
+ * @devman_ucd_dma_addr: UFS Command Descriptor DMA address for the reserved
+ *	device management tag
  * @host: Scsi_Host instance of the driver
  * @dev: device handle
  * @ufs_device_wlun: WLUN that controls the entire UFS device.
@@ -973,7 +981,7 @@ enum ufshcd_mcq_opr {
  * @capabilities: UFS Controller Capabilities
  * @mcq_capabilities: UFS Multi Circular Queue capabilities
  * @nutrs: Transfer Request Queue depth supported by controller
- * @nortt - Max outstanding RTTs supported by controller
+ * @nortt: Max outstanding RTTs supported by controller
  * @nutmrs: Task Management Queue depth supported by controller
  * @ufs_version: UFS Version to which controller complies
  * @vops: pointer to variant specific operations
@@ -1035,7 +1043,6 @@ enum ufshcd_mcq_opr {
  *  device is known or not.
  * @wb_mutex: used to serialize devfreq and sysfs write booster toggling
  * @clk_scaling_lock: used to serialize device commands and clock scaling
- * @desc_size: descriptor sizes reported by device
  * @bsg_dev: struct device associated with the BSG queue
  * @bsg_queue: BSG queue associated with the UFS controller
  * @rpm_dev_flush_recheck_work: used to suspend from RPM (runtime power
@@ -1050,16 +1057,19 @@ enum ufshcd_mcq_opr {
  * @debugfs_ee_work: used to restore ee_ctrl_mask after a delay
  * @debugfs_ee_rate_limit_ms: user configurable delay after which to restore
  *	ee_ctrl_mask
+ * @trigger_eh_attr: fault attributes for the fault injection trigger EH
+ * @timeout_attr: fault attributes for the timeout fault injection trigger
  * @luns_avail: number of regular and well known LUNs supported by the UFS
  *	device
  * @nr_hw_queues: number of hardware queues configured
  * @nr_queues: number of Queues of different queue types
  * @complete_put: whether or not to call ufshcd_rpm_put() from inside
  *	ufshcd_resume_complete()
+ * @scsi_host_added: indicates that scsi_add_host() has been called
  * @mcq_sup: is mcq supported by UFSHC
+ * @lsdb_sup: LSDBS capability
  * @mcq_enabled: is mcq ready to accept requests
  * @mcq_esi_enabled: is mcq ESI configured
- * @res: array of resource info of MCQ registers
  * @mcq_base: Multi circular queue registers base address
  * @uhq: array of supported hardware queues
  * @mcq_opr: MCQ operation and runtime registers
@@ -1075,6 +1085,7 @@ enum ufshcd_mcq_opr {
  *	field within the Host Controller's UECDME register. Bit[0] is a flag
  *	indicating that the DME QoS Monitor has been reset by the host.
  * @dme_qos_sysfs_handle: handle for 'dme_qos_notification' sysfs entry
+ * @vcc_off_delay_us: length of delay after VCC is powered off
  * @rpmbs: list of OP-TEE RPMB devices (one per RPMB region)
  * @host_preshoot_cap: a bitfield to indicate supported PreShoot dBs of host's TX lanes, cache of
  *	host M-PHY TX_HS_PreShoot_Setting_Capability Attribute (ID 0x15)
@@ -1093,11 +1104,13 @@ struct ufs_hba {
 	struct utp_transfer_cmd_desc *ucdl_base_addr;
 	struct utp_transfer_req_desc *utrdl_base_addr;
 	struct utp_task_req_desc *utmrdl_base_addr;
+	struct utp_devman_cmd_desc *devman_ucd_base_addr;
 
 	/* DMA memory reference */
 	dma_addr_t ucdl_dma_addr;
 	dma_addr_t utrdl_dma_addr;
 	dma_addr_t utmrdl_dma_addr;
+	dma_addr_t devman_ucd_dma_addr;
 
 	struct Scsi_Host *host;
 	struct device *dev;
@@ -1281,7 +1294,7 @@ struct ufs_hba {
  * @cqe_dma_addr: completion queue dma address
  * @max_entries: max number of slots in this hardware queue
  * @id: hardware queue ID
- * @sq_tp_slot: current slot to which SQ tail pointer is pointing
+ * @sq_tail_slot: current slot to which SQ tail pointer is pointing
  * @sq_lock: serialize submission queue access
  * @cq_tail_slot: current slot to which CQ tail pointer is pointing
  * @cq_head_slot: current slot to which CQ head pointer is pointing
@@ -1356,6 +1369,18 @@ static inline size_t ufshcd_get_ucd_size(const struct ufs_hba *hba)
 	return sizeof(struct utp_transfer_cmd_desc) + SG_ALL * ufshcd_sg_entry_size(hba);
 }
 
+/*
+ * Two entries should be enough for the largest devman PRDT transfer (4 KiB),
+ * like advanced RPMB.
+ */
+#define UFSHCD_DEVMAN_SG_ENTRIES	2
+
+static inline size_t ufshcd_get_devman_ucd_size(const struct ufs_hba *hba)
+{
+	return sizeof(struct utp_devman_cmd_desc) +
+		UFSHCD_DEVMAN_SG_ENTRIES * ufshcd_sg_entry_size(hba);
+}
+
 /* Returns true if clocks can be gated. Otherwise false */
 static inline bool ufshcd_is_clkgating_allowed(struct ufs_hba *hba)
 {
@@ -1505,6 +1530,8 @@ static inline void ufshcd_set_variant(struct ufs_hba *hba, void *variant)
 /**
  * ufshcd_get_variant - get variant specific data from the hba
  * @hba: per adapter instance
+ *
+ * Returns: pointer to the hba's private data area
  */
 static inline void *ufshcd_get_variant(struct ufs_hba *hba)
 {
diff --git a/include/ufs/ufshci.h b/include/ufs/ufshci.h
index 9f0fdd850e54..c3537bb544ae 100644
--- a/include/ufs/ufshci.h
+++ b/include/ufs/ufshci.h
@@ -18,6 +18,8 @@ enum {
 	TASK_REQ_UPIU_SIZE_DWORDS	= 8,
 	TASK_RSP_UPIU_SIZE_DWORDS	= 8,
 	ALIGNED_UPIU_SIZE		= 512,
+	/* Larger response area, only for the devman UCD */
+	ALIGNED_DEVMAN_RSP_SIZE		= 4096,
 };
 
 /* UFSHCI Registers */
@@ -501,6 +503,16 @@ struct utp_transfer_cmd_desc {
 	u8 prd_table[];
 };
 
+/* Dedicated UCD for the devman/reserved slot */
+struct utp_devman_cmd_desc {
+	u8 command_upiu[ALIGNED_UPIU_SIZE];
+	u8 response_upiu[ALIGNED_DEVMAN_RSP_SIZE];
+	u8 prd_table[];
+};
+
+static_assert(sizeof(struct utp_upiu_req) + QUERY_AGGREGATED_MAX_SIZE <=
+	      ALIGNED_DEVMAN_RSP_SIZE);
+
 /**
  * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD
  */
diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c
index 4fe04669e5e6..7e576e5c6b8b 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -979,6 +979,9 @@ size_t dma_max_mapping_size(struct device *dev)
 	const struct dma_map_ops *ops = get_dma_ops(dev);
 	size_t size = SIZE_MAX;
 
+	if (!dev->dma_mask)
+		return 0;
+
 	if (dma_map_direct(dev, ops))
 		size = dma_direct_max_mapping_size(dev);
 	else if (use_dma_iommu(dev))
