summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c2
-rw-r--r--drivers/scsi/FlashPoint.c32
-rw-r--r--drivers/scsi/Kconfig1
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.c25
-rw-r--r--drivers/scsi/be2iscsi/be_main.c5
-rw-r--r--drivers/scsi/bnx2i/bnx2i_iscsi.c32
-rw-r--r--drivers/scsi/cxgbi/cxgb3i/cxgb3i.c1
-rw-r--r--drivers/scsi/cxgbi/cxgb4i/cxgb4i.c1
-rw-r--r--drivers/scsi/cxgbi/libcxgbi.c17
-rw-r--r--drivers/scsi/esas2r/atioctl.h2
-rw-r--r--drivers/scsi/isci/task.c4
-rw-r--r--drivers/scsi/iscsi_tcp.c7
-rw-r--r--drivers/scsi/libiscsi.c229
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.h16
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_base.c96
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fp.c6
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.c20
-rw-r--r--drivers/scsi/mpi3mr/Kconfig7
-rw-r--r--drivers/scsi/mpi3mr/Makefile4
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h1880
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_image.h216
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_init.h159
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_ioc.h1004
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_sas.h33
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_transport.h463
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr.h900
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_debug.h60
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_fw.c3956
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_os.c4049
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.c1
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_scsih.c1
-rw-r--r--drivers/scsi/qedi/qedi.h1
-rw-r--r--drivers/scsi/qedi/qedi_fw.c291
-rw-r--r--drivers/scsi/qedi/qedi_gbl.h3
-rw-r--r--drivers/scsi/qedi/qedi_iscsi.c96
-rw-r--r--drivers/scsi/qedi/qedi_iscsi.h5
-rw-r--r--drivers/scsi/qedi/qedi_main.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_nvme.c2
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c2
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c499
-rw-r--r--drivers/scsi/ufs/ufs-mediatek.c3
-rw-r--r--drivers/scsi/ufs/ufshcd.c8
-rw-r--r--drivers/scsi/ufs/ufshcd.h1
-rw-r--r--include/scsi/libiscsi.h20
-rw-r--r--include/scsi/scsi_transport_iscsi.h14
46 files changed, 13608 insertions, 569 deletions
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 8fcaa1136f2c..776e46ee95da 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -506,6 +506,7 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
iser_conn->iscsi_conn = conn;
out:
+ iscsi_put_endpoint(ep);
mutex_unlock(&iser_conn->state_mutex);
return error;
}
@@ -1002,6 +1003,7 @@ static struct iscsi_transport iscsi_iser_transport = {
/* connection management */
.create_conn = iscsi_iser_conn_create,
.bind_conn = iscsi_iser_conn_bind,
+ .unbind_conn = iscsi_conn_unbind,
.destroy_conn = iscsi_conn_teardown,
.attr_is_visible = iser_attr_is_visible,
.set_param = iscsi_iser_set_param,
diff --git a/drivers/scsi/FlashPoint.c b/drivers/scsi/FlashPoint.c
index ee9e4028c0f3..90253208a72f 100644
--- a/drivers/scsi/FlashPoint.c
+++ b/drivers/scsi/FlashPoint.c
@@ -40,7 +40,7 @@ struct sccb_mgr_info {
u16 si_per_targ_ultra_nego;
u16 si_per_targ_no_disc;
u16 si_per_targ_wide_nego;
- u16 si_flags;
+ u16 si_mflags;
unsigned char si_card_family;
unsigned char si_bustype;
unsigned char si_card_model[3];
@@ -1045,22 +1045,22 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo)
ScamFlg =
(unsigned char)FPT_utilEERead(ioport, SCAM_CONFIG / 2);
- pCardInfo->si_flags = 0x0000;
+ pCardInfo->si_mflags = 0x0000;
if (i & 0x01)
- pCardInfo->si_flags |= SCSI_PARITY_ENA;
+ pCardInfo->si_mflags |= SCSI_PARITY_ENA;
if (!(i & 0x02))
- pCardInfo->si_flags |= SOFT_RESET;
+ pCardInfo->si_mflags |= SOFT_RESET;
if (i & 0x10)
- pCardInfo->si_flags |= EXTENDED_TRANSLATION;
+ pCardInfo->si_mflags |= EXTENDED_TRANSLATION;
if (ScamFlg & SCAM_ENABLED)
- pCardInfo->si_flags |= FLAG_SCAM_ENABLED;
+ pCardInfo->si_mflags |= FLAG_SCAM_ENABLED;
if (ScamFlg & SCAM_LEVEL2)
- pCardInfo->si_flags |= FLAG_SCAM_LEVEL2;
+ pCardInfo->si_mflags |= FLAG_SCAM_LEVEL2;
j = (RD_HARPOON(ioport + hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
if (i & 0x04) {
@@ -1076,7 +1076,7 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo)
if (!(RD_HARPOON(ioport + hp_page_ctrl) & NARROW_SCSI_CARD))
- pCardInfo->si_flags |= SUPPORT_16TAR_32LUN;
+ pCardInfo->si_mflags |= SUPPORT_16TAR_32LUN;
pCardInfo->si_card_family = HARPOON_FAMILY;
pCardInfo->si_bustype = BUSTYPE_PCI;
@@ -1112,15 +1112,15 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo)
if (pCardInfo->si_card_model[1] == '3') {
if (RD_HARPOON(ioport + hp_ee_ctrl) & BIT(7))
- pCardInfo->si_flags |= LOW_BYTE_TERM;
+ pCardInfo->si_mflags |= LOW_BYTE_TERM;
} else if (pCardInfo->si_card_model[2] == '0') {
temp = RD_HARPOON(ioport + hp_xfer_pad);
WR_HARPOON(ioport + hp_xfer_pad, (temp & ~BIT(4)));
if (RD_HARPOON(ioport + hp_ee_ctrl) & BIT(7))
- pCardInfo->si_flags |= LOW_BYTE_TERM;
+ pCardInfo->si_mflags |= LOW_BYTE_TERM;
WR_HARPOON(ioport + hp_xfer_pad, (temp | BIT(4)));
if (RD_HARPOON(ioport + hp_ee_ctrl) & BIT(7))
- pCardInfo->si_flags |= HIGH_BYTE_TERM;
+ pCardInfo->si_mflags |= HIGH_BYTE_TERM;
WR_HARPOON(ioport + hp_xfer_pad, temp);
} else {
temp = RD_HARPOON(ioport + hp_ee_ctrl);
@@ -1138,9 +1138,9 @@ static int FlashPoint_ProbeHostAdapter(struct sccb_mgr_info *pCardInfo)
WR_HARPOON(ioport + hp_ee_ctrl, temp);
WR_HARPOON(ioport + hp_xfer_pad, temp2);
if (!(temp3 & BIT(7)))
- pCardInfo->si_flags |= LOW_BYTE_TERM;
+ pCardInfo->si_mflags |= LOW_BYTE_TERM;
if (!(temp3 & BIT(6)))
- pCardInfo->si_flags |= HIGH_BYTE_TERM;
+ pCardInfo->si_mflags |= HIGH_BYTE_TERM;
}
ARAM_ACCESS(ioport);
@@ -1247,7 +1247,7 @@ static void *FlashPoint_HardwareResetHostAdapter(struct sccb_mgr_info
WR_HARPOON(ioport + hp_arb_id, pCardInfo->si_id);
CurrCard->ourId = pCardInfo->si_id;
- i = (unsigned char)pCardInfo->si_flags;
+ i = (unsigned char)pCardInfo->si_mflags;
if (i & SCSI_PARITY_ENA)
WR_HARPOON(ioport + hp_portctrl_1, (HOST_MODE8 | CHK_SCSI_P));
@@ -1261,14 +1261,14 @@ static void *FlashPoint_HardwareResetHostAdapter(struct sccb_mgr_info
j |= SCSI_TERM_ENA_H;
WR_HARPOON(ioport + hp_ee_ctrl, j);
- if (!(pCardInfo->si_flags & SOFT_RESET)) {
+ if (!(pCardInfo->si_mflags & SOFT_RESET)) {
FPT_sresb(ioport, thisCard);
FPT_scini(thisCard, pCardInfo->si_id, 0);
}
- if (pCardInfo->si_flags & POST_ALL_UNDERRRUNS)
+ if (pCardInfo->si_mflags & POST_ALL_UNDERRRUNS)
CurrCard->globalFlags |= F_NO_FILTER;
if (pCurrNvRam) {
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index c5612896cdb9..23e015af0e93 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -482,6 +482,7 @@ config SCSI_ARCMSR
source "drivers/scsi/esas2r/Kconfig"
source "drivers/scsi/megaraid/Kconfig.megaraid"
source "drivers/scsi/mpt3sas/Kconfig"
+source "drivers/scsi/mpi3mr/Kconfig"
source "drivers/scsi/smartpqi/Kconfig"
source "drivers/scsi/ufs/Kconfig"
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index bc3882f5cc69..06f2d5fab899 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -99,6 +99,7 @@ obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o
obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/
obj-$(CONFIG_MEGARAID_SAS) += megaraid/
obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas/
+obj-$(CONFIG_SCSI_MPI3MR) += mpi3mr/
obj-$(CONFIG_SCSI_UFSHCD) += ufs/
obj-$(CONFIG_SCSI_ACARD) += atp870u.o
obj-$(CONFIG_SCSI_SUNESP) += esp_scsi.o sun_esp.o
diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c
index 0e935c49b57b..8aeaddc93b16 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.c
+++ b/drivers/scsi/be2iscsi/be_iscsi.c
@@ -182,6 +182,7 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session,
struct beiscsi_endpoint *beiscsi_ep;
struct iscsi_endpoint *ep;
uint16_t cri_index;
+ int rc = 0;
ep = iscsi_lookup_endpoint(transport_fd);
if (!ep)
@@ -189,15 +190,17 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session,
beiscsi_ep = ep->dd_data;
- if (iscsi_conn_bind(cls_session, cls_conn, is_leading))
- return -EINVAL;
+ if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) {
+ rc = -EINVAL;
+ goto put_ep;
+ }
if (beiscsi_ep->phba != phba) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
"BS_%d : beiscsi_ep->hba=%p not equal to phba=%p\n",
beiscsi_ep->phba, phba);
-
- return -EEXIST;
+ rc = -EEXIST;
+ goto put_ep;
}
cri_index = BE_GET_CRI_FROM_CID(beiscsi_ep->ep_cid);
if (phba->conn_table[cri_index]) {
@@ -209,7 +212,8 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session,
beiscsi_ep->ep_cid,
beiscsi_conn,
phba->conn_table[cri_index]);
- return -EINVAL;
+ rc = -EINVAL;
+ goto put_ep;
}
}
@@ -226,7 +230,10 @@ int beiscsi_conn_bind(struct iscsi_cls_session *cls_session,
"BS_%d : cid %d phba->conn_table[%u]=%p\n",
beiscsi_ep->ep_cid, cri_index, beiscsi_conn);
phba->conn_table[cri_index] = beiscsi_conn;
- return 0;
+
+put_ep:
+ iscsi_put_endpoint(ep);
+ return rc;
}
static int beiscsi_iface_create_ipv4(struct beiscsi_hba *phba)
@@ -1293,7 +1300,6 @@ static int beiscsi_conn_close(struct beiscsi_endpoint *beiscsi_ep)
void beiscsi_ep_disconnect(struct iscsi_endpoint *ep)
{
struct beiscsi_endpoint *beiscsi_ep;
- struct beiscsi_conn *beiscsi_conn;
struct beiscsi_hba *phba;
uint16_t cri_index;
@@ -1312,11 +1318,6 @@ void beiscsi_ep_disconnect(struct iscsi_endpoint *ep)
return;
}
- if (beiscsi_ep->conn) {
- beiscsi_conn = beiscsi_ep->conn;
- iscsi_suspend_queue(beiscsi_conn->conn);
- }
-
if (!beiscsi_hba_is_online(phba)) {
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
"BS_%d : HBA in error 0x%lx\n", phba->state);
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index c15cc6c164d9..310b801c6c87 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -416,7 +416,7 @@ static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev)
"beiscsi_hba_alloc - iscsi_host_alloc failed\n");
return NULL;
}
- shost->max_id = BE2_MAX_SESSIONS;
+ shost->max_id = BE2_MAX_SESSIONS - 1;
shost->max_channel = 0;
shost->max_cmd_len = BEISCSI_MAX_CMD_LEN;
shost->max_lun = BEISCSI_NUM_MAX_LUN;
@@ -5316,7 +5316,7 @@ static int beiscsi_enable_port(struct beiscsi_hba *phba)
/* Re-enable UER. If different TPE occurs then it is recoverable. */
beiscsi_set_uer_feature(phba);
- phba->shost->max_id = phba->params.cxns_per_ctrl;
+ phba->shost->max_id = phba->params.cxns_per_ctrl - 1;
phba->shost->can_queue = phba->params.ios_per_ctrl;
ret = beiscsi_init_port(phba);
if (ret < 0) {
@@ -5807,6 +5807,7 @@ struct iscsi_transport beiscsi_iscsi_transport = {
.destroy_session = beiscsi_session_destroy,
.create_conn = beiscsi_conn_create,
.bind_conn = beiscsi_conn_bind,
+ .unbind_conn = iscsi_conn_unbind,
.destroy_conn = iscsi_conn_teardown,
.attr_is_visible = beiscsi_attr_is_visible,
.set_iface_param = beiscsi_iface_set_param,
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
index 1e6d8f62ea3c..1b5f3e143f07 100644
--- a/drivers/scsi/bnx2i/bnx2i_iscsi.c
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -791,7 +791,7 @@ struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic)
return NULL;
shost->dma_boundary = cnic->pcidev->dma_mask;
shost->transportt = bnx2i_scsi_xport_template;
- shost->max_id = ISCSI_MAX_CONNS_PER_HBA;
+ shost->max_id = ISCSI_MAX_CONNS_PER_HBA - 1;
shost->max_channel = 0;
shost->max_lun = 512;
shost->max_cmd_len = 16;
@@ -1420,17 +1420,23 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
* Forcefully terminate all in progress connection recovery at the
* earliest, either in bind(), send_pdu(LOGIN), or conn_start()
*/
- if (bnx2i_adapter_ready(hba))
- return -EIO;
+ if (bnx2i_adapter_ready(hba)) {
+ ret_code = -EIO;
+ goto put_ep;
+ }
bnx2i_ep = ep->dd_data;
if ((bnx2i_ep->state == EP_STATE_TCP_FIN_RCVD) ||
- (bnx2i_ep->state == EP_STATE_TCP_RST_RCVD))
+ (bnx2i_ep->state == EP_STATE_TCP_RST_RCVD)) {
/* Peer disconnect via' FIN or RST */
- return -EINVAL;
+ ret_code = -EINVAL;
+ goto put_ep;
+ }
- if (iscsi_conn_bind(cls_session, cls_conn, is_leading))
- return -EINVAL;
+ if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) {
+ ret_code = -EINVAL;
+ goto put_ep;
+ }
if (bnx2i_ep->hba != hba) {
/* Error - TCP connection does not belong to this device
@@ -1441,7 +1447,8 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
iscsi_conn_printk(KERN_ALERT, cls_conn->dd_data,
"belong to hba (%s)\n",
hba->netdev->name);
- return -EEXIST;
+ ret_code = -EEXIST;
+ goto put_ep;
}
bnx2i_ep->conn = bnx2i_conn;
bnx2i_conn->ep = bnx2i_ep;
@@ -1458,6 +1465,8 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
bnx2i_put_rq_buf(bnx2i_conn, 0);
bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, CNIC_ARM_CQE);
+put_ep:
+ iscsi_put_endpoint(ep);
return ret_code;
}
@@ -2113,7 +2122,6 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep)
{
struct bnx2i_endpoint *bnx2i_ep;
struct bnx2i_conn *bnx2i_conn = NULL;
- struct iscsi_conn *conn = NULL;
struct bnx2i_hba *hba;
bnx2i_ep = ep->dd_data;
@@ -2126,11 +2134,8 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep)
!time_after(jiffies, bnx2i_ep->timestamp + (12 * HZ)))
msleep(250);
- if (bnx2i_ep->conn) {
+ if (bnx2i_ep->conn)
bnx2i_conn = bnx2i_ep->conn;
- conn = bnx2i_conn->cls_conn->dd_data;
- iscsi_suspend_queue(conn);
- }
hba = bnx2i_ep->hba;
mutex_lock(&hba->net_dev_lock);
@@ -2276,6 +2281,7 @@ struct iscsi_transport bnx2i_iscsi_transport = {
.destroy_session = bnx2i_session_destroy,
.create_conn = bnx2i_conn_create,
.bind_conn = bnx2i_conn_bind,
+ .unbind_conn = iscsi_conn_unbind,
.destroy_conn = bnx2i_conn_destroy,
.attr_is_visible = bnx2i_attr_is_visible,
.set_param = iscsi_set_param,
diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
index 203f938fca7e..f949a4e00783 100644
--- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
+++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
@@ -117,6 +117,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = {
/* connection management */
.create_conn = cxgbi_create_conn,
.bind_conn = cxgbi_bind_conn,
+ .unbind_conn = iscsi_conn_unbind,
.destroy_conn = iscsi_tcp_conn_teardown,
.start_conn = iscsi_conn_start,
.stop_conn = iscsi_conn_stop,
diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
index 2c3491528d42..efb3e2b3398e 100644
--- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
+++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
@@ -134,6 +134,7 @@ static struct iscsi_transport cxgb4i_iscsi_transport = {
/* connection management */
.create_conn = cxgbi_create_conn,
.bind_conn = cxgbi_bind_conn,
+ .unbind_conn = iscsi_conn_unbind,
.destroy_conn = iscsi_tcp_conn_teardown,
.start_conn = iscsi_conn_start,
.stop_conn = iscsi_conn_stop,
diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c
index f078b3c4e083..8c7d4dda4cf2 100644
--- a/drivers/scsi/cxgbi/libcxgbi.c
+++ b/drivers/scsi/cxgbi/libcxgbi.c
@@ -337,7 +337,7 @@ void cxgbi_hbas_remove(struct cxgbi_device *cdev)
EXPORT_SYMBOL_GPL(cxgbi_hbas_remove);
int cxgbi_hbas_add(struct cxgbi_device *cdev, u64 max_lun,
- unsigned int max_id, struct scsi_host_template *sht,
+ unsigned int max_conns, struct scsi_host_template *sht,
struct scsi_transport_template *stt)
{
struct cxgbi_hba *chba;
@@ -357,7 +357,7 @@ int cxgbi_hbas_add(struct cxgbi_device *cdev, u64 max_lun,
shost->transportt = stt;
shost->max_lun = max_lun;
- shost->max_id = max_id;
+ shost->max_id = max_conns - 1;
shost->max_channel = 0;
shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE;
@@ -2690,11 +2690,13 @@ int cxgbi_bind_conn(struct iscsi_cls_session *cls_session,
err = csk->cdev->csk_ddp_setup_pgidx(csk, csk->tid,
ppm->tformat.pgsz_idx_dflt);
if (err < 0)
- return err;
+ goto put_ep;
err = iscsi_conn_bind(cls_session, cls_conn, is_leading);
- if (err)
- return -EINVAL;
+ if (err) {
+ err = -EINVAL;
+ goto put_ep;
+ }
/* calculate the tag idx bits needed for this conn based on cmds_max */
cconn->task_idx_bits = (__ilog2_u32(conn->session->cmds_max - 1)) + 1;
@@ -2715,7 +2717,9 @@ int cxgbi_bind_conn(struct iscsi_cls_session *cls_session,
/* init recv engine */
iscsi_tcp_hdr_recv_prep(tcp_conn);
- return 0;
+put_ep:
+ iscsi_put_endpoint(ep);
+ return err;
}
EXPORT_SYMBOL_GPL(cxgbi_bind_conn);
@@ -2968,7 +2972,6 @@ void cxgbi_ep_disconnect(struct iscsi_endpoint *ep)
ep, cep, cconn, csk, csk->state, csk->flags);
if (cconn && cconn->iconn) {
- iscsi_suspend_tx(cconn->iconn);
write_lock_bh(&csk->callback_lock);
cep->csk->user_data = NULL;
cconn->cep = NULL;
diff --git a/drivers/scsi/esas2r/atioctl.h b/drivers/scsi/esas2r/atioctl.h
index 4aca3d52c851..ff2ad9b38575 100644
--- a/drivers/scsi/esas2r/atioctl.h
+++ b/drivers/scsi/esas2r/atioctl.h
@@ -1141,7 +1141,7 @@ struct __packed atto_ioctl_vda_gsv_cmd {
u8 rsp_len;
u8 reserved[7];
- u8 version_info[1];
+ u8 version_info[];
#define ATTO_VDA_VER_UNSUPPORTED 0xFF
};
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index 62062ed6cd9a..eeaec26ac324 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -709,8 +709,8 @@ isci_task_request_complete(struct isci_host *ihost,
tmf->status = completion_status;
if (tmf->proto == SAS_PROTOCOL_SSP) {
- memcpy(&tmf->resp.resp_iu,
- &ireq->ssp.rsp,
+ memcpy(tmf->resp.rsp_buf,
+ ireq->ssp.rsp_buf,
SSP_RESP_IU_MAX_SIZE);
} else if (tmf->proto == SAS_PROTOCOL_SATA) {
memcpy(&tmf->resp.d2h_fis,
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index dd33ce0e3737..1bc37593c88f 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -600,6 +600,12 @@ static void iscsi_sw_tcp_release_conn(struct iscsi_conn *conn)
if (!sock)
return;
+ /*
+ * Make sure we start socket shutdown now in case userspace is up
+ * but delayed in releasing the socket.
+ */
+ kernel_sock_shutdown(sock, SHUT_RDWR);
+
sock_hold(sock->sk);
iscsi_sw_tcp_conn_restore_callbacks(conn);
sock_put(sock->sk);
@@ -689,6 +695,7 @@ iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session,
sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */
sk->sk_allocation = GFP_ATOMIC;
sk_set_memalloc(sk);
+ sock_no_linger(sk);
iscsi_sw_tcp_conn_set_callbacks(conn);
tcp_sw_conn->sendpage = tcp_sw_conn->sock->ops->sendpage;
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 9e69422cb77a..4683c183e9d4 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -230,11 +230,11 @@ static int iscsi_prep_ecdb_ahs(struct iscsi_task *task)
*/
static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode)
{
- struct iscsi_conn *conn = task->conn;
- struct iscsi_tm *tmf = &conn->tmhdr;
+ struct iscsi_session *session = task->conn->session;
+ struct iscsi_tm *tmf = &session->tmhdr;
u64 hdr_lun;
- if (conn->tmf_state == TMF_INITIAL)
+ if (session->tmf_state == TMF_INITIAL)
return 0;
if ((tmf->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_SCSI_TMFUNC)
@@ -254,24 +254,19 @@ static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode)
* Fail all SCSI cmd PDUs
*/
if (opcode != ISCSI_OP_SCSI_DATA_OUT) {
- iscsi_conn_printk(KERN_INFO, conn,
- "task [op %x itt "
- "0x%x/0x%x] "
- "rejected.\n",
- opcode, task->itt,
- task->hdr_itt);
+ iscsi_session_printk(KERN_INFO, session,
+ "task [op %x itt 0x%x/0x%x] rejected.\n",
+ opcode, task->itt, task->hdr_itt);
return -EACCES;
}
/*
* And also all data-out PDUs in response to R2T
* if fast_abort is set.
*/
- if (conn->session->fast_abort) {
- iscsi_conn_printk(KERN_INFO, conn,
- "task [op %x itt "
- "0x%x/0x%x] fast abort.\n",
- opcode, task->itt,
- task->hdr_itt);
+ if (session->fast_abort) {
+ iscsi_session_printk(KERN_INFO, session,
+ "task [op %x itt 0x%x/0x%x] fast abort.\n",
+ opcode, task->itt, task->hdr_itt);
return -EACCES;
}
break;
@@ -284,7 +279,7 @@ static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode)
*/
if (opcode == ISCSI_OP_SCSI_DATA_OUT &&
task->hdr_itt == tmf->rtt) {
- ISCSI_DBG_SESSION(conn->session,
+ ISCSI_DBG_SESSION(session,
"Preventing task %x/%x from sending "
"data-out due to abort task in "
"progress\n", task->itt,
@@ -578,6 +573,11 @@ static bool cleanup_queued_task(struct iscsi_task *task)
__iscsi_put_task(task);
}
+ if (conn->session->running_aborted_task == task) {
+ conn->session->running_aborted_task = NULL;
+ __iscsi_put_task(task);
+ }
+
if (conn->task == task) {
conn->task = NULL;
__iscsi_put_task(task);
@@ -933,20 +933,21 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
{
struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr;
+ struct iscsi_session *session = conn->session;
conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
conn->tmfrsp_pdus_cnt++;
- if (conn->tmf_state != TMF_QUEUED)
+ if (session->tmf_state != TMF_QUEUED)
return;
if (tmf->response == ISCSI_TMF_RSP_COMPLETE)
- conn->tmf_state = TMF_SUCCESS;
+ session->tmf_state = TMF_SUCCESS;
else if (tmf->response == ISCSI_TMF_RSP_NO_TASK)
- conn->tmf_state = TMF_NOT_FOUND;
+ session->tmf_state = TMF_NOT_FOUND;
else
- conn->tmf_state = TMF_FAILED;
- wake_up(&conn->ehwait);
+ session->tmf_state = TMF_FAILED;
+ wake_up(&session->ehwait);
}
static int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
@@ -1358,7 +1359,6 @@ void iscsi_session_failure(struct iscsi_session *session,
enum iscsi_err err)
{
struct iscsi_conn *conn;
- struct device *dev;
spin_lock_bh(&session->frwd_lock);
conn = session->leadconn;
@@ -1367,10 +1367,8 @@ void iscsi_session_failure(struct iscsi_session *session,
return;
}
- dev = get_device(&conn->cls_conn->dev);
+ iscsi_get_conn(conn->cls_conn);
spin_unlock_bh(&session->frwd_lock);
- if (!dev)
- return;
/*
* if the host is being removed bypass the connection
* recovery initialization because we are going to kill
@@ -1380,27 +1378,36 @@ void iscsi_session_failure(struct iscsi_session *session,
iscsi_conn_error_event(conn->cls_conn, err);
else
iscsi_conn_failure(conn, err);
- put_device(dev);
+ iscsi_put_conn(conn->cls_conn);
}
EXPORT_SYMBOL_GPL(iscsi_session_failure);
-void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
+static bool iscsi_set_conn_failed(struct iscsi_conn *conn)
{
struct iscsi_session *session = conn->session;
- spin_lock_bh(&session->frwd_lock);
- if (session->state == ISCSI_STATE_FAILED) {
- spin_unlock_bh(&session->frwd_lock);
- return;
- }
+ if (session->state == ISCSI_STATE_FAILED)
+ return false;
if (conn->stop_stage == 0)
session->state = ISCSI_STATE_FAILED;
- spin_unlock_bh(&session->frwd_lock);
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
- iscsi_conn_error_event(conn->cls_conn, err);
+ return true;
+}
+
+void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
+{
+ struct iscsi_session *session = conn->session;
+ bool needs_evt;
+
+ spin_lock_bh(&session->frwd_lock);
+ needs_evt = iscsi_set_conn_failed(conn);
+ spin_unlock_bh(&session->frwd_lock);
+
+ if (needs_evt)
+ iscsi_conn_error_event(conn->cls_conn, err);
}
EXPORT_SYMBOL_GPL(iscsi_conn_failure);
@@ -1817,15 +1824,14 @@ EXPORT_SYMBOL_GPL(iscsi_target_alloc);
static void iscsi_tmf_timedout(struct timer_list *t)
{
- struct iscsi_conn *conn = from_timer(conn, t, tmf_timer);
- struct iscsi_session *session = conn->session;
+ struct iscsi_session *session = from_timer(session, t, tmf_timer);
spin_lock(&session->frwd_lock);
- if (conn->tmf_state == TMF_QUEUED) {
- conn->tmf_state = TMF_TIMEDOUT;
+ if (session->tmf_state == TMF_QUEUED) {
+ session->tmf_state = TMF_TIMEDOUT;
ISCSI_DBG_EH(session, "tmf timedout\n");
/* unblock eh_abort() */
- wake_up(&conn->ehwait);
+ wake_up(&session->ehwait);
}
spin_unlock(&session->frwd_lock);
}
@@ -1848,8 +1854,8 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
return -EPERM;
}
conn->tmfcmd_pdus_cnt++;
- conn->tmf_timer.expires = timeout * HZ + jiffies;
- add_timer(&conn->tmf_timer);
+ session->tmf_timer.expires = timeout * HZ + jiffies;
+ add_timer(&session->tmf_timer);
ISCSI_DBG_EH(session, "tmf set timeout\n");
spin_unlock_bh(&session->frwd_lock);
@@ -1863,12 +1869,12 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
* 3) session is terminated or restarted or userspace has
* given up on recovery
*/
- wait_event_interruptible(conn->ehwait, age != session->age ||
+ wait_event_interruptible(session->ehwait, age != session->age ||
session->state != ISCSI_STATE_LOGGED_IN ||
- conn->tmf_state != TMF_QUEUED);
+ session->tmf_state != TMF_QUEUED);
if (signal_pending(current))
flush_signals(current);
- del_timer_sync(&conn->tmf_timer);
+ del_timer_sync(&session->tmf_timer);
mutex_lock(&session->eh_mutex);
spin_lock_bh(&session->frwd_lock);
@@ -2177,6 +2183,51 @@ done:
spin_unlock(&session->frwd_lock);
}
+/**
+ * iscsi_conn_unbind - prevent queueing to conn.
+ * @cls_conn: iscsi conn ep is bound to.
+ * @is_active: is the conn in use for boot or is this for EH/termination
+ *
+ * This must be called by drivers implementing the ep_disconnect callout.
+ * It disables queueing to the connection from libiscsi in preparation for
+ * an ep_disconnect call.
+ */
+void iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active)
+{
+ struct iscsi_session *session;
+ struct iscsi_conn *conn;
+
+ if (!cls_conn)
+ return;
+
+ conn = cls_conn->dd_data;
+ session = conn->session;
+ /*
+ * Wait for iscsi_eh calls to exit. We don't wait for the tmf to
+ * complete or timeout. The caller just wants to know what's running
+ * is everything that needs to be cleaned up, and no cmds will be
+ * queued.
+ */
+ mutex_lock(&session->eh_mutex);
+
+ iscsi_suspend_queue(conn);
+ iscsi_suspend_tx(conn);
+
+ spin_lock_bh(&session->frwd_lock);
+ if (!is_active) {
+ /*
+ * if logout timed out before userspace could even send a PDU
+ * the state might still be in ISCSI_STATE_LOGGED_IN and
+ * allowing new cmds and TMFs.
+ */
+ if (session->state == ISCSI_STATE_LOGGED_IN)
+ iscsi_set_conn_failed(conn);
+ }
+ spin_unlock_bh(&session->frwd_lock);
+ mutex_unlock(&session->eh_mutex);
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_unbind);
+
static void iscsi_prep_abort_task_pdu(struct iscsi_task *task,
struct iscsi_tm *hdr)
{
@@ -2231,6 +2282,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
}
conn = session->leadconn;
+ iscsi_get_conn(conn->cls_conn);
conn->eh_abort_cnt++;
age = session->age;
@@ -2241,9 +2293,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
ISCSI_DBG_EH(session, "sc completed while abort in progress\n");
spin_unlock(&session->back_lock);
- spin_unlock_bh(&session->frwd_lock);
- mutex_unlock(&session->eh_mutex);
- return SUCCESS;
+ goto success;
}
ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n", sc, task->itt);
__iscsi_get_task(task);
@@ -2255,17 +2305,17 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
}
/* only have one tmf outstanding at a time */
- if (conn->tmf_state != TMF_INITIAL)
+ if (session->tmf_state != TMF_INITIAL)
goto failed;
- conn->tmf_state = TMF_QUEUED;
+ session->tmf_state = TMF_QUEUED;
- hdr = &conn->tmhdr;
+ hdr = &session->tmhdr;
iscsi_prep_abort_task_pdu(task, hdr);
if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout))
goto failed;
- switch (conn->tmf_state) {
+ switch (session->tmf_state) {
case TMF_SUCCESS:
spin_unlock_bh(&session->frwd_lock);
/*
@@ -2280,18 +2330,19 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
*/
spin_lock_bh(&session->frwd_lock);
fail_scsi_task(task, DID_ABORT);
- conn->tmf_state = TMF_INITIAL;
+ session->tmf_state = TMF_INITIAL;
memset(hdr, 0, sizeof(*hdr));
spin_unlock_bh(&session->frwd_lock);
iscsi_start_tx(conn);
goto success_unlocked;
case TMF_TIMEDOUT:
+ session->running_aborted_task = task;
spin_unlock_bh(&session->frwd_lock);
iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST);
goto failed_unlocked;
case TMF_NOT_FOUND:
- if (!sc->SCp.ptr) {
- conn->tmf_state = TMF_INITIAL;
+ if (iscsi_task_is_completed(task)) {
+ session->tmf_state = TMF_INITIAL;
memset(hdr, 0, sizeof(*hdr));
/* task completed before tmf abort response */
ISCSI_DBG_EH(session, "sc completed while abort in "
@@ -2300,7 +2351,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
}
fallthrough;
default:
- conn->tmf_state = TMF_INITIAL;
+ session->tmf_state = TMF_INITIAL;
goto failed;
}
@@ -2310,6 +2361,7 @@ success_unlocked:
ISCSI_DBG_EH(session, "abort success [sc %p itt 0x%x]\n",
sc, task->itt);
iscsi_put_task(task);
+ iscsi_put_conn(conn->cls_conn);
mutex_unlock(&session->eh_mutex);
return SUCCESS;
@@ -2318,7 +2370,15 @@ failed:
failed_unlocked:
ISCSI_DBG_EH(session, "abort failed [sc %p itt 0x%x]\n", sc,
task ? task->itt : 0);
- iscsi_put_task(task);
+ /*
+ * The driver might be accessing the task so hold the ref. The conn
+ * stop cleanup will drop the ref after ep_disconnect so we know the
+ * driver's no longer touching the task.
+ */
+ if (!session->running_aborted_task)
+ iscsi_put_task(task);
+
+ iscsi_put_conn(conn->cls_conn);
mutex_unlock(&session->eh_mutex);
return FAILED;
}
@@ -2359,11 +2419,11 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
conn = session->leadconn;
/* only have one tmf outstanding at a time */
- if (conn->tmf_state != TMF_INITIAL)
+ if (session->tmf_state != TMF_INITIAL)
goto unlock;
- conn->tmf_state = TMF_QUEUED;
+ session->tmf_state = TMF_QUEUED;
- hdr = &conn->tmhdr;
+ hdr = &session->tmhdr;
iscsi_prep_lun_reset_pdu(sc, hdr);
if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age,
@@ -2372,7 +2432,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
goto unlock;
}
- switch (conn->tmf_state) {
+ switch (session->tmf_state) {
case TMF_SUCCESS:
break;
case TMF_TIMEDOUT:
@@ -2380,7 +2440,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST);
goto done;
default:
- conn->tmf_state = TMF_INITIAL;
+ session->tmf_state = TMF_INITIAL;
goto unlock;
}
@@ -2392,7 +2452,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
spin_lock_bh(&session->frwd_lock);
memset(hdr, 0, sizeof(*hdr));
fail_scsi_tasks(conn, sc->device->lun, DID_ERROR);
- conn->tmf_state = TMF_INITIAL;
+ session->tmf_state = TMF_INITIAL;
spin_unlock_bh(&session->frwd_lock);
iscsi_start_tx(conn);
@@ -2415,8 +2475,7 @@ void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
spin_lock_bh(&session->frwd_lock);
if (session->state != ISCSI_STATE_LOGGED_IN) {
session->state = ISCSI_STATE_RECOVERY_FAILED;
- if (session->leadconn)
- wake_up(&session->leadconn->ehwait);
+ wake_up(&session->ehwait);
}
spin_unlock_bh(&session->frwd_lock);
}
@@ -2437,7 +2496,6 @@ int iscsi_eh_session_reset(struct scsi_cmnd *sc)
cls_session = starget_to_session(scsi_target(sc->device));
session = cls_session->dd_data;
- conn = session->leadconn;
mutex_lock(&session->eh_mutex);
spin_lock_bh(&session->frwd_lock);
@@ -2452,16 +2510,17 @@ failed:
return FAILED;
}
+ conn = session->leadconn;
+ iscsi_get_conn(conn->cls_conn);
+
spin_unlock_bh(&session->frwd_lock);
mutex_unlock(&session->eh_mutex);
- /*
- * we drop the lock here but the leadconn cannot be destoyed while
- * we are in the scsi eh
- */
+
iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST);
+ iscsi_put_conn(conn->cls_conn);
ISCSI_DBG_EH(session, "wait for relogin\n");
- wait_event_interruptible(conn->ehwait,
+ wait_event_interruptible(session->ehwait,
session->state == ISCSI_STATE_TERMINATE ||
session->state == ISCSI_STATE_LOGGED_IN ||
session->state == ISCSI_STATE_RECOVERY_FAILED);
@@ -2522,11 +2581,11 @@ static int iscsi_eh_target_reset(struct scsi_cmnd *sc)
conn = session->leadconn;
/* only have one tmf outstanding at a time */
- if (conn->tmf_state != TMF_INITIAL)
+ if (session->tmf_state != TMF_INITIAL)
goto unlock;
- conn->tmf_state = TMF_QUEUED;
+ session->tmf_state = TMF_QUEUED;
- hdr = &conn->tmhdr;
+ hdr = &session->tmhdr;
iscsi_prep_tgt_reset_pdu(sc, hdr);
if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age,
@@ -2535,7 +2594,7 @@ static int iscsi_eh_target_reset(struct scsi_cmnd *sc)
goto unlock;
}
- switch (conn->tmf_state) {
+ switch (session->tmf_state) {
case TMF_SUCCESS:
break;
case TMF_TIMEDOUT:
@@ -2543,7 +2602,7 @@ static int iscsi_eh_target_reset(struct scsi_cmnd *sc)
iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST);
goto done;
default:
- conn->tmf_state = TMF_INITIAL;
+ session->tmf_state = TMF_INITIAL;
goto unlock;
}
@@ -2555,7 +2614,7 @@ static int iscsi_eh_target_reset(struct scsi_cmnd *sc)
spin_lock_bh(&session->frwd_lock);
memset(hdr, 0, sizeof(*hdr));
fail_scsi_tasks(conn, -1, DID_ERROR);
- conn->tmf_state = TMF_INITIAL;
+ session->tmf_state = TMF_INITIAL;
spin_unlock_bh(&session->frwd_lock);
iscsi_start_tx(conn);
@@ -2885,7 +2944,10 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
session->tt = iscsit;
session->dd_data = cls_session->dd_data + sizeof(*session);
+ session->tmf_state = TMF_INITIAL;
+ timer_setup(&session->tmf_timer, iscsi_tmf_timedout, 0);
mutex_init(&session->eh_mutex);
+
spin_lock_init(&session->frwd_lock);
spin_lock_init(&session->back_lock);
@@ -2936,10 +2998,9 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
struct module *owner = cls_session->transport->owner;
struct Scsi_Host *shost = session->host;
- iscsi_pool_free(&session->cmdpool);
-
iscsi_remove_session(cls_session);
+ iscsi_pool_free(&session->cmdpool);
kfree(session->password);
kfree(session->password_in);
kfree(session->username);
@@ -2989,7 +3050,6 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size,
conn->c_stage = ISCSI_CONN_INITIAL_STAGE;
conn->id = conn_idx;
conn->exp_statsn = 0;
- conn->tmf_state = TMF_INITIAL;
timer_setup(&conn->transport_timer, iscsi_check_transport_timeouts, 0);
@@ -3014,8 +3074,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size,
goto login_task_data_alloc_fail;
conn->login_task->data = conn->data = data;
- timer_setup(&conn->tmf_timer, iscsi_tmf_timedout, 0);
- init_waitqueue_head(&conn->ehwait);
+ init_waitqueue_head(&session->ehwait);
return cls_conn;
@@ -3050,7 +3109,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
* leading connection? then give up on recovery.
*/
session->state = ISCSI_STATE_TERMINATE;
- wake_up(&conn->ehwait);
+ wake_up(&session->ehwait);
}
spin_unlock_bh(&session->frwd_lock);
@@ -3125,7 +3184,7 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
* commands after successful recovery
*/
conn->stop_stage = 0;
- conn->tmf_state = TMF_INITIAL;
+ session->tmf_state = TMF_INITIAL;
session->age++;
if (session->age == 16)
session->age = 0;
@@ -3139,7 +3198,7 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
spin_unlock_bh(&session->frwd_lock);
iscsi_unblock_session(session->cls_session);
- wake_up(&conn->ehwait);
+ wake_up(&session->ehwait);
return 0;
}
EXPORT_SYMBOL_GPL(iscsi_conn_start);
@@ -3233,7 +3292,7 @@ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
spin_lock_bh(&session->frwd_lock);
fail_scsi_tasks(conn, -1, DID_TRANSPORT_DISRUPTED);
fail_mgmt_tasks(session, conn);
- memset(&conn->tmhdr, 0, sizeof(conn->tmhdr));
+ memset(&session->tmhdr, 0, sizeof(session->tmhdr));
spin_unlock_bh(&session->frwd_lock);
mutex_unlock(&session->eh_mutex);
}
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index b5a765b73c76..7af2c23652b0 100644
--- a/drivers/scsi/megaraid/megaraid_sas.h
+++ b/drivers/scsi/megaraid/megaraid_sas.h
@@ -21,8 +21,8 @@
/*
* MegaRAID SAS Driver meta data
*/
-#define MEGASAS_VERSION "07.714.04.00-rc1"
-#define MEGASAS_RELDATE "Apr 14, 2020"
+#define MEGASAS_VERSION "07.717.02.00-rc1"
+#define MEGASAS_RELDATE "May 19, 2021"
#define MEGASAS_MSIX_NAME_LEN 32
@@ -2262,6 +2262,15 @@ enum MR_PERF_MODE {
(mode) == MR_LATENCY_PERF_MODE ? "Latency" : \
"Unknown")
+enum MEGASAS_LD_TARGET_ID_STATUS {
+ LD_TARGET_ID_INITIAL,
+ LD_TARGET_ID_ACTIVE,
+ LD_TARGET_ID_DELETED,
+};
+
+#define MEGASAS_TARGET_ID(sdev) \
+ (((sdev->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + sdev->id)
+
struct megasas_instance {
unsigned int *reply_map;
@@ -2326,6 +2335,9 @@ struct megasas_instance {
struct megasas_pd_list pd_list[MEGASAS_MAX_PD];
struct megasas_pd_list local_pd_list[MEGASAS_MAX_PD];
u8 ld_ids[MEGASAS_MAX_LD_IDS];
+ u8 ld_tgtid_status[MEGASAS_MAX_LD_IDS];
+ u8 ld_ids_prev[MEGASAS_MAX_LD_IDS];
+ u8 ld_ids_from_raidmap[MEGASAS_MAX_LD_IDS];
s8 init_id;
u16 max_num_sge;
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index 181407888e58..ec10b2497310 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -141,6 +141,8 @@ static int megasas_register_aen(struct megasas_instance *instance,
u32 seq_num, u32 class_locale_word);
static void megasas_get_pd_info(struct megasas_instance *instance,
struct scsi_device *sdev);
+static void
+megasas_set_ld_removed_by_fw(struct megasas_instance *instance);
/*
* PCI ID table for all supported controllers
@@ -436,6 +438,12 @@ megasas_decode_evt(struct megasas_instance *instance)
(class_locale.members.locale),
format_class(class_locale.members.class),
evt_detail->description);
+
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ dev_info(&instance->pdev->dev,
+ "evt_detail.args.ld.target_id/index %d/%d\n",
+ evt_detail->args.ld.target_id, evt_detail->args.ld.ld_index);
+
}
/*
@@ -1779,6 +1787,7 @@ megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd)
{
struct megasas_instance *instance;
struct MR_PRIV_DEVICE *mr_device_priv_data;
+ u32 ld_tgt_id;
instance = (struct megasas_instance *)
scmd->device->host->hostdata;
@@ -1805,17 +1814,21 @@ megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd)
}
}
- if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) {
+ mr_device_priv_data = scmd->device->hostdata;
+ if (!mr_device_priv_data ||
+ (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR)) {
scmd->result = DID_NO_CONNECT << 16;
scmd->scsi_done(scmd);
return 0;
}
- mr_device_priv_data = scmd->device->hostdata;
- if (!mr_device_priv_data) {
- scmd->result = DID_NO_CONNECT << 16;
- scmd->scsi_done(scmd);
- return 0;
+ if (MEGASAS_IS_LOGICAL(scmd->device)) {
+ ld_tgt_id = MEGASAS_TARGET_ID(scmd->device);
+ if (instance->ld_tgtid_status[ld_tgt_id] == LD_TARGET_ID_DELETED) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ return 0;
+ }
}
if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL)
@@ -2095,7 +2108,7 @@ static int megasas_slave_configure(struct scsi_device *sdev)
static int megasas_slave_alloc(struct scsi_device *sdev)
{
- u16 pd_index = 0;
+ u16 pd_index = 0, ld_tgt_id;
struct megasas_instance *instance ;
struct MR_PRIV_DEVICE *mr_device_priv_data;
@@ -2120,6 +2133,14 @@ scan_target:
GFP_KERNEL);
if (!mr_device_priv_data)
return -ENOMEM;
+
+ if (MEGASAS_IS_LOGICAL(sdev)) {
+ ld_tgt_id = MEGASAS_TARGET_ID(sdev);
+ instance->ld_tgtid_status[ld_tgt_id] = LD_TARGET_ID_ACTIVE;
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ sdev_printk(KERN_INFO, sdev, "LD target ID %d created.\n", ld_tgt_id);
+ }
+
sdev->hostdata = mr_device_priv_data;
atomic_set(&mr_device_priv_data->r1_ldio_hint,
@@ -2129,6 +2150,19 @@ scan_target:
static void megasas_slave_destroy(struct scsi_device *sdev)
{
+ u16 ld_tgt_id;
+ struct megasas_instance *instance;
+
+ instance = megasas_lookup_instance(sdev->host->host_no);
+
+ if (MEGASAS_IS_LOGICAL(sdev)) {
+ ld_tgt_id = MEGASAS_TARGET_ID(sdev);
+ instance->ld_tgtid_status[ld_tgt_id] = LD_TARGET_ID_DELETED;
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ sdev_printk(KERN_INFO, sdev,
+ "LD target ID %d removed from OS stack\n", ld_tgt_id);
+ }
+
kfree(sdev->hostdata);
sdev->hostdata = NULL;
}
@@ -3525,6 +3559,22 @@ megasas_complete_abort(struct megasas_instance *instance,
}
}
+static void
+megasas_set_ld_removed_by_fw(struct megasas_instance *instance)
+{
+ uint i;
+
+ for (i = 0; (i < MEGASAS_MAX_LD_IDS); i++) {
+ if (instance->ld_ids_prev[i] != 0xff &&
+ instance->ld_ids_from_raidmap[i] == 0xff) {
+ if (megasas_dbg_lvl & LD_PD_DEBUG)
+ dev_info(&instance->pdev->dev,
+ "LD target ID %d removed from RAID map\n", i);
+ instance->ld_tgtid_status[i] = LD_TARGET_ID_DELETED;
+ }
+ }
+}
+
/**
* megasas_complete_cmd - Completes a command
* @instance: Adapter soft state
@@ -3685,9 +3735,13 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
fusion->fast_path_io = 0;
}
+ if (instance->adapter_type >= INVADER_SERIES)
+ megasas_set_ld_removed_by_fw(instance);
+
megasas_sync_map_info(instance);
spin_unlock_irqrestore(instance->host->host_lock,
flags);
+
break;
}
if (opcode == MR_DCMD_CTRL_EVENT_GET_INFO ||
@@ -7543,11 +7597,16 @@ static int megasas_probe_one(struct pci_dev *pdev,
return 0;
fail_start_aen:
+ instance->unload = 1;
+ scsi_remove_host(instance->host);
fail_io_attach:
megasas_mgmt_info.count--;
megasas_mgmt_info.max_index--;
megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL;
+ if (instance->requestorId && !instance->skip_heartbeat_timer_del)
+ del_timer_sync(&instance->sriov_heartbeat_timer);
+
instance->instancet->disable_intr(instance);
megasas_destroy_irqs(instance);
@@ -7555,8 +7614,16 @@ fail_io_attach:
megasas_release_fusion(instance);
else
megasas_release_mfi(instance);
+
if (instance->msix_vectors)
pci_free_irq_vectors(instance->pdev);
+ instance->msix_vectors = 0;
+
+ if (instance->fw_crash_state != UNAVAILABLE)
+ megasas_free_host_crash_buffer(instance);
+
+ if (instance->adapter_type != MFI_SERIES)
+ megasas_fusion_stop_watchdog(instance);
fail_init_mfi:
scsi_host_put(host);
fail_alloc_instance:
@@ -8816,8 +8883,10 @@ megasas_aen_polling(struct work_struct *work)
union megasas_evt_class_locale class_locale;
int event_type = 0;
u32 seq_num;
+ u16 ld_target_id;
int error;
u8 dcmd_ret = DCMD_SUCCESS;
+ struct scsi_device *sdev1;
if (!instance) {
printk(KERN_ERR "invalid instance!\n");
@@ -8840,12 +8909,23 @@ megasas_aen_polling(struct work_struct *work)
break;
case MR_EVT_LD_OFFLINE:
- case MR_EVT_CFG_CLEARED:
case MR_EVT_LD_DELETED:
+ ld_target_id = instance->evt_detail->args.ld.target_id;
+ sdev1 = scsi_device_lookup(instance->host,
+ MEGASAS_MAX_PD_CHANNELS +
+ (ld_target_id / MEGASAS_MAX_DEV_PER_CHANNEL),
+ (ld_target_id - MEGASAS_MAX_DEV_PER_CHANNEL),
+ 0);
+ if (sdev1)
+ megasas_remove_scsi_device(sdev1);
+
+ event_type = SCAN_VD_CHANNEL;
+ break;
case MR_EVT_LD_CREATED:
event_type = SCAN_VD_CHANNEL;
break;
+ case MR_EVT_CFG_CLEARED:
case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED:
case MR_EVT_FOREIGN_CFG_IMPORTED:
case MR_EVT_LD_STATE_CHANGE:
diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c
index b6c08d620033..83f69c33b01a 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fp.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fp.c
@@ -349,6 +349,10 @@ u8 MR_ValidateMapInfo(struct megasas_instance *instance, u64 map_id)
num_lds = le16_to_cpu(drv_map->raidMap.ldCount);
+ memcpy(instance->ld_ids_prev,
+ instance->ld_ids_from_raidmap,
+ sizeof(instance->ld_ids_from_raidmap));
+ memset(instance->ld_ids_from_raidmap, 0xff, MEGASAS_MAX_LD_IDS);
/*Convert Raid capability values to CPU arch */
for (i = 0; (num_lds > 0) && (i < MAX_LOGICAL_DRIVES_EXT); i++) {
ld = MR_TargetIdToLdGet(i, drv_map);
@@ -359,7 +363,7 @@ u8 MR_ValidateMapInfo(struct megasas_instance *instance, u64 map_id)
raid = MR_LdRaidGet(ld, drv_map);
le32_to_cpus((u32 *)&raid->capability);
-
+ instance->ld_ids_from_raidmap[i] = i;
num_lds--;
}
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c
index 1a4bd75229cf..06399c026a8d 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c
@@ -3202,6 +3202,8 @@ megasas_build_io_fusion(struct megasas_instance *instance,
{
int sge_count;
u8 cmd_type;
+ u16 pd_index = 0;
+ u8 drive_type = 0;
struct MPI2_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request;
struct MR_PRIV_DEVICE *mr_device_priv_data;
mr_device_priv_data = scp->device->hostdata;
@@ -3236,8 +3238,12 @@ megasas_build_io_fusion(struct megasas_instance *instance,
megasas_build_syspd_fusion(instance, scp, cmd, true);
break;
case NON_READ_WRITE_SYSPDIO:
- if (instance->secure_jbod_support ||
- mr_device_priv_data->is_tm_capable)
+ pd_index = MEGASAS_PD_INDEX(scp);
+ drive_type = instance->pd_list[pd_index].driveType;
+ if ((instance->secure_jbod_support ||
+ mr_device_priv_data->is_tm_capable) ||
+ (instance->adapter_type >= VENTURA_SERIES &&
+ drive_type == TYPE_ENCLOSURE))
megasas_build_syspd_fusion(instance, scp, cmd, false);
else
megasas_build_syspd_fusion(instance, scp, cmd, true);
@@ -3738,6 +3744,7 @@ static void megasas_sync_irqs(unsigned long instance_addr)
if (irq_ctx->irq_poll_scheduled) {
irq_ctx->irq_poll_scheduled = false;
enable_irq(irq_ctx->os_irq);
+ complete_cmd_fusion(instance, irq_ctx->MSIxIndex, irq_ctx);
}
}
}
@@ -3769,6 +3776,7 @@ int megasas_irqpoll(struct irq_poll *irqpoll, int budget)
irq_poll_complete(irqpoll);
irq_ctx->irq_poll_scheduled = false;
enable_irq(irq_ctx->os_irq);
+ complete_cmd_fusion(instance, irq_ctx->MSIxIndex, irq_ctx);
}
return num_entries;
@@ -3785,6 +3793,7 @@ megasas_complete_cmd_dpc_fusion(unsigned long instance_addr)
{
struct megasas_instance *instance =
(struct megasas_instance *)instance_addr;
+ struct megasas_irq_context *irq_ctx = NULL;
u32 count, MSIxIndex;
count = instance->msix_vectors > 0 ? instance->msix_vectors : 1;
@@ -3793,8 +3802,10 @@ megasas_complete_cmd_dpc_fusion(unsigned long instance_addr)
if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR)
return;
- for (MSIxIndex = 0 ; MSIxIndex < count; MSIxIndex++)
- complete_cmd_fusion(instance, MSIxIndex, NULL);
+ for (MSIxIndex = 0 ; MSIxIndex < count; MSIxIndex++) {
+ irq_ctx = &instance->irq_context[MSIxIndex];
+ complete_cmd_fusion(instance, MSIxIndex, irq_ctx);
+ }
}
/**
@@ -5265,6 +5276,7 @@ megasas_alloc_fusion_context(struct megasas_instance *instance)
if (!fusion->log_to_span) {
dev_err(&instance->pdev->dev, "Failed from %s %d\n",
__func__, __LINE__);
+ kfree(instance->ctrl_context);
return -ENOMEM;
}
}
diff --git a/drivers/scsi/mpi3mr/Kconfig b/drivers/scsi/mpi3mr/Kconfig
new file mode 100644
index 000000000000..f7882375e74f
--- /dev/null
+++ b/drivers/scsi/mpi3mr/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+config SCSI_MPI3MR
+ tristate "Broadcom MPI3 Storage Controller Device Driver"
+ depends on PCI && SCSI
+ help
+ MPI3 based Storage & RAID Controllers Driver.
diff --git a/drivers/scsi/mpi3mr/Makefile b/drivers/scsi/mpi3mr/Makefile
new file mode 100644
index 000000000000..7c2063e04c81
--- /dev/null
+++ b/drivers/scsi/mpi3mr/Makefile
@@ -0,0 +1,4 @@
+# mpi3mr makefile
+obj-m += mpi3mr.o
+mpi3mr-y += mpi3mr_os.o \
+ mpi3mr_fw.o \
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h b/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h
new file mode 100644
index 000000000000..d43bbecef651
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h
@@ -0,0 +1,1880 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2017-2021 Broadcom Inc. All rights reserved.
+ *
+ */
+#ifndef MPI30_CNFG_H
+#define MPI30_CNFG_H 1
+#define MPI3_CONFIG_PAGETYPE_IO_UNIT (0x00)
+#define MPI3_CONFIG_PAGETYPE_MANUFACTURING (0x01)
+#define MPI3_CONFIG_PAGETYPE_IOC (0x02)
+#define MPI3_CONFIG_PAGETYPE_UEFI_BSD (0x03)
+#define MPI3_CONFIG_PAGETYPE_SECURITY (0x04)
+#define MPI3_CONFIG_PAGETYPE_ENCLOSURE (0x11)
+#define MPI3_CONFIG_PAGETYPE_DEVICE (0x12)
+#define MPI3_CONFIG_PAGETYPE_SAS_IO_UNIT (0x20)
+#define MPI3_CONFIG_PAGETYPE_SAS_EXPANDER (0x21)
+#define MPI3_CONFIG_PAGETYPE_SAS_PHY (0x23)
+#define MPI3_CONFIG_PAGETYPE_SAS_PORT (0x24)
+#define MPI3_CONFIG_PAGETYPE_PCIE_IO_UNIT (0x30)
+#define MPI3_CONFIG_PAGETYPE_PCIE_SWITCH (0x31)
+#define MPI3_CONFIG_PAGETYPE_PCIE_LINK (0x33)
+#define MPI3_CONFIG_PAGEATTR_MASK (0xf0)
+#define MPI3_CONFIG_PAGEATTR_READ_ONLY (0x00)
+#define MPI3_CONFIG_PAGEATTR_CHANGEABLE (0x10)
+#define MPI3_CONFIG_PAGEATTR_PERSISTENT (0x20)
+#define MPI3_CONFIG_ACTION_PAGE_HEADER (0x00)
+#define MPI3_CONFIG_ACTION_READ_DEFAULT (0x01)
+#define MPI3_CONFIG_ACTION_READ_CURRENT (0x02)
+#define MPI3_CONFIG_ACTION_WRITE_CURRENT (0x03)
+#define MPI3_CONFIG_ACTION_READ_PERSISTENT (0x04)
+#define MPI3_CONFIG_ACTION_WRITE_PERSISTENT (0x05)
+#define MPI3_DEVICE_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI3_DEVICE_PGAD_FORM_HANDLE (0x20000000)
+#define MPI3_DEVICE_PGAD_HANDLE_MASK (0x0000ffff)
+#define MPI3_SAS_EXPAND_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_SAS_EXPAND_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI3_SAS_EXPAND_PGAD_FORM_HANDLE_PHY_NUM (0x10000000)
+#define MPI3_SAS_EXPAND_PGAD_FORM_HANDLE (0x20000000)
+#define MPI3_SAS_EXPAND_PGAD_PHYNUM_MASK (0x00ff0000)
+#define MPI3_SAS_EXPAND_PGAD_PHYNUM_SHIFT (16)
+#define MPI3_SAS_EXPAND_PGAD_HANDLE_MASK (0x0000ffff)
+#define MPI3_SAS_PHY_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_SAS_PHY_PGAD_FORM_PHY_NUMBER (0x00000000)
+#define MPI3_SAS_PHY_PGAD_PHY_NUMBER_MASK (0x000000ff)
+#define MPI3_SASPORT_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_SASPORT_PGAD_FORM_GET_NEXT_PORT (0x00000000)
+#define MPI3_SASPORT_PGAD_FORM_PORT_NUM (0x10000000)
+#define MPI3_SASPORT_PGAD_PORT_NUMBER_MASK (0x000000ff)
+#define MPI3_ENCLOS_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI3_ENCLOS_PGAD_FORM_HANDLE (0x10000000)
+#define MPI3_ENCLOS_PGAD_HANDLE_MASK (0x0000ffff)
+#define MPI3_PCIE_SWITCH_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_PCIE_SWITCH_PGAD_FORM_GET_NEXT_HANDLE (0x00000000)
+#define MPI3_PCIE_SWITCH_PGAD_FORM_HANDLE_PORT_NUM (0x10000000)
+#define MPI3_PCIE_SWITCH_PGAD_FORM_HANDLE (0x20000000)
+#define MPI3_PCIE_SWITCH_PGAD_PORTNUM_MASK (0x00ff0000)
+#define MPI3_PCIE_SWITCH_PGAD_PORTNUM_SHIFT (16)
+#define MPI3_PCIE_SWITCH_PGAD_HANDLE_MASK (0x0000ffff)
+#define MPI3_PCIE_LINK_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_PCIE_LINK_PGAD_FORM_GET_NEXT_LINK (0x00000000)
+#define MPI3_PCIE_LINK_PGAD_FORM_LINK_NUM (0x10000000)
+#define MPI3_PCIE_LINK_PGAD_LINKNUM_MASK (0x000000ff)
+#define MPI3_SECURITY_PGAD_FORM_MASK (0xf0000000)
+#define MPI3_SECURITY_PGAD_FORM_GET_NEXT_SLOT (0x00000000)
+#define MPI3_SECURITY_PGAD_FORM_SOT_NUM (0x10000000)
+#define MPI3_SECURITY_PGAD_SLOT_GROUP_MASK (0x0000ff00)
+#define MPI3_SECURITY_PGAD_SLOT_MASK (0x000000ff)
+struct mpi3_config_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ u8 page_version;
+ u8 page_number;
+ u8 page_type;
+ u8 action;
+ __le32 page_address;
+ __le16 page_length;
+ __le16 reserved16;
+ __le32 reserved18[2];
+ union mpi3_sge_union sgl;
+};
+
+struct mpi3_config_page_header {
+ u8 page_version;
+ u8 reserved01;
+ u8 page_number;
+ u8 page_attribute;
+ __le16 page_length;
+ u8 page_type;
+ u8 reserved07;
+};
+
+#define MPI3_SAS_NEG_LINK_RATE_LOGICAL_MASK (0xf0)
+#define MPI3_SAS_NEG_LINK_RATE_LOGICAL_SHIFT (4)
+#define MPI3_SAS_NEG_LINK_RATE_PHYSICAL_MASK (0x0f)
+#define MPI3_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE (0x00)
+#define MPI3_SAS_NEG_LINK_RATE_PHY_DISABLED (0x01)
+#define MPI3_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED (0x02)
+#define MPI3_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE (0x03)
+#define MPI3_SAS_NEG_LINK_RATE_PORT_SELECTOR (0x04)
+#define MPI3_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS (0x05)
+#define MPI3_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY (0x06)
+#define MPI3_SAS_NEG_LINK_RATE_1_5 (0x08)
+#define MPI3_SAS_NEG_LINK_RATE_3_0 (0x09)
+#define MPI3_SAS_NEG_LINK_RATE_6_0 (0x0a)
+#define MPI3_SAS_NEG_LINK_RATE_12_0 (0x0b)
+#define MPI3_SAS_NEG_LINK_RATE_22_5 (0x0c)
+#define MPI3_SAS_APHYINFO_INSIDE_ZPSDS_PERSISTENT (0x00000040)
+#define MPI3_SAS_APHYINFO_REQUESTED_INSIDE_ZPSDS (0x00000020)
+#define MPI3_SAS_APHYINFO_BREAK_REPLY_CAPABLE (0x00000010)
+#define MPI3_SAS_APHYINFO_REASON_MASK (0x0000000f)
+#define MPI3_SAS_APHYINFO_REASON_UNKNOWN (0x00000000)
+#define MPI3_SAS_APHYINFO_REASON_POWER_ON (0x00000001)
+#define MPI3_SAS_APHYINFO_REASON_HARD_RESET (0x00000002)
+#define MPI3_SAS_APHYINFO_REASON_SMP_PHY_CONTROL (0x00000003)
+#define MPI3_SAS_APHYINFO_REASON_LOSS_OF_SYNC (0x00000004)
+#define MPI3_SAS_APHYINFO_REASON_MULTIPLEXING_SEQ (0x00000005)
+#define MPI3_SAS_APHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00000006)
+#define MPI3_SAS_APHYINFO_REASON_BREAK_TIMEOUT (0x00000007)
+#define MPI3_SAS_APHYINFO_REASON_PHY_TEST_STOPPED (0x00000008)
+#define MPI3_SAS_APHYINFO_REASON_EXP_REDUCED_FUNC (0x00000009)
+#define MPI3_SAS_PHYINFO_STATUS_MASK (0xc0000000)
+#define MPI3_SAS_PHYINFO_STATUS_SHIFT (30)
+#define MPI3_SAS_PHYINFO_STATUS_ACCESSIBLE (0x00000000)
+#define MPI3_SAS_PHYINFO_STATUS_NOT_EXIST (0x40000000)
+#define MPI3_SAS_PHYINFO_STATUS_VACANT (0x80000000)
+#define MPI3_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000)
+#define MPI3_SAS_PHYINFO_PHY_POWER_CONDITION_ACTIVE (0x00000000)
+#define MPI3_SAS_PHYINFO_PHY_POWER_CONDITION_PARTIAL (0x08000000)
+#define MPI3_SAS_PHYINFO_PHY_POWER_CONDITION_SLUMBER (0x10000000)
+#define MPI3_SAS_PHYINFO_REASON_MASK (0x000f0000)
+#define MPI3_SAS_PHYINFO_REASON_UNKNOWN (0x00000000)
+#define MPI3_SAS_PHYINFO_REASON_POWER_ON (0x00010000)
+#define MPI3_SAS_PHYINFO_REASON_HARD_RESET (0x00020000)
+#define MPI3_SAS_PHYINFO_REASON_SMP_PHY_CONTROL (0x00030000)
+#define MPI3_SAS_PHYINFO_REASON_LOSS_OF_SYNC (0x00040000)
+#define MPI3_SAS_PHYINFO_REASON_MULTIPLEXING_SEQ (0x00050000)
+#define MPI3_SAS_PHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00060000)
+#define MPI3_SAS_PHYINFO_REASON_BREAK_TIMEOUT (0x00070000)
+#define MPI3_SAS_PHYINFO_REASON_PHY_TEST_STOPPED (0x00080000)
+#define MPI3_SAS_PHYINFO_REASON_EXP_REDUCED_FUNC (0x00090000)
+#define MPI3_SAS_PHYINFO_SATA_PORT_ACTIVE (0x00004000)
+#define MPI3_SAS_PHYINFO_SATA_PORT_SELECTOR_PRESENT (0x00002000)
+#define MPI3_SAS_PHYINFO_VIRTUAL_PHY (0x00001000)
+#define MPI3_SAS_PHYINFO_PARTIAL_PATHWAY_TIME_MASK (0x00000f00)
+#define MPI3_SAS_PHYINFO_PARTIAL_PATHWAY_TIME_SHIFT (8)
+#define MPI3_SAS_PHYINFO_ROUTING_ATTRIBUTE_MASK (0x000000f0)
+#define MPI3_SAS_PHYINFO_ROUTING_ATTRIBUTE_DIRECT (0x00000000)
+#define MPI3_SAS_PHYINFO_ROUTING_ATTRIBUTE_SUBTRACTIVE (0x00000010)
+#define MPI3_SAS_PHYINFO_ROUTING_ATTRIBUTE_TABLE (0x00000020)
+#define MPI3_SAS_PRATE_MAX_RATE_MASK (0xf0)
+#define MPI3_SAS_PRATE_MAX_RATE_NOT_PROGRAMMABLE (0x00)
+#define MPI3_SAS_PRATE_MAX_RATE_1_5 (0x80)
+#define MPI3_SAS_PRATE_MAX_RATE_3_0 (0x90)
+#define MPI3_SAS_PRATE_MAX_RATE_6_0 (0xa0)
+#define MPI3_SAS_PRATE_MAX_RATE_12_0 (0xb0)
+#define MPI3_SAS_PRATE_MAX_RATE_22_5 (0xc0)
+#define MPI3_SAS_PRATE_MIN_RATE_MASK (0x0f)
+#define MPI3_SAS_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00)
+#define MPI3_SAS_PRATE_MIN_RATE_1_5 (0x08)
+#define MPI3_SAS_PRATE_MIN_RATE_3_0 (0x09)
+#define MPI3_SAS_PRATE_MIN_RATE_6_0 (0x0a)
+#define MPI3_SAS_PRATE_MIN_RATE_12_0 (0x0b)
+#define MPI3_SAS_PRATE_MIN_RATE_22_5 (0x0c)
+#define MPI3_SAS_HWRATE_MAX_RATE_MASK (0xf0)
+#define MPI3_SAS_HWRATE_MAX_RATE_1_5 (0x80)
+#define MPI3_SAS_HWRATE_MAX_RATE_3_0 (0x90)
+#define MPI3_SAS_HWRATE_MAX_RATE_6_0 (0xa0)
+#define MPI3_SAS_HWRATE_MAX_RATE_12_0 (0xb0)
+#define MPI3_SAS_HWRATE_MAX_RATE_22_5 (0xc0)
+#define MPI3_SAS_HWRATE_MIN_RATE_MASK (0x0f)
+#define MPI3_SAS_HWRATE_MIN_RATE_1_5 (0x08)
+#define MPI3_SAS_HWRATE_MIN_RATE_3_0 (0x09)
+#define MPI3_SAS_HWRATE_MIN_RATE_6_0 (0x0a)
+#define MPI3_SAS_HWRATE_MIN_RATE_12_0 (0x0b)
+#define MPI3_SAS_HWRATE_MIN_RATE_22_5 (0x0c)
+#define MPI3_SLOT_INVALID (0xffff)
+#define MPI3_SLOT_INDEX_INVALID (0xffff)
+struct mpi3_man_page0 {
+ struct mpi3_config_page_header header;
+ u8 chip_revision[8];
+ u8 chip_name[32];
+ u8 board_name[32];
+ u8 board_assembly[32];
+ u8 board_tracer_number[32];
+ __le32 board_power;
+ __le32 reserved94;
+ __le32 reserved98;
+ u8 oem;
+ u8 sub_oem;
+ __le16 reserved9e;
+ u8 board_mfg_day;
+ u8 board_mfg_month;
+ __le16 board_mfg_year;
+ u8 board_rework_day;
+ u8 board_rework_month;
+ __le16 board_rework_year;
+ __le64 board_revision;
+ u8 e_pack_fru[16];
+ u8 product_name[256];
+};
+
+#define MPI3_MAN0_PAGEVERSION (0x00)
+#define MPI3_MAN1_VPD_SIZE (512)
+struct mpi3_man_page1 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08[2];
+ u8 vpd[MPI3_MAN1_VPD_SIZE];
+};
+
+#define MPI3_MAN1_PAGEVERSION (0x00)
+struct mpi3_man5_phy_entry {
+ __le64 ioc_wwid;
+ __le64 device_name;
+ __le64 sata_wwid;
+};
+
+#ifndef MPI3_MAN5_PHY_MAX
+#define MPI3_MAN5_PHY_MAX (1)
+#endif
+struct mpi3_man_page5 {
+ struct mpi3_config_page_header header;
+ u8 num_phys;
+ u8 reserved09[3];
+ __le32 reserved0c;
+ struct mpi3_man5_phy_entry phy[MPI3_MAN5_PHY_MAX];
+};
+
+#define MPI3_MAN5_PAGEVERSION (0x00)
+struct mpi3_man6_gpio_entry {
+ u8 function_code;
+ u8 reserved01;
+ __le16 flags;
+ u8 param1;
+ u8 param2;
+ __le16 reserved06;
+ __le32 param3;
+};
+
+#define MPI3_MAN6_GPIO_FUNCTION_GENERIC (0x00)
+#define MPI3_MAN6_GPIO_FUNCTION_ALTERNATE (0x01)
+#define MPI3_MAN6_GPIO_FUNCTION_EXT_INTERRUPT (0x02)
+#define MPI3_MAN6_GPIO_FUNCTION_GLOBAL_ACTIVITY (0x03)
+#define MPI3_MAN6_GPIO_FUNCTION_OVER_TEMPERATURE (0x04)
+#define MPI3_MAN6_GPIO_FUNCTION_PORT_STATUS_GREEN (0x05)
+#define MPI3_MAN6_GPIO_FUNCTION_PORT_STATUS_YELLOW (0x06)
+#define MPI3_MAN6_GPIO_FUNCTION_CABLE_MANAGEMENT (0x07)
+#define MPI3_MAN6_GPIO_FUNCTION_BKPLANE_MGMT_TYPE (0x08)
+#define MPI3_MAN6_GPIO_FUNCTION_ISTWI_MUX_RESET (0x09)
+#define MPI3_MAN6_GPIO_FUNCTION_ISTWI_RESET (0x0a)
+#define MPI3_MAN6_GPIO_FUNCTION_BACKEND_PCIE_RESET (0x0b)
+#define MPI3_MAN6_GPIO_FUNCTION_GLOBAL_FAULT (0x0c)
+#define MPI3_MAN6_GPIO_FUNCTION_EPACK_ATTN (0x0d)
+#define MPI3_MAN6_GPIO_FUNCTION_EPACK_ONLINE (0x0e)
+#define MPI3_MAN6_GPIO_FUNCTION_EPACK_FAULT (0x0f)
+#define MPI3_MAN6_GPIO_FUNCTION_CTRL_TYPE (0x10)
+#define MPI3_MAN6_GPIO_FUNCTION_LICENSE (0x11)
+#define MPI3_MAN6_GPIO_FUNCTION_REFCLK_CONTROL (0x12)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_SOURCE_MASK (0xf0)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_SOURCE_GENERIC (0x00)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_SOURCE_CABLE_MGMT (0x10)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_SOURCE_ACTIVE_CABLE_OVERCURRENT (0x20)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_TRIGGER_MASK (0x01)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_TRIGGER_EDGE (0x00)
+#define MPI3_MAN6_GPIO_EXTINT_PARAM1_FLAGS_TRIGGER_LEVEL (0x01)
+#define MPI3_MAN6_GPIO_PORT_GREEN_PARAM1_PHY_STATUS_ALL_UP (0x00)
+#define MPI3_MAN6_GPIO_PORT_GREEN_PARAM1_PHY_STATUS_ONE_OR_MORE_UP (0x01)
+#define MPI3_MAN6_GPIO_CABLE_MGMT_PARAM1_INTERFACE_MODULE_PRESENT (0x00)
+#define MPI3_MAN6_GPIO_CABLE_MGMT_PARAM1_INTERFACE_ACTIVE_CABLE_ENABLE (0x01)
+#define MPI3_MAN6_GPIO_CABLE_MGMT_PARAM1_INTERFACE_CABLE_MGMT_ENABLE (0x02)
+#define MPI3_MAN6_GPIO_ISTWI_MUX_RESET_PARAM2_SPEC_MUX (0x00)
+#define MPI3_MAN6_GPIO_ISTWI_MUX_RESET_PARAM2_ALL_MUXES (0x01)
+#define MPI3_MAN6_GPIO_LICENSE_PARAM1_TYPE_IBUTTON (0x00)
+#define MPI3_MAN6_GPIO_FLAGS_SLEW_RATE_MASK (0x0100)
+#define MPI3_MAN6_GPIO_FLAGS_SLEW_RATE_FAST_EDGE (0x0100)
+#define MPI3_MAN6_GPIO_FLAGS_SLEW_RATE_SLOW_EDGE (0x0000)
+#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_MASK (0x00c0)
+#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_100OHM (0x0000)
+#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_66OHM (0x0040)
+#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_50OHM (0x0080)
+#define MPI3_MAN6_GPIO_FLAGS_DRIVE_STRENGTH_33OHM (0x00c0)
+#define MPI3_MAN6_GPIO_FLAGS_ALT_DATA_SEL_MASK (0x0030)
+#define MPI3_MAN6_GPIO_FLAGS_ALT_DATA_SEL_SHIFT (4)
+#define MPI3_MAN6_GPIO_FLAGS_ACTIVE_HIGH (0x0008)
+#define MPI3_MAN6_GPIO_FLAGS_BI_DIR_ENABLED (0x0004)
+#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_MASK (0x0003)
+#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_INPUT (0x0000)
+#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_OPEN_DRAIN_OUTPUT (0x0001)
+#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_OPEN_SOURCE_OUTPUT (0x0002)
+#define MPI3_MAN6_GPIO_FLAGS_DIRECTION_PUSH_PULL_OUTPUT (0x0003)
+#ifndef MPI3_MAN6_GPIO_MAX
+#define MPI3_MAN6_GPIO_MAX (1)
+#endif
+struct mpi3_man_page6 {
+ struct mpi3_config_page_header header;
+ __le16 flags;
+ __le16 reserved0a;
+ u8 num_gpio;
+ u8 reserved0d[3];
+ struct mpi3_man6_gpio_entry gpio[MPI3_MAN6_GPIO_MAX];
+};
+
+#define MPI3_MAN6_PAGEVERSION (0x00)
+#define MPI3_MAN6_FLAGS_HEARTBEAT_LED_DISABLED (0x0001)
+struct mpi3_man7_receptacle_info {
+ __le32 name[4];
+ u8 location;
+ u8 connector_type;
+ u8 ped_clk;
+ u8 connector_id;
+ __le32 reserved14;
+};
+
+#define MPI3_MAN7_LOCATION_UNKNOWN (0x00)
+#define MPI3_MAN7_LOCATION_INTERNAL (0x01)
+#define MPI3_MAN7_LOCATION_EXTERNAL (0x02)
+#define MPI3_MAN7_LOCATION_VIRTUAL (0x03)
+#define MPI3_MAN7_PEDCLK_ROUTING_MASK (0x10)
+#define MPI3_MAN7_PEDCLK_ROUTING_DIRECT (0x00)
+#define MPI3_MAN7_PEDCLK_ROUTING_CLOCK_BUFFER (0x10)
+#define MPI3_MAN7_PEDCLK_ID_MASK (0x0f)
+#ifndef MPI3_MAN7_RECEPTACLE_INFO_MAX
+#define MPI3_MAN7_RECEPTACLE_INFO_MAX (1)
+#endif
+struct mpi3_man_page7 {
+ struct mpi3_config_page_header header;
+ __le32 flags;
+ u8 num_receptacles;
+ u8 reserved0d[3];
+ __le32 enclosure_name[4];
+ struct mpi3_man7_receptacle_info receptacle_info[MPI3_MAN7_RECEPTACLE_INFO_MAX];
+};
+
+#define MPI3_MAN7_PAGEVERSION (0x00)
+#define MPI3_MAN7_FLAGS_BASE_ENCLOSURE_LEVEL_MASK (0x01)
+#define MPI3_MAN7_FLAGS_BASE_ENCLOSURE_LEVEL_0 (0x00)
+#define MPI3_MAN7_FLAGS_BASE_ENCLOSURE_LEVEL_1 (0x01)
+struct mpi3_man8_phy_info {
+ u8 receptacle_id;
+ u8 connector_lane;
+ __le16 reserved02;
+ __le16 slotx1;
+ __le16 slotx2;
+ __le16 slotx4;
+ __le16 reserved0a;
+ __le32 reserved0c;
+};
+
+#ifndef MPI3_MAN8_PHY_INFO_MAX
+#define MPI3_MAN8_PHY_INFO_MAX (1)
+#endif
+struct mpi3_man_page8 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_phys;
+ u8 reserved0d[3];
+ struct mpi3_man8_phy_info phy_info[MPI3_MAN8_PHY_INFO_MAX];
+};
+
+#define MPI3_MAN8_PAGEVERSION (0x00)
+struct mpi3_man9_rsrc_entry {
+ __le32 maximum;
+ __le32 decrement;
+ __le32 minimum;
+ __le32 actual;
+};
+
+enum mpi3_man9_resources {
+ MPI3_MAN9_RSRC_OUTSTANDING_REQS = 0,
+ MPI3_MAN9_RSRC_TARGET_CMDS = 1,
+ MPI3_MAN9_RSRC_SAS_TARGETS = 2,
+ MPI3_MAN9_RSRC_PCIE_TARGETS = 3,
+ MPI3_MAN9_RSRC_INITIATORS = 4,
+ MPI3_MAN9_RSRC_VDS = 5,
+ MPI3_MAN9_RSRC_ENCLOSURES = 6,
+ MPI3_MAN9_RSRC_ENCLOSURE_PHYS = 7,
+ MPI3_MAN9_RSRC_EXPANDERS = 8,
+ MPI3_MAN9_RSRC_PCIE_SWITCHES = 9,
+ MPI3_MAN9_RSRC_PDS = 10,
+ MPI3_MAN9_RSRC_HOST_PDS = 11,
+ MPI3_MAN9_RSRC_ADV_HOST_PDS = 12,
+ MPI3_MAN9_RSRC_RAID_PDS = 13,
+ MPI3_MAN9_RSRC_NUM_RESOURCES
+};
+
+#define MPI3_MAN9_MIN_OUTSTANDING_REQS (1)
+#define MPI3_MAN9_MAX_OUTSTANDING_REQS (65000)
+#define MPI3_MAN9_MIN_TARGET_CMDS (0)
+#define MPI3_MAN9_MAX_TARGET_CMDS (65535)
+#define MPI3_MAN9_MIN_SAS_TARGETS (0)
+#define MPI3_MAN9_MAX_SAS_TARGETS (65535)
+#define MPI3_MAN9_MIN_PCIE_TARGETS (0)
+#define MPI3_MAN9_MIN_INITIATORS (0)
+#define MPI3_MAN9_MAX_INITIATORS (65535)
+#define MPI3_MAN9_MIN_ENCLOSURES (0)
+#define MPI3_MAN9_MAX_ENCLOSURES (65535)
+#define MPI3_MAN9_MIN_ENCLOSURE_PHYS (0)
+#define MPI3_MAN9_MIN_EXPANDERS (0)
+#define MPI3_MAN9_MAX_EXPANDERS (65535)
+#define MPI3_MAN9_MIN_PCIE_SWITCHES (0)
+struct mpi3_man_page9 {
+ struct mpi3_config_page_header header;
+ u8 num_resources;
+ u8 reserved09;
+ __le16 reserved0a;
+ __le32 reserved0c;
+ __le32 reserved10;
+ __le32 reserved14;
+ __le32 reserved18;
+ __le32 reserved1c;
+ struct mpi3_man9_rsrc_entry resource[MPI3_MAN9_RSRC_NUM_RESOURCES];
+};
+
+#define MPI3_MAN9_PAGEVERSION (0x00)
+struct mpi3_man10_istwi_ctrlr_entry {
+ __le16 slave_address;
+ __le16 flags;
+ __le32 reserved04;
+};
+
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_SLAVE_ENABLED (0x0002)
+#define MPI3_MAN10_ISTWI_CTRLR_FLAGS_MASTER_ENABLED (0x0001)
+#ifndef MPI3_MAN10_ISTWI_CTRLR_MAX
+#define MPI3_MAN10_ISTWI_CTRLR_MAX (1)
+#endif
+struct mpi3_man_page10 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_istwi_ctrl;
+ u8 reserved0d[3];
+ struct mpi3_man10_istwi_ctrlr_entry istwi_controller[MPI3_MAN10_ISTWI_CTRLR_MAX];
+};
+
+#define MPI3_MAN10_PAGEVERSION (0x00)
+struct mpi3_man11_mux_device_format {
+ u8 max_channel;
+ u8 reserved01[3];
+ __le32 reserved04;
+};
+
+struct mpi3_man11_temp_sensor_device_format {
+ u8 type;
+ u8 reserved01[3];
+ u8 temp_channel[4];
+};
+
+#define MPI3_MAN11_TEMP_SENSOR_TYPE_MAX6654 (0x00)
+#define MPI3_MAN11_TEMP_SENSOR_TYPE_EMC1442 (0x01)
+#define MPI3_MAN11_TEMP_SENSOR_TYPE_ADT7476 (0x02)
+#define MPI3_MAN11_TEMP_SENSOR_CHANNEL_ENABLED (0x01)
+struct mpi3_man11_seeprom_device_format {
+ u8 size;
+ u8 page_write_size;
+ __le16 reserved02;
+ __le32 reserved04;
+};
+
+#define MPI3_MAN11_SEEPROM_SIZE_1KBITS (0x01)
+#define MPI3_MAN11_SEEPROM_SIZE_2KBITS (0x02)
+#define MPI3_MAN11_SEEPROM_SIZE_4KBITS (0x03)
+#define MPI3_MAN11_SEEPROM_SIZE_8KBITS (0x04)
+#define MPI3_MAN11_SEEPROM_SIZE_16KBITS (0x05)
+#define MPI3_MAN11_SEEPROM_SIZE_32KBITS (0x06)
+#define MPI3_MAN11_SEEPROM_SIZE_64KBITS (0x07)
+#define MPI3_MAN11_SEEPROM_SIZE_128KBITS (0x08)
+struct mpi3_man11_ddr_spd_device_format {
+ u8 channel;
+ u8 reserved01[3];
+ __le32 reserved04;
+};
+
+struct mpi3_man11_cable_mgmt_device_format {
+ u8 type;
+ u8 receptacle_id;
+ __le16 reserved02;
+ __le32 reserved04;
+};
+
+#define MPI3_MAN11_CABLE_MGMT_TYPE_SFF_8636 (0x00)
+struct mpi3_man11_bkplane_spec_ubm_format {
+ __le16 flags;
+ __le16 reserved02;
+};
+
+#define MPI3_MAN11_BKPLANE_UBM_FLAGS_REFCLK_POLICY_ALWAYS_ENABLED (0x0200)
+#define MPI3_MAN11_BKPLANE_UBM_FLAGS_FORCE_POLLING (0x0100)
+#define MPI3_MAN11_BKPLANE_UBM_FLAGS_MAX_FRU_MASK (0x00f0)
+#define MPI3_MAN11_BKPLANE_UBM_FLAGS_MAX_FRU_SHIFT (4)
+#define MPI3_MAN11_BKPLANE_UBM_FLAGS_POLL_INTERVAL_MASK (0x000f)
+#define MPI3_MAN11_BKPLANE_UBM_FLAGS_POLL_INTERVAL_SHIFT (0)
+struct mpi3_man11_bkplane_spec_vpp_format {
+ __le16 flags;
+ __le16 reserved02;
+};
+
+#define MPI3_MAN11_BKPLANE_VPP_FLAGS_REFCLK_POLICY_ALWAYS_ENABLED (0x0040)
+#define MPI3_MAN11_BKPLANE_VPP_FLAGS_PRESENCE_DETECT_MASK (0x0030)
+#define MPI3_MAN11_BKPLANE_VPP_FLAGS_PRESENCE_DETECT_GPIO (0x0000)
+#define MPI3_MAN11_BKPLANE_VPP_FLAGS_PRESENCE_DETECT_REG (0x0010)
+#define MPI3_MAN11_BKPLANE_VPP_FLAGS_POLL_INTERVAL_MASK (0x000f)
+#define MPI3_MAN11_BKPLANE_VPP_FLAGS_POLL_INTERVAL_SHIFT (0)
+union mpi3_man11_bkplane_spec_format {
+ struct mpi3_man11_bkplane_spec_ubm_format ubm;
+ struct mpi3_man11_bkplane_spec_vpp_format vpp;
+};
+
+struct mpi3_man11_bkplane_mgmt_device_format {
+ u8 type;
+ u8 receptacle_id;
+ __le16 reserved02;
+ union mpi3_man11_bkplane_spec_format backplane_mgmt_specific;
+};
+
+#define MPI3_MAN11_BKPLANE_MGMT_TYPE_UBM (0x00)
+#define MPI3_MAN11_BKPLANE_MGMT_TYPE_VPP (0x01)
+struct mpi3_man11_gas_gauge_device_format {
+ u8 type;
+ u8 reserved01[3];
+ __le32 reserved04;
+};
+
+#define MPI3_MAN11_GAS_GAUGE_TYPE_STANDARD (0x00)
+union mpi3_man11_device_specific_format {
+ struct mpi3_man11_mux_device_format mux;
+ struct mpi3_man11_temp_sensor_device_format temp_sensor;
+ struct mpi3_man11_seeprom_device_format seeprom;
+ struct mpi3_man11_ddr_spd_device_format ddr_spd;
+ struct mpi3_man11_cable_mgmt_device_format cable_mgmt;
+ struct mpi3_man11_bkplane_mgmt_device_format bkplane_mgmt;
+ struct mpi3_man11_gas_gauge_device_format gas_gauge;
+ __le32 words[2];
+};
+
+struct mpi3_man11_istwi_device_format {
+ u8 device_type;
+ u8 controller;
+ u8 reserved02;
+ u8 flags;
+ __le16 device_address;
+ u8 mux_channel;
+ u8 mux_index;
+ union mpi3_man11_device_specific_format device_specific;
+};
+
+#define MPI3_MAN11_ISTWI_DEVTYPE_MUX (0x00)
+#define MPI3_MAN11_ISTWI_DEVTYPE_TEMP_SENSOR (0x01)
+#define MPI3_MAN11_ISTWI_DEVTYPE_SEEPROM (0x02)
+#define MPI3_MAN11_ISTWI_DEVTYPE_DDR_SPD (0x03)
+#define MPI3_MAN11_ISTWI_DEVTYPE_CABLE_MGMT (0x04)
+#define MPI3_MAN11_ISTWI_DEVTYPE_BACKPLANE_MGMT (0x05)
+#define MPI3_MAN11_ISTWI_DEVTYPE_GAS_GAUGE (0x06)
+#define MPI3_MAN11_ISTWI_FLAGS_MUX_PRESENT (0x01)
+#define MPI3_MAN11_ISTWI_FLAGS_BUS_SPEED_MASK (0x06)
+#define MPI3_MAN11_ISTWI_FLAGS_BUS_SPEED_100KHZ (0x00)
+#define MPI3_MAN11_ISTWI_FLAGS_BUS_SPEED_400KHZ (0x02)
+#ifndef MPI3_MAN11_ISTWI_DEVICE_MAX
+#define MPI3_MAN11_ISTWI_DEVICE_MAX (1)
+#endif
+struct mpi3_man_page11 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_istwi_dev;
+ u8 reserved0d[3];
+ struct mpi3_man11_istwi_device_format istwi_device[MPI3_MAN11_ISTWI_DEVICE_MAX];
+};
+
+#define MPI3_MAN11_PAGEVERSION (0x00)
+#ifndef MPI3_MAN12_NUM_SGPIO_MAX
+#define MPI3_MAN12_NUM_SGPIO_MAX (1)
+#endif
+struct mpi3_man12_sgpio_info {
+ u8 slot_count;
+ u8 reserved01[3];
+ __le32 reserved04;
+ u8 phy_order[32];
+};
+
+struct mpi3_man_page12 {
+ struct mpi3_config_page_header header;
+ __le32 flags;
+ __le32 s_clock_freq;
+ __le32 activity_modulation;
+ u8 num_sgpio;
+ u8 reserved15[3];
+ __le32 reserved18;
+ __le32 reserved1c;
+ __le32 pattern[8];
+ struct mpi3_man12_sgpio_info sgpio_info[MPI3_MAN12_NUM_SGPIO_MAX];
+};
+
+#define MPI3_MAN12_PAGEVERSION (0x00)
+#define MPI3_MAN12_FLAGS_ERROR_PRESENCE_ENABLED (0x0400)
+#define MPI3_MAN12_FLAGS_ACTIVITY_INVERT_ENABLED (0x0200)
+#define MPI3_MAN12_FLAGS_GROUP_ID_DISABLED (0x0100)
+#define MPI3_MAN12_FLAGS_SIO_CLK_FILTER_ENABLED (0x0004)
+#define MPI3_MAN12_FLAGS_SCLOCK_SLOAD_TYPE_MASK (0x0002)
+#define MPI3_MAN12_FLAGS_SCLOCK_SLOAD_TYPE_PUSH_PULL (0x0000)
+#define MPI3_MAN12_FLAGS_SCLOCK_SLOAD_TYPE_OPEN_DRAIN (0x0002)
+#define MPI3_MAN12_FLAGS_SDATAOUT_TYPE_MASK (0x0001)
+#define MPI3_MAN12_FLAGS_SDATAOUT_TYPE_PUSH_PULL (0x0000)
+#define MPI3_MAN12_FLAGS_SDATAOUT_TYPE_OPEN_DRAIN (0x0001)
+#define MPI3_MAN12_SIO_CLK_FREQ_MIN (32)
+#define MPI3_MAN12_SIO_CLK_FREQ_MAX (100000)
+#define MPI3_MAN12_ACTIVITY_MODULATION_FORCE_OFF_MASK (0x0000f000)
+#define MPI3_MAN12_ACTIVITY_MODULATION_FORCE_OFF_SHIFT (12)
+#define MPI3_MAN12_ACTIVITY_MODULATION_MAX_ON_MASK (0x00000f00)
+#define MPI3_MAN12_ACTIVITY_MODULATION_MAX_ON_SHIFT (8)
+#define MPI3_MAN12_ACTIVITY_MODULATION_STRETCH_OFF_MASK (0x000000f0)
+#define MPI3_MAN12_ACTIVITY_MODULATION_STRETCH_OFF_SHIFT (4)
+#define MPI3_MAN12_ACTIVITY_MODULATION_STRETCH_ON_MASK (0x0000000f)
+#define MPI3_MAN12_ACTIVITY_MODULATION_STRETCH_ON_SHIFT (0)
+#define MPI3_MAN12_PATTERN_RATE_MASK (0xe0000000)
+#define MPI3_MAN12_PATTERN_RATE_2_HZ (0x00000000)
+#define MPI3_MAN12_PATTERN_RATE_4_HZ (0x20000000)
+#define MPI3_MAN12_PATTERN_RATE_8_HZ (0x40000000)
+#define MPI3_MAN12_PATTERN_RATE_16_HZ (0x60000000)
+#define MPI3_MAN12_PATTERN_RATE_10_HZ (0x80000000)
+#define MPI3_MAN12_PATTERN_RATE_20_HZ (0xa0000000)
+#define MPI3_MAN12_PATTERN_RATE_40_HZ (0xc0000000)
+#define MPI3_MAN12_PATTERN_LENGTH_MASK (0x1f000000)
+#define MPI3_MAN12_PATTERN_LENGTH_SHIFT (24)
+#define MPI3_MAN12_PATTERN_BIT_PATTERN_MASK (0x00ffffff)
+#define MPI3_MAN12_PATTERN_BIT_PATTERN_SHIFT (0)
+#ifndef MPI3_MAN13_NUM_TRANSLATION_MAX
+#define MPI3_MAN13_NUM_TRANSLATION_MAX (1)
+#endif
+struct mpi3_man13_translation_info {
+ __le32 slot_status;
+ __le32 mask;
+ u8 activity;
+ u8 locate;
+ u8 error;
+ u8 reserved0b;
+};
+
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_FAULT (0x20000000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_DEVICE_OFF (0x10000000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_DEVICE_ACTIVITY (0x00800000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_DO_NOT_REMOVE (0x00400000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_DEVICE_MISSING (0x00100000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_INSERT (0x00080000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_REMOVAL (0x00040000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_IDENTIFY (0x00020000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_OK (0x00008000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_RESERVED_DEVICE (0x00004000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_HOT_SPARE (0x00002000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_CONSISTENCY_CHECK (0x00001000)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000800)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_IN_FAILED_ARRAY (0x00000400)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_REBUILD_REMAP (0x00000200)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_REBUILD_REMAP_ABORT (0x00000100)
+#define MPI3_MAN13_TRANSLATION_SLOTSTATUS_PREDICTED_FAILURE (0x00000040)
+#define MPI3_MAN13_BLINK_PATTERN_FORCE_OFF (0x00)
+#define MPI3_MAN13_BLINK_PATTERN_FORCE_ON (0x01)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_0 (0x02)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_1 (0x03)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_2 (0x04)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_3 (0x05)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_4 (0x06)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_5 (0x07)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_6 (0x08)
+#define MPI3_MAN13_BLINK_PATTERN_PATTERN_7 (0x09)
+#define MPI3_MAN13_BLINK_PATTERN_ACTIVITY (0x0a)
+#define MPI3_MAN13_BLINK_PATTERN_ACTIVITY_TRAIL (0x0b)
+struct mpi3_man_page13 {
+ struct mpi3_config_page_header header;
+ u8 num_trans;
+ u8 reserved09[3];
+ __le32 reserved0c;
+ struct mpi3_man13_translation_info translation[MPI3_MAN13_NUM_TRANSLATION_MAX];
+};
+
+#define MPI3_MAN13_PAGEVERSION (0x00)
+struct mpi3_man_page14 {
+ struct mpi3_config_page_header header;
+ __le16 flags;
+ __le16 reserved0a;
+ u8 num_slot_groups;
+ u8 num_slots;
+ __le16 max_cert_chain_length;
+ __le32 sealed_slots;
+};
+
+#define MPI3_MAN14_PAGEVERSION (0x00)
+#define MPI3_MAN14_FLAGS_AUTH_SESSION_REQ (0x01)
+#define MPI3_MAN14_FLAGS_AUTH_API_MASK (0x0e)
+#define MPI3_MAN14_FLAGS_AUTH_API_NONE (0x00)
+#define MPI3_MAN14_FLAGS_AUTH_API_CEREBUS (0x02)
+#define MPI3_MAN14_FLAGS_AUTH_API_DMTF_PMCI (0x04)
+#ifndef MPI3_MAN15_VERSION_RECORD_MAX
+#define MPI3_MAN15_VERSION_RECORD_MAX 1
+#endif
+struct mpi3_man15_version_record {
+ __le16 spdm_version;
+ __le16 reserved02;
+};
+
+struct mpi3_man_page15 {
+ struct mpi3_config_page_header header;
+ u8 num_version_records;
+ u8 reserved09[3];
+ __le32 reserved0c;
+ struct mpi3_man15_version_record version_record[MPI3_MAN15_VERSION_RECORD_MAX];
+};
+
+#define MPI3_MAN15_PAGEVERSION (0x00)
+#ifndef MPI3_MAN16_CERT_ALGO_MAX
+#define MPI3_MAN16_CERT_ALGO_MAX 1
+#endif
+struct mpi3_man16_certificate_algorithm {
+ u8 slot_group;
+ u8 reserved01[3];
+ __le32 base_asym_algo;
+ __le32 base_hash_algo;
+ __le32 reserved0c[3];
+};
+
+struct mpi3_man_page16 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_cert_algos;
+ u8 reserved0d[3];
+ struct mpi3_man16_certificate_algorithm certificate_algorithm[MPI3_MAN16_CERT_ALGO_MAX];
+};
+
+#define MPI3_MAN16_PAGEVERSION (0x00)
+#ifndef MPI3_MAN17_HASH_ALGORITHM_MAX
+#define MPI3_MAN17_HASH_ALGORITHM_MAX 1
+#endif
+struct mpi3_man17_hash_algorithm {
+ u8 meas_specification;
+ u8 reserved01[3];
+ __le32 measurement_hash_algo;
+ __le32 reserved08[2];
+};
+
+struct mpi3_man_page17 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_hash_algos;
+ u8 reserved0d[3];
+ struct mpi3_man17_hash_algorithm hash_algorithm[MPI3_MAN17_HASH_ALGORITHM_MAX];
+};
+
+#define MPI3_MAN17_PAGEVERSION (0x00)
+struct mpi3_man_page20 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ __le32 nonpremium_features;
+ u8 allowed_personalities;
+ u8 reserved11[3];
+};
+
+#define MPI3_MAN20_PAGEVERSION (0x00)
+#define MPI3_MAN20_ALLOWEDPERSON_RAID_MASK (0x02)
+#define MPI3_MAN20_ALLOWEDPERSON_RAID_ALLOWED (0x02)
+#define MPI3_MAN20_ALLOWEDPERSON_RAID_NOT_ALLOWED (0x00)
+#define MPI3_MAN20_ALLOWEDPERSON_EHBA_MASK (0x01)
+#define MPI3_MAN20_ALLOWEDPERSON_EHBA_ALLOWED (0x01)
+#define MPI3_MAN20_ALLOWEDPERSON_EHBA_NOT_ALLOWED (0x00)
+#define MPI3_MAN20_NONPREMUIM_DISABLE_PD_DEGRADED_MASK (0x01)
+#define MPI3_MAN20_NONPREMUIM_DISABLE_PD_DEGRADED_ENABLED (0x00)
+#define MPI3_MAN20_NONPREMUIM_DISABLE_PD_DEGRADED_DISABLED (0x01)
+struct mpi3_man_page21 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ __le32 flags;
+};
+
+#define MPI3_MAN21_PAGEVERSION (0x00)
+#define MPI3_MAN21_FLAGS_HOST_METADATA_CAPABILITY_MASK (0x80)
+#define MPI3_MAN21_FLAGS_HOST_METADATA_CAPABILITY_ENABLED (0x80)
+#define MPI3_MAN21_FLAGS_HOST_METADATA_CAPABILITY_DISABLED (0x00)
+#define MPI3_MAN21_FLAGS_UNCERTIFIED_DRIVES_MASK (0x60)
+#define MPI3_MAN21_FLAGS_UNCERTIFIED_DRIVES_BLOCK (0x00)
+#define MPI3_MAN21_FLAGS_UNCERTIFIED_DRIVES_ALLOW (0x20)
+#define MPI3_MAN21_FLAGS_UNCERTIFIED_DRIVES_WARN (0x40)
+#define MPI3_MAN21_FLAGS_BLOCK_SSD_WR_CACHE_CHANGE_MASK (0x08)
+#define MPI3_MAN21_FLAGS_BLOCK_SSD_WR_CACHE_CHANGE_ALLOW (0x00)
+#define MPI3_MAN21_FLAGS_BLOCK_SSD_WR_CACHE_CHANGE_PREVENT (0x08)
+#define MPI3_MAN21_FLAGS_SES_VPD_ASSOC_MASK (0x01)
+#define MPI3_MAN21_FLAGS_SES_VPD_ASSOC_DEFAULT (0x00)
+#define MPI3_MAN21_FLAGS_SES_VPD_ASSOC_OEM_SPECIFIC (0x01)
+#ifndef MPI3_MAN_PROD_SPECIFIC_MAX
+#define MPI3_MAN_PROD_SPECIFIC_MAX (1)
+#endif
+struct mpi3_man_page_product_specific {
+ struct mpi3_config_page_header header;
+ __le32 product_specific_info[MPI3_MAN_PROD_SPECIFIC_MAX];
+};
+
+struct mpi3_io_unit_page0 {
+ struct mpi3_config_page_header header;
+ __le64 unique_value;
+ __le32 nvdata_version_default;
+ __le32 nvdata_version_persistent;
+};
+
+#define MPI3_IOUNIT0_PAGEVERSION (0x00)
+struct mpi3_io_unit_page1 {
+ struct mpi3_config_page_header header;
+ __le32 flags;
+ u8 dmd_io_delay;
+ u8 dmd_report_pc_ie;
+ u8 dmd_report_sata;
+ u8 dmd_report_sas;
+};
+
+#define MPI3_IOUNIT1_PAGEVERSION (0x00)
+#define MPI3_IOUNIT1_FLAGS_NVME_WRITE_CACHE_MASK (0x00000030)
+#define MPI3_IOUNIT1_FLAGS_NVME_WRITE_CACHE_ENABLE (0x00000000)
+#define MPI3_IOUNIT1_FLAGS_NVME_WRITE_CACHE_DISABLE (0x00000010)
+#define MPI3_IOUNIT1_FLAGS_NVME_WRITE_CACHE_NO_MODIFY (0x00000020)
+#define MPI3_IOUNIT1_FLAGS_ATA_SECURITY_FREEZE_LOCK (0x00000008)
+#define MPI3_IOUNIT1_FLAGS_WRITE_SAME_BUFFER (0x00000004)
+#define MPI3_IOUNIT1_FLAGS_SATA_WRITE_CACHE_MASK (0x00000003)
+#define MPI3_IOUNIT1_FLAGS_SATA_WRITE_CACHE_ENABLE (0x00000000)
+#define MPI3_IOUNIT1_FLAGS_SATA_WRITE_CACHE_DISABLE (0x00000001)
+#define MPI3_IOUNIT1_FLAGS_SATA_WRITE_CACHE_UNCHANGED (0x00000002)
+#define MPI3_IOUNIT1_DMD_REPORT_DELAY_TIME_MASK (0x7f)
+#define MPI3_IOUNIT1_DMD_REPORT_UNIT_16_SEC (0x80)
+#ifndef MPI3_IO_UNIT2_GPIO_VAL_MAX
+#define MPI3_IO_UNIT2_GPIO_VAL_MAX (1)
+#endif
+struct mpi3_io_unit_page2 {
+ struct mpi3_config_page_header header;
+ u8 gpio_count;
+ u8 reserved09[3];
+ __le16 gpio_val[MPI3_IO_UNIT2_GPIO_VAL_MAX];
+};
+
+#define MPI3_IOUNIT2_PAGEVERSION (0x00)
+#define MPI3_IOUNIT2_GPIO_FUNCTION_MASK (0xfffc)
+#define MPI3_IOUNIT2_GPIO_FUNCTION_SHIFT (2)
+#define MPI3_IOUNIT2_GPIO_SETTING_MASK (0x0001)
+#define MPI3_IOUNIT2_GPIO_SETTING_OFF (0x0000)
+#define MPI3_IOUNIT2_GPIO_SETTING_ON (0x0001)
+struct mpi3_io_unit3_sensor {
+ __le16 flags;
+ __le16 reserved02;
+ __le16 threshold[4];
+ __le32 reserved0c;
+ __le32 reserved10;
+ __le32 reserved14;
+};
+
+#define MPI3_IOUNIT3_SENSOR_FLAGS_T3_ENABLE (0x0008)
+#define MPI3_IOUNIT3_SENSOR_FLAGS_T2_ENABLE (0x0004)
+#define MPI3_IOUNIT3_SENSOR_FLAGS_T1_ENABLE (0x0002)
+#define MPI3_IOUNIT3_SENSOR_FLAGS_T0_ENABLE (0x0001)
+#ifndef MPI3_IO_UNIT3_SENSOR_MAX
+#define MPI3_IO_UNIT3_SENSOR_MAX (1)
+#endif
+struct mpi3_io_unit_page3 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_sensors;
+ u8 polling_interval;
+ __le16 reserved0e;
+ struct mpi3_io_unit3_sensor sensor[MPI3_IO_UNIT3_SENSOR_MAX];
+};
+
+#define MPI3_IOUNIT3_PAGEVERSION (0x00)
+struct mpi3_io_unit4_sensor {
+ __le16 current_temperature;
+ __le16 reserved02;
+ u8 flags;
+ u8 reserved05[3];
+ __le32 reserved08;
+ __le32 reserved0c;
+};
+
+#define MPI3_IOUNIT4_SENSOR_FLAGS_TEMP_VALID (0x01)
+#ifndef MPI3_IO_UNIT4_SENSOR_MAX
+#define MPI3_IO_UNIT4_SENSOR_MAX (1)
+#endif
+struct mpi3_io_unit_page4 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_sensors;
+ u8 reserved0d[3];
+ struct mpi3_io_unit4_sensor sensor[MPI3_IO_UNIT4_SENSOR_MAX];
+};
+
+#define MPI3_IOUNIT4_PAGEVERSION (0x00)
+struct mpi3_io_unit5_spinup_group {
+ u8 max_target_spinup;
+ u8 spinup_delay;
+ u8 spinup_flags;
+ u8 reserved03;
+};
+
+#define MPI3_IOUNIT5_SPINUP_FLAGS_DISABLE (0x01)
+#ifndef MPI3_IO_UNIT5_PHY_MAX
+#define MPI3_IO_UNIT5_PHY_MAX (4)
+#endif
+struct mpi3_io_unit_page5 {
+ struct mpi3_config_page_header header;
+ struct mpi3_io_unit5_spinup_group spinup_group_parameters[4];
+ __le32 reserved18;
+ __le32 reserved1c;
+ __le32 reserved20;
+ u8 reserved24;
+ u8 sata_device_wait_time;
+ u8 spinup_encl_drive_count;
+ u8 spinup_encl_delay;
+ u8 num_phys;
+ u8 pe_initial_spinup_delay;
+ u8 topology_stable_time;
+ u8 flags;
+ u8 phy[MPI3_IO_UNIT5_PHY_MAX];
+};
+
+#define MPI3_IOUNIT5_PAGEVERSION (0x00)
+#define MPI3_IOUNIT5_FLAGS_POWER_CAPABLE_SPINUP (0x02)
+#define MPI3_IOUNIT5_FLAGS_AUTO_PORT_ENABLE (0x01)
+#define MPI3_IOUNIT5_PHY_SPINUP_GROUP_MASK (0x03)
+struct mpi3_io_unit_page6 {
+ struct mpi3_config_page_header header;
+ __le32 board_power_requirement;
+ __le32 pci_slot_power_allocation;
+ u8 flags;
+ u8 reserved11[3];
+};
+
+#define MPI3_IOUNIT6_PAGEVERSION (0x00)
+#define MPI3_IOUNIT6_FLAGS_ACT_CABLE_PWR_EXC (0x01)
+struct mpi3_io_unit_page7 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+};
+
+#define MPI3_IOUNIT7_PAGEVERSION (0x00)
+#ifndef MPI3_IOUNIT8_DIGEST_MAX
+#define MPI3_IOUNIT8_DIGEST_MAX (1)
+#endif
+union mpi3_iounit8_digest {
+ __le32 dword[16];
+ __le16 word[32];
+ u8 byte[64];
+};
+
+struct mpi3_io_unit_page8 {
+ struct mpi3_config_page_header header;
+ u8 sb_mode;
+ u8 sb_state;
+ __le16 reserved0a;
+ u8 num_slots;
+ u8 slots_available;
+ u8 current_key_encryption_algo;
+ u8 key_digest_hash_algo;
+ __le32 reserved10[2];
+ __le32 current_key[128];
+ union mpi3_iounit8_digest digest[MPI3_IOUNIT8_DIGEST_MAX];
+};
+
+#define MPI3_IOUNIT8_PAGEVERSION (0x00)
+#define MPI3_IOUNIT8_SBMODE_SECURE_DEBUG (0x04)
+#define MPI3_IOUNIT8_SBMODE_HARD_SECURE (0x02)
+#define MPI3_IOUNIT8_SBMODE_CONFIG_SECURE (0x01)
+#define MPI3_IOUNIT8_SBSTATE_KEY_UPDATE_PENDING (0x02)
+#define MPI3_IOUNIT8_SBSTATE_SECURE_BOOT_ENABLED (0x01)
+struct mpi3_io_unit_page9 {
+ struct mpi3_config_page_header header;
+ __le32 flags;
+ __le16 first_device;
+ __le16 reserved0e;
+};
+
+#define MPI3_IOUNIT9_PAGEVERSION (0x00)
+#define MPI3_IOUNIT9_FLAGS_VDFIRST_ENABLED (0x01)
+#define MPI3_IOUNIT9_FIRSTDEVICE_UNKNOWN (0xffff)
+struct mpi3_ioc_page0 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ __le16 vendor_id;
+ __le16 device_id;
+ u8 revision_id;
+ u8 reserved11[3];
+ __le32 class_code;
+ __le16 subsystem_vendor_id;
+ __le16 subsystem_id;
+};
+
+#define MPI3_IOC0_PAGEVERSION (0x00)
+struct mpi3_ioc_page1 {
+ struct mpi3_config_page_header header;
+ __le32 coalescing_timeout;
+ u8 coalescing_depth;
+ u8 pci_slot_num;
+ __le16 reserved0e;
+};
+
+#define MPI3_IOC1_PAGEVERSION (0x00)
+#define MPI3_IOC1_PCISLOTNUM_UNKNOWN (0xff)
+#ifndef MPI3_IOC2_EVENTMASK_WORDS
+#define MPI3_IOC2_EVENTMASK_WORDS (4)
+#endif
+struct mpi3_ioc_page2 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ __le16 sas_broadcast_primitive_masks;
+ __le16 sas_notify_primitive_masks;
+ __le32 event_masks[MPI3_IOC2_EVENTMASK_WORDS];
+};
+
+#define MPI3_IOC2_PAGEVERSION (0x00)
+struct mpi3_uefibsd_page0 {
+ struct mpi3_config_page_header header;
+ __le32 bsd_options;
+ u8 ssu_timeout;
+ u8 io_timeout;
+ u8 tur_retries;
+ u8 tur_interval;
+ u8 reserved10;
+ u8 security_key_timeout;
+ __le16 reserved12;
+ __le32 reserved14;
+ __le32 reserved18;
+};
+
+#define MPI3_UEFIBSD_PAGEVERSION (0x00)
+#define MPI3_UEFIBSD_BSDOPTS_REGISTRATION_MASK (0x00000003)
+#define MPI3_UEFIBSD_BSDOPTS_REGISTRATION_IOC_AND_DEVS (0x00000000)
+#define MPI3_UEFIBSD_BSDOPTS_REGISTRATION_IOC_ONLY (0x00000001)
+#define MPI3_UEFIBSD_BSDOPTS_REGISTRATION_NONE (0x00000002)
+#define MPI3_UEFIBSD_BSDOPTS_DIS_HII_CONFIG_UTIL (0x00000004)
+#define MPI3_UEFIBSD_BSDOPTS_EN_ADV_ADAPTER_CONFIG (0x00000008)
+union mpi3_security_mac {
+ __le32 dword[16];
+ __le16 word[32];
+ u8 byte[64];
+};
+
+union mpi3_security_nonce {
+ __le32 dword[16];
+ __le16 word[32];
+ u8 byte[64];
+};
+
+union mpi3_security0_cert_chain {
+ __le32 dword[1024];
+ __le16 word[2048];
+ u8 byte[4096];
+};
+
+struct mpi3_security_page0 {
+ struct mpi3_config_page_header header;
+ u8 slot_num_group;
+ u8 slot_num;
+ __le16 cert_chain_length;
+ u8 cert_chain_flags;
+ u8 reserved0d[3];
+ __le32 base_asym_algo;
+ __le32 base_hash_algo;
+ __le32 reserved18[4];
+ union mpi3_security_mac mac;
+ union mpi3_security_nonce nonce;
+ union mpi3_security0_cert_chain certificate_chain;
+};
+
+#define MPI3_SECURITY0_PAGEVERSION (0x00)
+#define MPI3_SECURITY0_CERTCHAIN_FLAGS_AUTH_API_MASK (0x0e)
+#define MPI3_SECURITY0_CERTCHAIN_FLAGS_AUTH_API_UNUSED (0x00)
+#define MPI3_SECURITY0_CERTCHAIN_FLAGS_AUTH_API_CERBERUS (0x02)
+#define MPI3_SECURITY0_CERTCHAIN_FLAGS_AUTH_API_SPDM (0x04)
+#define MPI3_SECURITY0_CERTCHAIN_FLAGS_SEALED (0x01)
+#ifndef MPI3_SECURITY1_KEY_RECORD_MAX
+#define MPI3_SECURITY1_KEY_RECORD_MAX 1
+#endif
+#ifndef MPI3_SECURITY1_PAD_MAX
+#define MPI3_SECURITY1_PAD_MAX 1
+#endif
+union mpi3_security1_key_data {
+ __le32 dword[128];
+ __le16 word[256];
+ u8 byte[512];
+};
+
+struct mpi3_security1_key_record {
+ u8 flags;
+ u8 consumer;
+ __le16 key_data_size;
+ __le32 additional_key_data;
+ __le32 reserved08[2];
+ union mpi3_security1_key_data key_data;
+};
+
+#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_MASK (0x1f)
+#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_NOT_VALID (0x00)
+#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_HMAC (0x01)
+#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_AES (0x02)
+#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_ECDSA_PRIVATE (0x03)
+#define MPI3_SECURITY1_KEY_RECORD_FLAGS_TYPE_ECDSA_PUBLIC (0x04)
+#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_NOT_VALID (0x00)
+#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_SAFESTORE (0x01)
+#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_CERT_CHAIN (0x02)
+#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_AUTH_DEV_KEY (0x03)
+#define MPI3_SECURITY1_KEY_RECORD_CONSUMER_CACHE_OFFLOAD (0x04)
+struct mpi3_security_page1 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08[2];
+ union mpi3_security_mac mac;
+ union mpi3_security_nonce nonce;
+ u8 num_keys;
+ u8 reserved91[3];
+ __le32 reserved94[3];
+ struct mpi3_security1_key_record key_record[MPI3_SECURITY1_KEY_RECORD_MAX];
+ u8 pad[MPI3_SECURITY1_PAD_MAX];
+};
+
+#define MPI3_SECURITY1_PAGEVERSION (0x00)
+struct mpi3_sas_io_unit0_phy_data {
+ u8 io_unit_port;
+ u8 port_flags;
+ u8 phy_flags;
+ u8 negotiated_link_rate;
+ __le16 controller_phy_device_info;
+ __le16 reserved06;
+ __le16 attached_dev_handle;
+ __le16 controller_dev_handle;
+ __le32 discovery_status;
+ __le32 reserved10;
+};
+
+#ifndef MPI3_SAS_IO_UNIT0_PHY_MAX
+#define MPI3_SAS_IO_UNIT0_PHY_MAX (1)
+#endif
+struct mpi3_sas_io_unit_page0 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_phys;
+ u8 reserved0d[3];
+ struct mpi3_sas_io_unit0_phy_data phy_data[MPI3_SAS_IO_UNIT0_PHY_MAX];
+};
+
+#define MPI3_SASIOUNIT0_PAGEVERSION (0x00)
+#define MPI3_SASIOUNIT0_PORTFLAGS_DISC_IN_PROGRESS (0x08)
+#define MPI3_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG (0x01)
+#define MPI3_SASIOUNIT0_PHYFLAGS_INIT_PERSIST_CONNECT (0x40)
+#define MPI3_SASIOUNIT0_PHYFLAGS_TARG_PERSIST_CONNECT (0x20)
+#define MPI3_SASIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08)
+struct mpi3_sas_io_unit1_phy_data {
+ u8 io_unit_port;
+ u8 port_flags;
+ u8 phy_flags;
+ u8 max_min_link_rate;
+ __le16 controller_phy_device_info;
+ __le16 max_target_port_connect_time;
+ __le32 reserved08;
+};
+
+#ifndef MPI3_SAS_IO_UNIT1_PHY_MAX
+#define MPI3_SAS_IO_UNIT1_PHY_MAX (1)
+#endif
+struct mpi3_sas_io_unit_page1 {
+ struct mpi3_config_page_header header;
+ __le16 control_flags;
+ __le16 sas_narrow_max_queue_depth;
+ __le16 additional_control_flags;
+ __le16 sas_wide_max_queue_depth;
+ u8 num_phys;
+ u8 sata_max_q_depth;
+ __le16 reserved12;
+ struct mpi3_sas_io_unit1_phy_data phy_data[MPI3_SAS_IO_UNIT1_PHY_MAX];
+};
+
+#define MPI3_SASIOUNIT1_PAGEVERSION (0x00)
+#define MPI3_SASIOUNIT1_CONTROL_CONTROLLER_DEVICE_SELF_TEST (0x8000)
+#define MPI3_SASIOUNIT1_CONTROL_SATA_SW_PRESERVE (0x1000)
+#define MPI3_SASIOUNIT1_CONTROL_SATA_48BIT_LBA_REQUIRED (0x0080)
+#define MPI3_SASIOUNIT1_CONTROL_SATA_SMART_REQUIRED (0x0040)
+#define MPI3_SASIOUNIT1_CONTROL_SATA_NCQ_REQUIRED (0x0020)
+#define MPI3_SASIOUNIT1_CONTROL_SATA_FUA_REQUIRED (0x0010)
+#define MPI3_SASIOUNIT1_CONTROL_TABLE_SUBTRACTIVE_ILLEGAL (0x0008)
+#define MPI3_SASIOUNIT1_CONTROL_SUBTRACTIVE_ILLEGAL (0x0004)
+#define MPI3_SASIOUNIT1_CONTROL_FIRST_LVL_DISC_ONLY (0x0002)
+#define MPI3_SASIOUNIT1_CONTROL_HARD_RESET_MASK (0x0001)
+#define MPI3_SASIOUNIT1_CONTROL_HARD_RESET_DEVICE_NAME (0x0000)
+#define MPI3_SASIOUNIT1_CONTROL_HARD_RESET_SAS_ADDRESS (0x0001)
+#define MPI3_SASIOUNIT1_ACONTROL_DA_PERSIST_CONNECT (0x0100)
+#define MPI3_SASIOUNIT1_ACONTROL_MULTI_PORT_DOMAIN_ILLEGAL (0x0080)
+#define MPI3_SASIOUNIT1_ACONTROL_SATA_ASYNCHROUNOUS_NOTIFICATION (0x0040)
+#define MPI3_SASIOUNIT1_ACONTROL_INVALID_TOPOLOGY_CORRECTION (0x0020)
+#define MPI3_SASIOUNIT1_ACONTROL_PORT_ENABLE_ONLY_SATA_LINK_RESET (0x0010)
+#define MPI3_SASIOUNIT1_ACONTROL_OTHER_AFFILIATION_SATA_LINK_RESET (0x0008)
+#define MPI3_SASIOUNIT1_ACONTROL_SELF_AFFILIATION_SATA_LINK_RESET (0x0004)
+#define MPI3_SASIOUNIT1_ACONTROL_NO_AFFILIATION_SATA_LINK_RESET (0x0002)
+#define MPI3_SASIOUNIT1_ACONTROL_ALLOW_TABLE_TO_TABLE (0x0001)
+#define MPI3_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01)
+#define MPI3_SASIOUNIT1_PHYFLAGS_INIT_PERSIST_CONNECT (0x40)
+#define MPI3_SASIOUNIT1_PHYFLAGS_TARG_PERSIST_CONNECT (0x20)
+#define MPI3_SASIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08)
+#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_MASK (0xf0)
+#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_SHIFT (4)
+#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_6_0 (0xa0)
+#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_12_0 (0xb0)
+#define MPI3_SASIOUNIT1_MMLR_MAX_RATE_22_5 (0xc0)
+#define MPI3_SASIOUNIT1_MMLR_MIN_RATE_MASK (0x0f)
+#define MPI3_SASIOUNIT1_MMLR_MIN_RATE_6_0 (0x0a)
+#define MPI3_SASIOUNIT1_MMLR_MIN_RATE_12_0 (0x0b)
+#define MPI3_SASIOUNIT1_MMLR_MIN_RATE_22_5 (0x0c)
+struct mpi3_sas_io_unit2_phy_pm_settings {
+ u8 control_flags;
+ u8 reserved01;
+ __le16 inactivity_timer_exponent;
+ u8 sata_partial_timeout;
+ u8 reserved05;
+ u8 sata_slumber_timeout;
+ u8 reserved07;
+ u8 sas_partial_timeout;
+ u8 reserved09;
+ u8 sas_slumber_timeout;
+ u8 reserved0b;
+};
+
+#ifndef MPI3_SAS_IO_UNIT2_PHY_MAX
+#define MPI3_SAS_IO_UNIT2_PHY_MAX (1)
+#endif
+struct mpi3_sas_io_unit_page2 {
+ struct mpi3_config_page_header header;
+ u8 num_phys;
+ u8 reserved09[3];
+ __le32 reserved0c;
+ struct mpi3_sas_io_unit2_phy_pm_settings sas_phy_power_management_settings[MPI3_SAS_IO_UNIT2_PHY_MAX];
+};
+
+#define MPI3_SASIOUNIT2_PAGEVERSION (0x00)
+#define MPI3_SASIOUNIT2_CONTROL_SAS_SLUMBER_ENABLE (0x08)
+#define MPI3_SASIOUNIT2_CONTROL_SAS_PARTIAL_ENABLE (0x04)
+#define MPI3_SASIOUNIT2_CONTROL_SATA_SLUMBER_ENABLE (0x02)
+#define MPI3_SASIOUNIT2_CONTROL_SATA_PARTIAL_ENABLE (0x01)
+#define MPI3_SASIOUNIT2_ITE_SAS_SLUMBER_MASK (0x7000)
+#define MPI3_SASIOUNIT2_ITE_SAS_SLUMBER_SHIFT (12)
+#define MPI3_SASIOUNIT2_ITE_SAS_PARTIAL_MASK (0x0700)
+#define MPI3_SASIOUNIT2_ITE_SAS_PARTIAL_SHIFT (8)
+#define MPI3_SASIOUNIT2_ITE_SATA_SLUMBER_MASK (0x0070)
+#define MPI3_SASIOUNIT2_ITE_SATA_SLUMBER_SHIFT (4)
+#define MPI3_SASIOUNIT2_ITE_SATA_PARTIAL_MASK (0x0007)
+#define MPI3_SASIOUNIT2_ITE_SATA_PARTIAL_SHIFT (0)
+#define MPI3_SASIOUNIT2_ITE_EXP_TEN_SECONDS (7)
+#define MPI3_SASIOUNIT2_ITE_EXP_ONE_SECOND (6)
+#define MPI3_SASIOUNIT2_ITE_EXP_HUNDRED_MILLISECONDS (5)
+#define MPI3_SASIOUNIT2_ITE_EXP_TEN_MILLISECONDS (4)
+#define MPI3_SASIOUNIT2_ITE_EXP_ONE_MILLISECOND (3)
+#define MPI3_SASIOUNIT2_ITE_EXP_HUNDRED_MICROSECONDS (2)
+#define MPI3_SASIOUNIT2_ITE_EXP_TEN_MICROSECONDS (1)
+#define MPI3_SASIOUNIT2_ITE_EXP_ONE_MICROSECOND (0)
+struct mpi3_sas_io_unit_page3 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ __le32 power_management_capabilities;
+};
+
+#define MPI3_SASIOUNIT3_PAGEVERSION (0x00)
+#define MPI3_SASIOUNIT3_PM_HOST_SAS_SLUMBER_MODE (0x00000800)
+#define MPI3_SASIOUNIT3_PM_HOST_SAS_PARTIAL_MODE (0x00000400)
+#define MPI3_SASIOUNIT3_PM_HOST_SATA_SLUMBER_MODE (0x00000200)
+#define MPI3_SASIOUNIT3_PM_HOST_SATA_PARTIAL_MODE (0x00000100)
+#define MPI3_SASIOUNIT3_PM_IOUNIT_SAS_SLUMBER_MODE (0x00000008)
+#define MPI3_SASIOUNIT3_PM_IOUNIT_SAS_PARTIAL_MODE (0x00000004)
+#define MPI3_SASIOUNIT3_PM_IOUNIT_SATA_SLUMBER_MODE (0x00000002)
+#define MPI3_SASIOUNIT3_PM_IOUNIT_SATA_PARTIAL_MODE (0x00000001)
+struct mpi3_sas_expander_page0 {
+ struct mpi3_config_page_header header;
+ u8 io_unit_port;
+ u8 report_gen_length;
+ __le16 enclosure_handle;
+ __le32 reserved0c;
+ __le64 sas_address;
+ __le32 discovery_status;
+ __le16 dev_handle;
+ __le16 parent_dev_handle;
+ __le16 expander_change_count;
+ __le16 expander_route_indexes;
+ u8 num_phys;
+ u8 sas_level;
+ __le16 flags;
+ __le16 stp_bus_inactivity_time_limit;
+ __le16 stp_max_connect_time_limit;
+ __le16 stp_smp_nexus_loss_time;
+ __le16 max_num_routed_sas_addresses;
+ __le64 active_zone_manager_sas_address;
+ __le16 zone_lock_inactivity_limit;
+ __le16 reserved3a;
+ u8 time_to_reduced_func;
+ u8 initial_time_to_reduced_func;
+ u8 max_reduced_func_time;
+ u8 exp_status;
+};
+
+#define MPI3_SASEXPANDER0_PAGEVERSION (0x00)
+#define MPI3_SASEXPANDER0_FLAGS_REDUCED_FUNCTIONALITY (0x2000)
+#define MPI3_SASEXPANDER0_FLAGS_ZONE_LOCKED (0x1000)
+#define MPI3_SASEXPANDER0_FLAGS_SUPPORTED_PHYSICAL_PRES (0x0800)
+#define MPI3_SASEXPANDER0_FLAGS_ASSERTED_PHYSICAL_PRES (0x0400)
+#define MPI3_SASEXPANDER0_FLAGS_ZONING_SUPPORT (0x0200)
+#define MPI3_SASEXPANDER0_FLAGS_ENABLED_ZONING (0x0100)
+#define MPI3_SASEXPANDER0_FLAGS_TABLE_TO_TABLE_SUPPORT (0x0080)
+#define MPI3_SASEXPANDER0_FLAGS_CONNECTOR_END_DEVICE (0x0010)
+#define MPI3_SASEXPANDER0_FLAGS_OTHERS_CONFIG (0x0004)
+#define MPI3_SASEXPANDER0_FLAGS_CONFIG_IN_PROGRESS (0x0002)
+#define MPI3_SASEXPANDER0_FLAGS_ROUTE_TABLE_CONFIG (0x0001)
+#define MPI3_SASEXPANDER0_ES_NOT_RESPONDING (0x02)
+#define MPI3_SASEXPANDER0_ES_RESPONDING (0x03)
+#define MPI3_SASEXPANDER0_ES_DELAY_NOT_RESPONDING (0x04)
+struct mpi3_sas_expander_page1 {
+ struct mpi3_config_page_header header;
+ u8 io_unit_port;
+ u8 reserved09[3];
+ u8 num_phys;
+ u8 phy;
+ __le16 num_table_entries_programmed;
+ u8 programmed_link_rate;
+ u8 hw_link_rate;
+ __le16 attached_dev_handle;
+ __le32 phy_info;
+ __le16 attached_device_info;
+ __le16 reserved1a;
+ __le16 expander_dev_handle;
+ u8 change_count;
+ u8 negotiated_link_rate;
+ u8 phy_identifier;
+ u8 attached_phy_identifier;
+ u8 reserved22;
+ u8 discovery_info;
+ __le32 attached_phy_info;
+ u8 zone_group;
+ u8 self_config_status;
+ __le16 reserved2a;
+ __le16 slot;
+ __le16 slot_index;
+};
+
+#define MPI3_SASEXPANDER1_PAGEVERSION (0x00)
+#define MPI3_SASEXPANDER1_DISCINFO_BAD_PHY_DISABLED (0x04)
+#define MPI3_SASEXPANDER1_DISCINFO_LINK_STATUS_CHANGE (0x02)
+#define MPI3_SASEXPANDER1_DISCINFO_NO_ROUTING_ENTRIES (0x01)
+struct mpi3_sas_port_page0 {
+ struct mpi3_config_page_header header;
+ u8 port_number;
+ u8 reserved09;
+ u8 port_width;
+ u8 reserved0b;
+ u8 zone_group;
+ u8 reserved0d[3];
+ __le64 sas_address;
+ __le16 device_info;
+ __le16 reserved1a;
+ __le32 reserved1c;
+};
+
+#define MPI3_SASPORT0_PAGEVERSION (0x00)
+struct mpi3_sas_phy_page0 {
+ struct mpi3_config_page_header header;
+ __le16 owner_dev_handle;
+ __le16 reserved0a;
+ __le16 attached_dev_handle;
+ u8 attached_phy_identifier;
+ u8 reserved0f;
+ __le32 attached_phy_info;
+ u8 programmed_link_rate;
+ u8 hw_link_rate;
+ u8 change_count;
+ u8 flags;
+ __le32 phy_info;
+ u8 negotiated_link_rate;
+ u8 reserved1d[3];
+ __le16 slot;
+ __le16 slot_index;
+};
+
+#define MPI3_SASPHY0_PAGEVERSION (0x00)
+#define MPI3_SASPHY0_FLAGS_SGPIO_DIRECT_ATTACH_ENC (0x01)
+struct mpi3_sas_phy_page1 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ __le32 invalid_dword_count;
+ __le32 running_disparity_error_count;
+ __le32 loss_dword_synch_count;
+ __le32 phy_reset_problem_count;
+};
+
+#define MPI3_SASPHY1_PAGEVERSION (0x00)
+struct mpi3_sas_phy2_phy_event {
+ u8 phy_event_code;
+ u8 reserved01[3];
+ __le32 phy_event_info;
+};
+
+#ifndef MPI3_SAS_PHY2_PHY_EVENT_MAX
+#define MPI3_SAS_PHY2_PHY_EVENT_MAX (1)
+#endif
+struct mpi3_sas_phy_page2 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_phy_events;
+ u8 reserved0d[3];
+ struct mpi3_sas_phy2_phy_event phy_event[MPI3_SAS_PHY2_PHY_EVENT_MAX];
+};
+
+#define MPI3_SASPHY2_PAGEVERSION (0x00)
+struct mpi3_sas_phy3_phy_event_config {
+ u8 phy_event_code;
+ u8 reserved01[3];
+ u8 counter_type;
+ u8 threshold_window;
+ u8 time_units;
+ u8 reserved07;
+ __le32 event_threshold;
+ __le16 threshold_flags;
+ __le16 reserved0e;
+};
+
+#define MPI3_SASPHY3_EVENT_CODE_NO_EVENT (0x00)
+#define MPI3_SASPHY3_EVENT_CODE_INVALID_DWORD (0x01)
+#define MPI3_SASPHY3_EVENT_CODE_RUNNING_DISPARITY_ERROR (0x02)
+#define MPI3_SASPHY3_EVENT_CODE_LOSS_DWORD_SYNC (0x03)
+#define MPI3_SASPHY3_EVENT_CODE_PHY_RESET_PROBLEM (0x04)
+#define MPI3_SASPHY3_EVENT_CODE_ELASTICITY_BUF_OVERFLOW (0x05)
+#define MPI3_SASPHY3_EVENT_CODE_RX_ERROR (0x06)
+#define MPI3_SASPHY3_EVENT_CODE_INV_SPL_PACKETS (0x07)
+#define MPI3_SASPHY3_EVENT_CODE_LOSS_SPL_PACKET_SYNC (0x08)
+#define MPI3_SASPHY3_EVENT_CODE_RX_ADDR_FRAME_ERROR (0x20)
+#define MPI3_SASPHY3_EVENT_CODE_TX_AC_OPEN_REJECT (0x21)
+#define MPI3_SASPHY3_EVENT_CODE_RX_AC_OPEN_REJECT (0x22)
+#define MPI3_SASPHY3_EVENT_CODE_TX_RC_OPEN_REJECT (0x23)
+#define MPI3_SASPHY3_EVENT_CODE_RX_RC_OPEN_REJECT (0x24)
+#define MPI3_SASPHY3_EVENT_CODE_RX_AIP_PARTIAL_WAITING_ON (0x25)
+#define MPI3_SASPHY3_EVENT_CODE_RX_AIP_CONNECT_WAITING_ON (0x26)
+#define MPI3_SASPHY3_EVENT_CODE_TX_BREAK (0x27)
+#define MPI3_SASPHY3_EVENT_CODE_RX_BREAK (0x28)
+#define MPI3_SASPHY3_EVENT_CODE_BREAK_TIMEOUT (0x29)
+#define MPI3_SASPHY3_EVENT_CODE_CONNECTION (0x2a)
+#define MPI3_SASPHY3_EVENT_CODE_PEAKTX_PATHWAY_BLOCKED (0x2b)
+#define MPI3_SASPHY3_EVENT_CODE_PEAKTX_ARB_WAIT_TIME (0x2c)
+#define MPI3_SASPHY3_EVENT_CODE_PEAK_ARB_WAIT_TIME (0x2d)
+#define MPI3_SASPHY3_EVENT_CODE_PEAK_CONNECT_TIME (0x2e)
+#define MPI3_SASPHY3_EVENT_CODE_PERSIST_CONN (0x2f)
+#define MPI3_SASPHY3_EVENT_CODE_TX_SSP_FRAMES (0x40)
+#define MPI3_SASPHY3_EVENT_CODE_RX_SSP_FRAMES (0x41)
+#define MPI3_SASPHY3_EVENT_CODE_TX_SSP_ERROR_FRAMES (0x42)
+#define MPI3_SASPHY3_EVENT_CODE_RX_SSP_ERROR_FRAMES (0x43)
+#define MPI3_SASPHY3_EVENT_CODE_TX_CREDIT_BLOCKED (0x44)
+#define MPI3_SASPHY3_EVENT_CODE_RX_CREDIT_BLOCKED (0x45)
+#define MPI3_SASPHY3_EVENT_CODE_TX_SATA_FRAMES (0x50)
+#define MPI3_SASPHY3_EVENT_CODE_RX_SATA_FRAMES (0x51)
+#define MPI3_SASPHY3_EVENT_CODE_SATA_OVERFLOW (0x52)
+#define MPI3_SASPHY3_EVENT_CODE_TX_SMP_FRAMES (0x60)
+#define MPI3_SASPHY3_EVENT_CODE_RX_SMP_FRAMES (0x61)
+#define MPI3_SASPHY3_EVENT_CODE_RX_SMP_ERROR_FRAMES (0x63)
+#define MPI3_SASPHY3_EVENT_CODE_HOTPLUG_TIMEOUT (0xd0)
+#define MPI3_SASPHY3_EVENT_CODE_MISALIGNED_MUX_PRIMITIVE (0xd1)
+#define MPI3_SASPHY3_EVENT_CODE_RX_AIP (0xd2)
+#define MPI3_SASPHY3_EVENT_CODE_LCARB_WAIT_TIME (0xd3)
+#define MPI3_SASPHY3_EVENT_CODE_RCVD_CONN_RESP_WAIT_TIME (0xd4)
+#define MPI3_SASPHY3_EVENT_CODE_LCCONN_TIME (0xd5)
+#define MPI3_SASPHY3_EVENT_CODE_SSP_TX_START_TRANSMIT (0xd6)
+#define MPI3_SASPHY3_EVENT_CODE_SATA_TX_START (0xd7)
+#define MPI3_SASPHY3_EVENT_CODE_SMP_TX_START_TRANSMT (0xd8)
+#define MPI3_SASPHY3_EVENT_CODE_TX_SMP_BREAK_CONN (0xd9)
+#define MPI3_SASPHY3_EVENT_CODE_SSP_RX_START_RECEIVE (0xda)
+#define MPI3_SASPHY3_EVENT_CODE_SATA_RX_START_RECEIVE (0xdb)
+#define MPI3_SASPHY3_EVENT_CODE_SMP_RX_START_RECEIVE (0xdc)
+#define MPI3_SASPHY3_COUNTER_TYPE_WRAPPING (0x00)
+#define MPI3_SASPHY3_COUNTER_TYPE_SATURATING (0x01)
+#define MPI3_SASPHY3_COUNTER_TYPE_PEAK_VALUE (0x02)
+#define MPI3_SASPHY3_TIME_UNITS_10_MICROSECONDS (0x00)
+#define MPI3_SASPHY3_TIME_UNITS_100_MICROSECONDS (0x01)
+#define MPI3_SASPHY3_TIME_UNITS_1_MILLISECOND (0x02)
+#define MPI3_SASPHY3_TIME_UNITS_10_MILLISECONDS (0x03)
+#define MPI3_SASPHY3_TFLAGS_PHY_RESET (0x0002)
+#define MPI3_SASPHY3_TFLAGS_EVENT_NOTIFY (0x0001)
+#ifndef MPI3_SAS_PHY3_PHY_EVENT_MAX
+#define MPI3_SAS_PHY3_PHY_EVENT_MAX (1)
+#endif
+struct mpi3_sas_phy_page3 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_phy_events;
+ u8 reserved0d[3];
+ struct mpi3_sas_phy3_phy_event_config phy_event_config[MPI3_SAS_PHY3_PHY_EVENT_MAX];
+};
+
+#define MPI3_SASPHY3_PAGEVERSION (0x00)
+struct mpi3_sas_phy_page4 {
+ struct mpi3_config_page_header header;
+ u8 reserved08[3];
+ u8 flags;
+ u8 initial_frame[28];
+};
+
+#define MPI3_SASPHY4_PAGEVERSION (0x00)
+#define MPI3_SASPHY4_FLAGS_FRAME_VALID (0x02)
+#define MPI3_SASPHY4_FLAGS_SATA_FRAME (0x01)
+#define MPI3_PCIE_LINK_RETIMERS_MASK (0x30)
+#define MPI3_PCIE_LINK_RETIMERS_SHIFT (4)
+#define MPI3_PCIE_NEG_LINK_RATE_MASK (0x0f)
+#define MPI3_PCIE_NEG_LINK_RATE_UNKNOWN (0x00)
+#define MPI3_PCIE_NEG_LINK_RATE_PHY_DISABLED (0x01)
+#define MPI3_PCIE_NEG_LINK_RATE_2_5 (0x02)
+#define MPI3_PCIE_NEG_LINK_RATE_5_0 (0x03)
+#define MPI3_PCIE_NEG_LINK_RATE_8_0 (0x04)
+#define MPI3_PCIE_NEG_LINK_RATE_16_0 (0x05)
+#define MPI3_PCIE_NEG_LINK_RATE_32_0 (0x06)
+struct mpi3_pcie_io_unit0_phy_data {
+ u8 link;
+ u8 link_flags;
+ u8 phy_flags;
+ u8 negotiated_link_rate;
+ __le16 attached_dev_handle;
+ __le16 controller_dev_handle;
+ __le32 enumeration_status;
+ u8 io_unit_port;
+ u8 reserved0d[3];
+};
+
+#define MPI3_PCIEIOUNIT0_LINKFLAGS_CONFIG_SOURCE_MASK (0x10)
+#define MPI3_PCIEIOUNIT0_LINKFLAGS_CONFIG_SOURCE_IOUNIT1 (0x00)
+#define MPI3_PCIEIOUNIT0_LINKFLAGS_CONFIG_SOURCE_BKPLANE (0x10)
+#define MPI3_PCIEIOUNIT0_LINKFLAGS_ENUM_IN_PROGRESS (0x08)
+#define MPI3_PCIEIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08)
+#define MPI3_PCIEIOUNIT0_PHYFLAGS_HOST_PHY (0x01)
+#define MPI3_PCIEIOUNIT0_ES_MAX_SWITCH_DEPTH_EXCEEDED (0x80000000)
+#define MPI3_PCIEIOUNIT0_ES_MAX_SWITCHES_EXCEEDED (0x40000000)
+#define MPI3_PCIEIOUNIT0_ES_MAX_ENDPOINTS_EXCEEDED (0x20000000)
+#define MPI3_PCIEIOUNIT0_ES_INSUFFICIENT_RESOURCES (0x10000000)
+#ifndef MPI3_PCIE_IO_UNIT0_PHY_MAX
+#define MPI3_PCIE_IO_UNIT0_PHY_MAX (1)
+#endif
+struct mpi3_pcie_io_unit_page0 {
+ struct mpi3_config_page_header header;
+ __le32 reserved08;
+ u8 num_phys;
+ u8 init_status;
+ __le16 reserved0e;
+ struct mpi3_pcie_io_unit0_phy_data phy_data[MPI3_PCIE_IO_UNIT0_PHY_MAX];
+};
+
+#define MPI3_PCIEIOUNIT0_PAGEVERSION (0x00)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_NO_ERRORS (0x00)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_NEEDS_INITIALIZATION (0x01)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_NO_TARGETS_ALLOCATED (0x02)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_RESOURCE_ALLOC_FAILED (0x03)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_BAD_NUM_PHYS (0x04)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_UNSUPPORTED_CONFIG (0x05)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_HOST_PORT_MISMATCH (0x06)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_PHYS_NOT_CONSECUTIVE (0x07)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_BAD_CLOCKING_MODE (0x08)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_PROD_SPEC_START (0xf0)
+#define MPI3_PCIEIOUNIT0_INITSTATUS_PROD_SPEC_END (0xff)
+struct mpi3_pcie_io_unit1_phy_data {
+ u8 link;
+ u8 link_flags;
+ u8 phy_flags;
+ u8 max_min_link_rate;
+ __le32 reserved04;
+ __le32 reserved08;
+};
+
+#define MPI3_PCIEIOUNIT1_LINKFLAGS_PCIE_CLK_MODE_MASK (0x03)
+#define MPI3_PCIEIOUNIT1_LINKFLAGS_PCIE_CLK_MODE_DIS_SEPARATE_REFCLK (0x00)
+#define MPI3_PCIEIOUNIT1_LINKFLAGS_PCIE_CLK_MODE_EN_SRIS (0x01)
+#define MPI3_PCIEIOUNIT1_LINKFLAGS_PCIE_CLK_MODE_EN_SRNS (0x02)
+#define MPI3_PCIEIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_MASK (0xf0)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_SHIFT (4)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_2_5 (0x20)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_5_0 (0x30)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_8_0 (0x40)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_16_0 (0x50)
+#define MPI3_PCIEIOUNIT1_MMLR_MAX_RATE_32_0 (0x60)
+#ifndef MPI3_PCIE_IO_UNIT1_PHY_MAX
+#define MPI3_PCIE_IO_UNIT1_PHY_MAX (1)
+#endif
+struct mpi3_pcie_io_unit_page1 {
+ struct mpi3_config_page_header header;
+ __le32 control_flags;
+ __le32 reserved0c;
+ u8 num_phys;
+ u8 reserved11;
+ __le16 reserved12;
+ struct mpi3_pcie_io_unit1_phy_data phy_data[MPI3_PCIE_IO_UNIT1_PHY_MAX];
+};
+
+#define MPI3_PCIEIOUNIT1_PAGEVERSION (0x00)
+struct mpi3_pcie_io_unit_page2 {
+ struct mpi3_config_page_header header;
+ __le16 nv_me_max_queue_depth;
+ __le16 reserved0a;
+ u8 nv_me_abort_to;
+ u8 reserved0d;
+ __le16 reserved0e;
+};
+
+#define MPI3_PCIEIOUNIT2_PAGEVERSION (0x00)
+struct mpi3_pcie_switch_page0 {
+ struct mpi3_config_page_header header;
+ u8 io_unit_port;
+ u8 switch_status;
+ u8 reserved0a[2];
+ __le16 dev_handle;
+ __le16 parent_dev_handle;
+ u8 num_ports;
+ u8 pc_ie_level;
+ __le16 reserved12;
+ __le32 reserved14;
+ __le32 reserved18;
+ __le32 reserved1c;
+};
+
+#define MPI3_PCIESWITCH0_PAGEVERSION (0x00)
+#define MPI3_PCIESWITCH0_SS_NOT_RESPONDING (0x02)
+#define MPI3_PCIESWITCH0_SS_RESPONDING (0x03)
+#define MPI3_PCIESWITCH0_SS_DELAY_NOT_RESPONDING (0x04)
+struct mpi3_pcie_switch_page1 {
+ struct mpi3_config_page_header header;
+ u8 io_unit_port;
+ u8 reserved09[3];
+ u8 num_ports;
+ u8 port_num;
+ __le16 attached_dev_handle;
+ __le16 switch_dev_handle;
+ u8 negotiated_port_width;
+ u8 negotiated_link_rate;
+ __le16 slot;
+ __le16 slot_index;
+ __le32 reserved18;
+};
+
+#define MPI3_PCIESWITCH1_PAGEVERSION (0x00)
+struct mpi3_pcie_link_page0 {
+ struct mpi3_config_page_header header;
+ u8 link;
+ u8 reserved09[3];
+ __le32 correctable_error_count;
+ __le16 n_fatal_error_count;
+ __le16 reserved12;
+ __le16 fatal_error_count;
+ __le16 reserved16;
+};
+
+#define MPI3_PCIELINK0_PAGEVERSION (0x00)
+struct mpi3_enclosure_page0 {
+ struct mpi3_config_page_header header;
+ __le64 enclosure_logical_id;
+ __le16 flags;
+ __le16 enclosure_handle;
+ __le16 num_slots;
+ __le16 start_slot;
+ u8 io_unit_port;
+ u8 enclosure_level;
+ __le16 sep_dev_handle;
+ __le32 reserved1c;
+};
+
+#define MPI3_ENCLOSURE0_PAGEVERSION (0x00)
+#define MPI3_ENCLS0_FLAGS_ENCL_TYPE_MASK (0xc000)
+#define MPI3_ENCLS0_FLAGS_ENCL_TYPE_VIRTUAL (0x0000)
+#define MPI3_ENCLS0_FLAGS_ENCL_TYPE_SAS (0x4000)
+#define MPI3_ENCLS0_FLAGS_ENCL_TYPE_PCIE (0x8000)
+#define MPI3_ENCLS0_FLAGS_ENCL_DEV_PRESENT_MASK (0x0010)
+#define MPI3_ENCLS0_FLAGS_ENCL_DEV_NOT_FOUND (0x0000)
+#define MPI3_ENCLS0_FLAGS_ENCL_DEV_PRESENT (0x0010)
+#define MPI3_ENCLS0_FLAGS_MNG_MASK (0x000f)
+#define MPI3_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000)
+#define MPI3_ENCLS0_FLAGS_MNG_IOC_SES (0x0001)
+#define MPI3_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0002)
+#define MPI3_DEVICE_DEVFORM_SAS_SATA (0x00)
+#define MPI3_DEVICE_DEVFORM_PCIE (0x01)
+#define MPI3_DEVICE_DEVFORM_VD (0x02)
+struct mpi3_device0_sas_sata_format {
+ __le64 sas_address;
+ __le16 flags;
+ __le16 device_info;
+ u8 phy_num;
+ u8 attached_phy_identifier;
+ u8 max_port_connections;
+ u8 zone_group;
+};
+
+#define MPI3_DEVICE0_SASSATA_FLAGS_SLUMBER_CAP (0x0200)
+#define MPI3_DEVICE0_SASSATA_FLAGS_PARTIAL_CAP (0x0100)
+#define MPI3_DEVICE0_SASSATA_FLAGS_ASYNC_NOTIFY (0x0080)
+#define MPI3_DEVICE0_SASSATA_FLAGS_SW_PRESERVE (0x0040)
+#define MPI3_DEVICE0_SASSATA_FLAGS_UNSUPP_DEV (0x0020)
+#define MPI3_DEVICE0_SASSATA_FLAGS_48BIT_LBA (0x0010)
+#define MPI3_DEVICE0_SASSATA_FLAGS_SMART_SUPP (0x0008)
+#define MPI3_DEVICE0_SASSATA_FLAGS_NCQ_SUPP (0x0004)
+#define MPI3_DEVICE0_SASSATA_FLAGS_FUA_SUPP (0x0002)
+#define MPI3_DEVICE0_SASSATA_FLAGS_PERSIST_CAP (0x0001)
+struct mpi3_device0_pcie_format {
+ u8 supported_link_rates;
+ u8 max_port_width;
+ u8 negotiated_port_width;
+ u8 negotiated_link_rate;
+ u8 port_num;
+ u8 controller_reset_to;
+ __le16 device_info;
+ __le32 maximum_data_transfer_size;
+ __le32 capabilities;
+ __le16 noiob;
+ u8 nv_me_abort_to;
+ u8 page_size;
+ __le16 shutdown_latency;
+ __le16 reserved16;
+};
+
+#define MPI3_DEVICE0_PCIE_LINK_RATE_32_0_SUPP (0x10)
+#define MPI3_DEVICE0_PCIE_LINK_RATE_16_0_SUPP (0x08)
+#define MPI3_DEVICE0_PCIE_LINK_RATE_8_0_SUPP (0x04)
+#define MPI3_DEVICE0_PCIE_LINK_RATE_5_0_SUPP (0x02)
+#define MPI3_DEVICE0_PCIE_LINK_RATE_2_5_SUPP (0x01)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK (0x0003)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NO_DEVICE (0x0000)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NVME_DEVICE (0x0001)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_SWITCH_DEVICE (0x0002)
+#define MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_SCSI_DEVICE (0x0003)
+#define MPI3_DEVICE0_PCIE_CAP_METADATA_SEPARATED (0x00000010)
+#define MPI3_DEVICE0_PCIE_CAP_SGL_DWORD_ALIGN_REQUIRED (0x00000008)
+#define MPI3_DEVICE0_PCIE_CAP_NVME_SGL_ENABLED (0x00000004)
+#define MPI3_DEVICE0_PCIE_CAP_BIT_BUCKET_SGL_SUPP (0x00000002)
+#define MPI3_DEVICE0_PCIE_CAP_SGL_SUPP (0x00000001)
+struct mpi3_device0_vd_format {
+ u8 vd_state;
+ u8 raid_level;
+ __le16 device_info;
+ __le16 flags;
+ __le16 reserved06;
+ __le32 reserved08[2];
+};
+
+#define MPI3_DEVICE0_VD_STATE_OFFLINE (0x00)
+#define MPI3_DEVICE0_VD_STATE_PARTIALLY_DEGRADED (0x01)
+#define MPI3_DEVICE0_VD_STATE_DEGRADED (0x02)
+#define MPI3_DEVICE0_VD_STATE_OPTIMAL (0x03)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_0 (0)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_1 (1)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_5 (5)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_6 (6)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_10 (10)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_50 (50)
+#define MPI3_DEVICE0_VD_RAIDLEVEL_RAID_60 (60)
+#define MPI3_DEVICE0_VD_DEVICE_INFO_HDD (0x0010)
+#define MPI3_DEVICE0_VD_DEVICE_INFO_SSD (0x0008)
+#define MPI3_DEVICE0_VD_DEVICE_INFO_NVME (0x0004)
+#define MPI3_DEVICE0_VD_DEVICE_INFO_SATA (0x0002)
+#define MPI3_DEVICE0_VD_DEVICE_INFO_SAS (0x0001)
+#define MPI3_DEVICE0_VD_FLAGS_METADATA_MODE_MASK (0x0003)
+#define MPI3_DEVICE0_VD_FLAGS_METADATA_MODE_NONE (0x0000)
+#define MPI3_DEVICE0_VD_FLAGS_METADATA_MODE_HOST (0x0001)
+#define MPI3_DEVICE0_VD_FLAGS_METADATA_MODE_IOC (0x0002)
+union mpi3_device0_dev_spec_format {
+ struct mpi3_device0_sas_sata_format sas_sata_format;
+ struct mpi3_device0_pcie_format pcie_format;
+ struct mpi3_device0_vd_format vd_format;
+};
+
+struct mpi3_device_page0 {
+ struct mpi3_config_page_header header;
+ __le16 dev_handle;
+ __le16 parent_dev_handle;
+ __le16 slot;
+ __le16 enclosure_handle;
+ __le64 wwid;
+ __le16 persistent_id;
+ u8 io_unit_port;
+ u8 access_status;
+ __le16 flags;
+ __le16 reserved1e;
+ __le16 slot_index;
+ __le16 queue_depth;
+ u8 reserved24[3];
+ u8 device_form;
+ union mpi3_device0_dev_spec_format device_specific;
+};
+
+#define MPI3_DEVICE0_PAGEVERSION (0x00)
+#define MPI3_DEVICE0_WWID_INVALID (0xffffffffffffffff)
+#define MPI3_DEVICE0_PERSISTENTID_INVALID (0xffff)
+#define MPI3_DEVICE0_IOUNITPORT_INVALID (0xff)
+#define MPI3_DEVICE0_ASTATUS_NO_ERRORS (0x00)
+#define MPI3_DEVICE0_ASTATUS_NEEDS_INITIALIZATION (0x01)
+#define MPI3_DEVICE0_ASTATUS_CAP_UNSUPPORTED (0x02)
+#define MPI3_DEVICE0_ASTATUS_DEVICE_BLOCKED (0x03)
+#define MPI3_DEVICE0_ASTATUS_UNAUTHORIZED (0x04)
+#define MPI3_DEVICE0_ASTATUS_DEVICE_MISSING_DELAY (0x05)
+#define MPI3_DEVICE0_ASTATUS_SAS_UNKNOWN (0x10)
+#define MPI3_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE (0x11)
+#define MPI3_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE (0x12)
+#define MPI3_DEVICE0_ASTATUS_SIF_UNKNOWN (0x20)
+#define MPI3_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT (0x21)
+#define MPI3_DEVICE0_ASTATUS_SIF_DIAG (0x22)
+#define MPI3_DEVICE0_ASTATUS_SIF_IDENTIFICATION (0x23)
+#define MPI3_DEVICE0_ASTATUS_SIF_CHECK_POWER (0x24)
+#define MPI3_DEVICE0_ASTATUS_SIF_PIO_SN (0x25)
+#define MPI3_DEVICE0_ASTATUS_SIF_MDMA_SN (0x26)
+#define MPI3_DEVICE0_ASTATUS_SIF_UDMA_SN (0x27)
+#define MPI3_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION (0x28)
+#define MPI3_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE (0x29)
+#define MPI3_DEVICE0_ASTATUS_SIF_MAX (0x2f)
+#define MPI3_DEVICE0_ASTATUS_PCIE_UNKNOWN (0x30)
+#define MPI3_DEVICE0_ASTATUS_PCIE_MEM_SPACE_ACCESS (0x31)
+#define MPI3_DEVICE0_ASTATUS_PCIE_UNSUPPORTED (0x32)
+#define MPI3_DEVICE0_ASTATUS_PCIE_MSIX_REQUIRED (0x33)
+#define MPI3_DEVICE0_ASTATUS_NVME_UNKNOWN (0x40)
+#define MPI3_DEVICE0_ASTATUS_NVME_READY_TIMEOUT (0x41)
+#define MPI3_DEVICE0_ASTATUS_NVME_DEVCFG_UNSUPPORTED (0x42)
+#define MPI3_DEVICE0_ASTATUS_NVME_IDENTIFY_FAILED (0x43)
+#define MPI3_DEVICE0_ASTATUS_NVME_QCONFIG_FAILED (0x44)
+#define MPI3_DEVICE0_ASTATUS_NVME_QCREATION_FAILED (0x45)
+#define MPI3_DEVICE0_ASTATUS_NVME_EVENTCFG_FAILED (0x46)
+#define MPI3_DEVICE0_ASTATUS_NVME_GET_FEATURE_STAT_FAILED (0x47)
+#define MPI3_DEVICE0_ASTATUS_NVME_IDLE_TIMEOUT (0x48)
+#define MPI3_DEVICE0_ASTATUS_NVME_CTRL_FAILURE_STATUS (0x49)
+#define MPI3_DEVICE0_ASTATUS_VD_UNKNOWN (0x50)
+#define MPI3_DEVICE0_FLAGS_CONTROLLER_DEV_HANDLE (0x0080)
+#define MPI3_DEVICE0_FLAGS_HIDDEN (0x0008)
+#define MPI3_DEVICE0_FLAGS_ATT_METHOD_MASK (0x0006)
+#define MPI3_DEVICE0_FLAGS_ATT_METHOD_NOT_DIR_ATTACHED (0x0000)
+#define MPI3_DEVICE0_FLAGS_ATT_METHOD_DIR_ATTACHED (0x0002)
+#define MPI3_DEVICE0_FLAGS_ATT_METHOD_VIRTUAL (0x0004)
+#define MPI3_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001)
+#define MPI3_DEVICE0_QUEUE_DEPTH_NOT_APPLICABLE (0x0000)
+struct mpi3_device1_sas_sata_format {
+ __le32 reserved00;
+};
+
+struct mpi3_device1_pcie_format {
+ __le16 vendor_id;
+ __le16 device_id;
+ __le16 subsystem_vendor_id;
+ __le16 subsystem_id;
+ __le32 reserved08;
+ u8 revision_id;
+ u8 reserved0d;
+ __le16 pci_parameters;
+};
+
+#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_128B (0x0)
+#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_256B (0x1)
+#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_512B (0x2)
+#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_1024B (0x3)
+#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_2048B (0x4)
+#define MPI3_DEVICE1_PCIE_PARAMS_DATA_SIZE_4096B (0x5)
+#define MPI3_DEVICE1_PCIE_PARAMS_MAX_READ_REQ_MASK (0x01c0)
+#define MPI3_DEVICE1_PCIE_PARAMS_MAX_READ_REQ_SHIFT (6)
+#define MPI3_DEVICE1_PCIE_PARAMS_CURR_MAX_PAYLOAD_MASK (0x0038)
+#define MPI3_DEVICE1_PCIE_PARAMS_CURR_MAX_PAYLOAD_SHIFT (3)
+#define MPI3_DEVICE1_PCIE_PARAMS_SUPP_MAX_PAYLOAD_MASK (0x0007)
+#define MPI3_DEVICE1_PCIE_PARAMS_SUPP_MAX_PAYLOAD_SHIFT (0)
+struct mpi3_device1_vd_format {
+ __le32 reserved00;
+};
+
+union mpi3_device1_dev_spec_format {
+ struct mpi3_device1_sas_sata_format sas_sata_format;
+ struct mpi3_device1_pcie_format pcie_format;
+ struct mpi3_device1_vd_format vd_format;
+};
+
+struct mpi3_device_page1 {
+ struct mpi3_config_page_header header;
+ __le16 dev_handle;
+ __le16 reserved0a;
+ __le32 reserved0c[12];
+ u8 reserved3c[3];
+ u8 device_form;
+ union mpi3_device1_dev_spec_format device_specific;
+};
+
+#define MPI3_DEVICE1_PAGEVERSION (0x00)
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_image.h b/drivers/scsi/mpi3mr/mpi/mpi30_image.h
new file mode 100644
index 000000000000..169e4f9b7b7c
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_image.h
@@ -0,0 +1,216 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2018-2021 Broadcom Inc. All rights reserved.
+ *
+ */
+#ifndef MPI30_IMAGE_H
+#define MPI30_IMAGE_H 1
+struct mpi3_comp_image_version {
+ __le16 build_num;
+ __le16 customer_id;
+ u8 phase_minor;
+ u8 phase_major;
+ u8 gen_minor;
+ u8 gen_major;
+};
+
+struct mpi3_hash_exclusion_format {
+ __le32 offset;
+ __le32 size;
+};
+
+#define MPI3_IMAGE_HASH_EXCUSION_NUM (4)
+struct mpi3_component_image_header {
+ __le32 signature0;
+ __le32 load_address;
+ __le32 data_size;
+ __le32 start_offset;
+ __le32 signature1;
+ __le32 flash_offset;
+ __le32 image_size;
+ __le32 version_string_offset;
+ __le32 build_date_string_offset;
+ __le32 build_time_string_offset;
+ __le32 environment_variable_offset;
+ __le32 application_specific;
+ __le32 signature2;
+ __le32 header_size;
+ __le32 crc;
+ __le32 flags;
+ __le32 secondary_flash_offset;
+ __le32 etp_offset;
+ __le32 etp_size;
+ union mpi3_version_union rmc_interface_version;
+ union mpi3_version_union etp_interface_version;
+ struct mpi3_comp_image_version component_image_version;
+ struct mpi3_hash_exclusion_format hash_exclusion[MPI3_IMAGE_HASH_EXCUSION_NUM];
+ __le32 next_image_header_offset;
+ union mpi3_version_union security_version;
+ __le32 reserved84[31];
+};
+
+#define MPI3_IMAGE_HEADER_SIGNATURE0_MPI3 (0xeb00003e)
+#define MPI3_IMAGE_HEADER_LOAD_ADDRESS_INVALID (0x00000000)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_APPLICATION (0x20505041)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_FIRST_MUTABLE (0x20434d46)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_BSP (0x20505342)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_ROM_BIOS (0x534f4942)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_HII_X64 (0x4d494948)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_HII_ARM (0x41494948)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_CPLD (0x444c5043)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_SPD (0x20445053)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_GAS_GAUGE (0x20534147)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_PBLP (0x504c4250)
+#define MPI3_IMAGE_HEADER_SIGNATURE2_VALUE (0x50584546)
+#define MPI3_IMAGE_HEADER_FLAGS_DEVICE_KEY_BASIS_MASK (0x00000030)
+#define MPI3_IMAGE_HEADER_FLAGS_DEVICE_KEY_BASIS_CDI (0x00000000)
+#define MPI3_IMAGE_HEADER_FLAGS_DEVICE_KEY_BASIS_DI (0x00000010)
+#define MPI3_IMAGE_HEADER_FLAGS_SIGNED_NVDATA (0x00000008)
+#define MPI3_IMAGE_HEADER_FLAGS_REQUIRES_ACTIVATION (0x00000004)
+#define MPI3_IMAGE_HEADER_FLAGS_COMPRESSED (0x00000002)
+#define MPI3_IMAGE_HEADER_FLAGS_FLASH (0x00000001)
+#define MPI3_IMAGE_HEADER_SIGNATURE0_OFFSET (0x00)
+#define MPI3_IMAGE_HEADER_LOAD_ADDRESS_OFFSET (0x04)
+#define MPI3_IMAGE_HEADER_DATA_SIZE_OFFSET (0x08)
+#define MPI3_IMAGE_HEADER_START_OFFSET_OFFSET (0x0c)
+#define MPI3_IMAGE_HEADER_SIGNATURE1_OFFSET (0x10)
+#define MPI3_IMAGE_HEADER_FLASH_OFFSET_OFFSET (0x14)
+#define MPI3_IMAGE_HEADER_FLASH_SIZE_OFFSET (0x18)
+#define MPI3_IMAGE_HEADER_VERSION_STRING_OFFSET_OFFSET (0x1c)
+#define MPI3_IMAGE_HEADER_BUILD_DATE_STRING_OFFSET_OFFSET (0x20)
+#define MPI3_IMAGE_HEADER_BUILD_TIME_OFFSET_OFFSET (0x24)
+#define MPI3_IMAGE_HEADER_ENVIROMENT_VAR_OFFSET_OFFSET (0x28)
+#define MPI3_IMAGE_HEADER_APPLICATION_SPECIFIC_OFFSET (0x2c)
+#define MPI3_IMAGE_HEADER_SIGNATURE2_OFFSET (0x30)
+#define MPI3_IMAGE_HEADER_HEADER_SIZE_OFFSET (0x34)
+#define MPI3_IMAGE_HEADER_CRC_OFFSET (0x38)
+#define MPI3_IMAGE_HEADER_FLAGS_OFFSET (0x3c)
+#define MPI3_IMAGE_HEADER_SECONDARY_FLASH_OFFSET_OFFSET (0x40)
+#define MPI3_IMAGE_HEADER_ETP_OFFSET_OFFSET (0x44)
+#define MPI3_IMAGE_HEADER_ETP_SIZE_OFFSET (0x48)
+#define MPI3_IMAGE_HEADER_RMC_INTERFACE_VER_OFFSET (0x4c)
+#define MPI3_IMAGE_HEADER_ETP_INTERFACE_VER_OFFSET (0x50)
+#define MPI3_IMAGE_HEADER_COMPONENT_IMAGE_VER_OFFSET (0x54)
+#define MPI3_IMAGE_HEADER_HASH_EXCLUSION_OFFSET (0x5c)
+#define MPI3_IMAGE_HEADER_NEXT_IMAGE_HEADER_OFFSET_OFFSET (0x7c)
+#define MPI3_IMAGE_HEADER_SIZE (0x100)
+struct mpi3_extended_image_header {
+ u8 image_type;
+ u8 reserved01[3];
+ __le32 checksum;
+ __le32 image_size;
+ __le32 next_image_header_offset;
+ __le32 reserved10[4];
+ __le32 identify_string[8];
+};
+
+#define MPI3_EXT_IMAGE_IMAGETYPE_OFFSET (0x00)
+#define MPI3_EXT_IMAGE_IMAGESIZE_OFFSET (0x08)
+#define MPI3_EXT_IMAGE_NEXTIMAGE_OFFSET (0x0c)
+#define MPI3_EXT_IMAGE_HEADER_SIZE (0x40)
+#define MPI3_EXT_IMAGE_TYPE_UNSPECIFIED (0x00)
+#define MPI3_EXT_IMAGE_TYPE_NVDATA (0x03)
+#define MPI3_EXT_IMAGE_TYPE_SUPPORTED_DEVICES (0x07)
+#define MPI3_EXT_IMAGE_TYPE_ENCRYPTED_HASH (0x09)
+#define MPI3_EXT_IMAGE_TYPE_RDE (0x0a)
+#define MPI3_EXT_IMAGE_TYPE_AUXILIARY_PROCESSOR (0x0b)
+#define MPI3_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC (0x80)
+#define MPI3_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC (0xff)
+struct mpi3_supported_device {
+ __le16 device_id;
+ __le16 vendor_id;
+ __le16 device_id_mask;
+ __le16 reserved06;
+ u8 low_pci_rev;
+ u8 high_pci_rev;
+ __le16 reserved0a;
+ __le32 reserved0c;
+};
+
+#ifndef MPI3_SUPPORTED_DEVICE_MAX
+#define MPI3_SUPPORTED_DEVICE_MAX (1)
+#endif
+struct mpi3_supported_devices_data {
+ u8 image_version;
+ u8 reserved01;
+ u8 num_devices;
+ u8 reserved03;
+ __le32 reserved04;
+ struct mpi3_supported_device supported_device[MPI3_SUPPORTED_DEVICE_MAX];
+};
+
+#ifndef MPI3_ENCRYPTED_HASH_MAX
+#define MPI3_ENCRYPTED_HASH_MAX (1)
+#endif
+struct mpi3_encrypted_hash_entry {
+ u8 hash_image_type;
+ u8 hash_algorithm;
+ u8 encryption_algorithm;
+ u8 reserved03;
+ __le32 reserved04;
+ __le32 encrypted_hash[MPI3_ENCRYPTED_HASH_MAX];
+};
+
+#define MPI3_HASH_IMAGE_TYPE_KEY_WITH_SIGNATURE (0x03)
+#define MPI3_HASH_ALGORITHM_VERSION_MASK (0xe0)
+#define MPI3_HASH_ALGORITHM_VERSION_NONE (0x00)
+#define MPI3_HASH_ALGORITHM_VERSION_SHA1 (0x20)
+#define MPI3_HASH_ALGORITHM_VERSION_SHA2 (0x40)
+#define MPI3_HASH_ALGORITHM_VERSION_SHA3 (0x60)
+#define MPI3_HASH_ALGORITHM_SIZE_MASK (0x1f)
+#define MPI3_HASH_ALGORITHM_SIZE_UNUSED (0x00)
+#define MPI3_HASH_ALGORITHM_SIZE_SHA256 (0x01)
+#define MPI3_HASH_ALGORITHM_SIZE_SHA512 (0x02)
+#define MPI3_ENCRYPTION_ALGORITHM_UNUSED (0x00)
+#define MPI3_ENCRYPTION_ALGORITHM_RSA256 (0x01)
+#define MPI3_ENCRYPTION_ALGORITHM_RSA512 (0x02)
+#define MPI3_ENCRYPTION_ALGORITHM_RSA1024 (0x03)
+#define MPI3_ENCRYPTION_ALGORITHM_RSA2048 (0x04)
+#define MPI3_ENCRYPTION_ALGORITHM_RSA4096 (0x05)
+#define MPI3_ENCRYPTION_ALGORITHM_RSA3072 (0x06)
+#ifndef MPI3_PUBLIC_KEY_MAX
+#define MPI3_PUBLIC_KEY_MAX (1)
+#endif
+struct mpi3_encrypted_key_with_hash_entry {
+ u8 hash_image_type;
+ u8 hash_algorithm;
+ u8 encryption_algorithm;
+ u8 reserved03;
+ __le32 reserved04;
+ __le32 public_key[MPI3_PUBLIC_KEY_MAX];
+ __le32 encrypted_hash[MPI3_ENCRYPTED_HASH_MAX];
+};
+
+#ifndef MPI3_ENCRYPTED_HASH_ENTRY_MAX
+#define MPI3_ENCRYPTED_HASH_ENTRY_MAX (1)
+#endif
+struct mpi3_encrypted_hash_data {
+ u8 image_version;
+ u8 num_hash;
+ __le16 reserved02;
+ __le32 reserved04;
+ struct mpi3_encrypted_hash_entry encrypted_hash_entry[MPI3_ENCRYPTED_HASH_ENTRY_MAX];
+};
+
+#ifndef MPI3_AUX_PROC_DATA_MAX
+#define MPI3_AUX_PROC_DATA_MAX (1)
+#endif
+struct mpi3_aux_processor_data {
+ u8 boot_method;
+ u8 num_load_addr;
+ u8 reserved02;
+ u8 type;
+ __le32 version;
+ __le32 load_address[8];
+ __le32 reserved28[22];
+ __le32 aux_processor_data[MPI3_AUX_PROC_DATA_MAX];
+};
+
+#define MPI3_AUX_PROC_DATA_OFFSET (0x80)
+#define MPI3_AUXPROCESSOR_BOOT_METHOD_MO_MSG (0x00)
+#define MPI3_AUXPROCESSOR_BOOT_METHOD_MO_DOORBELL (0x01)
+#define MPI3_AUXPROCESSOR_BOOT_METHOD_COMPONENT (0x02)
+#define MPI3_AUXPROCESSOR_TYPE_ARM_A15 (0x00)
+#define MPI3_AUXPROCESSOR_TYPE_ARM_M0 (0x01)
+#define MPI3_AUXPROCESSOR_TYPE_ARM_R4 (0x02)
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_init.h b/drivers/scsi/mpi3mr/mpi/mpi30_init.h
new file mode 100644
index 000000000000..e02b6d3cfba2
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_init.h
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2016-2021 Broadcom Inc. All rights reserved.
+ *
+ */
+#ifndef MPI30_INIT_H
+#define MPI30_INIT_H 1
+struct mpi3_scsi_io_cdb_eedp32 {
+ u8 cdb[20];
+ __be32 primary_reference_tag;
+ __le16 primary_application_tag;
+ __le16 primary_application_tag_mask;
+ __le32 transfer_length;
+};
+
+union mpi3_scso_io_cdb_union {
+ u8 cdb32[32];
+ struct mpi3_scsi_io_cdb_eedp32 eedp32;
+ struct mpi3_sge_common sge;
+};
+
+struct mpi3_scsi_io_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 dev_handle;
+ __le32 flags;
+ __le32 skip_count;
+ __le32 data_length;
+ u8 lun[8];
+ union mpi3_scso_io_cdb_union cdb;
+ union mpi3_sge_union sgl[4];
+};
+
+#define MPI3_SCSIIO_MSGFLAGS_METASGL_VALID (0x80)
+#define MPI3_SCSIIO_FLAGS_LARGE_CDB (0x60000000)
+#define MPI3_SCSIIO_FLAGS_CDB_16_OR_LESS (0x00000000)
+#define MPI3_SCSIIO_FLAGS_CDB_GREATER_THAN_16 (0x20000000)
+#define MPI3_SCSIIO_FLAGS_CDB_IN_SEPARATE_BUFFER (0x40000000)
+#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_MASK (0x07000000)
+#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_SIMPLEQ (0x00000000)
+#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_HEADOFQ (0x01000000)
+#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_ORDEREDQ (0x02000000)
+#define MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_ACAQ (0x04000000)
+#define MPI3_SCSIIO_FLAGS_CMDPRI_MASK (0x00f00000)
+#define MPI3_SCSIIO_FLAGS_CMDPRI_SHIFT (20)
+#define MPI3_SCSIIO_FLAGS_DATADIRECTION_MASK (0x000c0000)
+#define MPI3_SCSIIO_FLAGS_DATADIRECTION_NO_DATA_TRANSFER (0x00000000)
+#define MPI3_SCSIIO_FLAGS_DATADIRECTION_WRITE (0x00040000)
+#define MPI3_SCSIIO_FLAGS_DATADIRECTION_READ (0x00080000)
+#define MPI3_SCSIIO_FLAGS_DMAOPERATION_MASK (0x00030000)
+#define MPI3_SCSIIO_FLAGS_DMAOPERATION_HOST_PI (0x00010000)
+#define MPI3_SCSIIO_METASGL_INDEX (3)
+struct mpi3_scsi_io_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+ u8 scsi_status;
+ u8 scsi_state;
+ __le16 dev_handle;
+ __le32 transfer_count;
+ __le32 sense_count;
+ __le32 response_data;
+ __le16 task_tag;
+ __le16 scsi_status_qualifier;
+ __le32 eedp_error_offset;
+ __le16 eedp_observed_app_tag;
+ __le16 eedp_observed_guard;
+ __le32 eedp_observed_ref_tag;
+ __le64 sense_data_buffer_address;
+};
+
+#define MPI3_SCSIIO_REPLY_MSGFLAGS_REFTAG_OBSERVED_VALID (0x01)
+#define MPI3_SCSIIO_REPLY_MSGFLAGS_APPTAG_OBSERVED_VALID (0x02)
+#define MPI3_SCSIIO_REPLY_MSGFLAGS_GUARD_OBSERVED_VALID (0x04)
+#define MPI3_SCSI_STATUS_GOOD (0x00)
+#define MPI3_SCSI_STATUS_CHECK_CONDITION (0x02)
+#define MPI3_SCSI_STATUS_CONDITION_MET (0x04)
+#define MPI3_SCSI_STATUS_BUSY (0x08)
+#define MPI3_SCSI_STATUS_INTERMEDIATE (0x10)
+#define MPI3_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14)
+#define MPI3_SCSI_STATUS_RESERVATION_CONFLICT (0x18)
+#define MPI3_SCSI_STATUS_COMMAND_TERMINATED (0x22)
+#define MPI3_SCSI_STATUS_TASK_SET_FULL (0x28)
+#define MPI3_SCSI_STATUS_ACA_ACTIVE (0x30)
+#define MPI3_SCSI_STATUS_TASK_ABORTED (0x40)
+#define MPI3_SCSI_STATE_SENSE_MASK (0x03)
+#define MPI3_SCSI_STATE_SENSE_VALID (0x00)
+#define MPI3_SCSI_STATE_SENSE_FAILED (0x01)
+#define MPI3_SCSI_STATE_SENSE_BUFF_Q_EMPTY (0x02)
+#define MPI3_SCSI_STATE_SENSE_NOT_AVAILABLE (0x03)
+#define MPI3_SCSI_STATE_NO_SCSI_STATUS (0x04)
+#define MPI3_SCSI_STATE_TERMINATED (0x08)
+#define MPI3_SCSI_STATE_RESPONSE_DATA_VALID (0x10)
+#define MPI3_SCSI_RSP_RESPONSECODE_MASK (0x000000ff)
+#define MPI3_SCSI_RSP_RESPONSECODE_SHIFT (0)
+#define MPI3_SCSI_RSP_ARI2_MASK (0x0000ff00)
+#define MPI3_SCSI_RSP_ARI2_SHIFT (8)
+#define MPI3_SCSI_RSP_ARI1_MASK (0x00ff0000)
+#define MPI3_SCSI_RSP_ARI1_SHIFT (16)
+#define MPI3_SCSI_RSP_ARI0_MASK (0xff000000)
+#define MPI3_SCSI_RSP_ARI0_SHIFT (24)
+#define MPI3_SCSI_TASKTAG_UNKNOWN (0xffff)
+struct mpi3_scsi_task_mgmt_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 dev_handle;
+ __le16 task_host_tag;
+ u8 task_type;
+ u8 reserved0f;
+ __le16 task_request_queue_id;
+ __le16 reserved12;
+ __le32 reserved14;
+ u8 lun[8];
+};
+
+#define MPI3_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU (0x08)
+#define MPI3_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x01)
+#define MPI3_SCSITASKMGMT_TASKTYPE_ABORT_TASK_SET (0x02)
+#define MPI3_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03)
+#define MPI3_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05)
+#define MPI3_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET (0x06)
+#define MPI3_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07)
+#define MPI3_SCSITASKMGMT_TASKTYPE_CLEAR_ACA (0x08)
+#define MPI3_SCSITASKMGMT_TASKTYPE_QUERY_TASK_SET (0x09)
+#define MPI3_SCSITASKMGMT_TASKTYPE_QUERY_ASYNC_EVENT (0x0a)
+#define MPI3_SCSITASKMGMT_TASKTYPE_I_T_NEXUS_RESET (0x0b)
+struct mpi3_scsi_task_mgmt_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+ __le32 termination_count;
+ __le32 response_data;
+ __le32 reserved18;
+};
+
+#define MPI3_SCSITASKMGMT_RSPCODE_IO_QUEUED_ON_IOC (0x80)
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h b/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h
new file mode 100644
index 000000000000..1af99a5382d5
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h
@@ -0,0 +1,1004 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2016-2021 Broadcom Inc. All rights reserved.
+ *
+ */
+#ifndef MPI30_IOC_H
+#define MPI30_IOC_H 1
+struct mpi3_ioc_init_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ union mpi3_version_union mpi_version;
+ __le64 time_stamp;
+ u8 reserved18;
+ u8 who_init;
+ __le16 reserved1a;
+ __le16 reply_free_queue_depth;
+ __le16 reserved1e;
+ __le64 reply_free_queue_address;
+ __le32 reserved28;
+ __le16 sense_buffer_free_queue_depth;
+ __le16 sense_buffer_length;
+ __le64 sense_buffer_free_queue_address;
+ __le64 driver_information_address;
+};
+
+#define MPI3_WHOINIT_NOT_INITIALIZED (0x00)
+#define MPI3_WHOINIT_ROM_BIOS (0x02)
+#define MPI3_WHOINIT_HOST_DRIVER (0x03)
+#define MPI3_WHOINIT_MANUFACTURER (0x04)
+struct mpi3_driver_info_layout {
+ __le32 information_length;
+ u8 driver_signature[12];
+ u8 os_name[16];
+ u8 os_version[12];
+ u8 driver_name[20];
+ u8 driver_version[32];
+ u8 driver_release_date[20];
+ __le32 driver_capabilities;
+};
+
+struct mpi3_ioc_facts_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ __le32 reserved0c;
+ union mpi3_sge_union sgl;
+};
+
+struct mpi3_ioc_facts_data {
+ __le16 ioc_facts_data_length;
+ __le16 reserved02;
+ union mpi3_version_union mpi_version;
+ struct mpi3_comp_image_version fw_version;
+ __le32 ioc_capabilities;
+ u8 ioc_number;
+ u8 who_init;
+ __le16 max_msix_vectors;
+ __le16 max_outstanding_request;
+ __le16 product_id;
+ __le16 ioc_request_frame_size;
+ __le16 reply_frame_size;
+ __le16 ioc_exceptions;
+ __le16 max_persistent_id;
+ u8 sge_modifier_mask;
+ u8 sge_modifier_value;
+ u8 sge_modifier_shift;
+ u8 protocol_flags;
+ __le16 max_sas_initiators;
+ __le16 max_sas_targets;
+ __le16 max_sas_expanders;
+ __le16 max_enclosures;
+ __le16 min_dev_handle;
+ __le16 max_dev_handle;
+ __le16 max_pc_ie_switches;
+ __le16 max_nvme;
+ __le16 max_pds;
+ __le16 max_vds;
+ __le16 max_host_pds;
+ __le16 max_advanced_host_pds;
+ __le16 max_raid_pds;
+ __le16 max_posted_cmd_buffers;
+ __le32 flags;
+ __le16 max_operational_request_queues;
+ __le16 max_operational_reply_queues;
+ __le16 shutdown_timeout;
+ __le16 reserved4e;
+ __le32 diag_trace_size;
+ __le32 diag_fw_size;
+};
+
+#define MPI3_IOCFACTS_CAPABILITY_ADVANCED_HOST_PD (0x00000010)
+#define MPI3_IOCFACTS_CAPABILITY_RAID_CAPABLE (0x00000008)
+#define MPI3_IOCFACTS_CAPABILITY_COALESCE_CTRL_GRAN_MASK (0x00000001)
+#define MPI3_IOCFACTS_CAPABILITY_COALESCE_CTRL_IOC_GRAN (0x00000000)
+#define MPI3_IOCFACTS_CAPABILITY_COALESCE_CTRL_REPLY_Q_GRAN (0x00000001)
+#define MPI3_IOCFACTS_PID_TYPE_MASK (0xf000)
+#define MPI3_IOCFACTS_PID_TYPE_SHIFT (12)
+#define MPI3_IOCFACTS_PID_PRODUCT_MASK (0x0f00)
+#define MPI3_IOCFACTS_PID_PRODUCT_SHIFT (8)
+#define MPI3_IOCFACTS_PID_FAMILY_MASK (0x00ff)
+#define MPI3_IOCFACTS_PID_FAMILY_SHIFT (0)
+#define MPI3_IOCFACTS_EXCEPT_SAFE_MODE (0x0800)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_MASK (0x0700)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_NONE (0x0000)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_LOCAL_VIA_RAID (0x0100)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_LOCAL_VIA_OOB (0x0200)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_EXT_VIA_RAID (0x0300)
+#define MPI3_IOCFACTS_EXCEPT_SECURITY_KEY_EXT_VIA_OOB (0x0400)
+#define MPI3_IOCFACTS_EXCEPT_PCIE_DISABLED (0x0080)
+#define MPI3_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE (0x0040)
+#define MPI3_IOCFACTS_EXCEPT_MANUFACT_CHECKSUM_FAIL (0x0020)
+#define MPI3_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (0x0010)
+#define MPI3_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0008)
+#define MPI3_IOCFACTS_EXCEPT_BOOTSTAT_MASK (0x0001)
+#define MPI3_IOCFACTS_EXCEPT_BOOTSTAT_PRIMARY (0x0000)
+#define MPI3_IOCFACTS_EXCEPT_BOOTSTAT_SECONDARY (0x0001)
+#define MPI3_IOCFACTS_PROTOCOL_SAS (0x0010)
+#define MPI3_IOCFACTS_PROTOCOL_SATA (0x0008)
+#define MPI3_IOCFACTS_PROTOCOL_NVME (0x0004)
+#define MPI3_IOCFACTS_PROTOCOL_SCSI_INITIATOR (0x0002)
+#define MPI3_IOCFACTS_PROTOCOL_SCSI_TARGET (0x0001)
+#define MPI3_IOCFACTS_FLAGS_SIGNED_NVDATA_REQUIRED (0x00010000)
+#define MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_MASK (0x0000ff00)
+#define MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_SHIFT (8)
+#define MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_MASK (0x00000030)
+#define MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_NOT_STARTED (0x00000000)
+#define MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_IN_PROGRESS (0x00000010)
+#define MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_COMPLETE (0x00000020)
+#define MPI3_IOCFACTS_FLAGS_PERSONALITY_MASK (0x0000000f)
+#define MPI3_IOCFACTS_FLAGS_PERSONALITY_EHBA (0x00000000)
+#define MPI3_IOCFACTS_FLAGS_PERSONALITY_RAID_DDR (0x00000002)
+struct mpi3_mgmt_passthrough_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ __le32 reserved0c[5];
+ union mpi3_sge_union command_sgl;
+ union mpi3_sge_union response_sgl;
+};
+
+struct mpi3_create_request_queue_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 flags;
+ u8 burst;
+ __le16 size;
+ __le16 queue_id;
+ __le16 reply_queue_id;
+ __le16 reserved12;
+ __le32 reserved14;
+ __le64 base_address;
+};
+
+#define MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_MASK (0x80)
+#define MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_SEGMENTED (0x80)
+#define MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_CONTIGUOUS (0x00)
+struct mpi3_delete_request_queue_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 queue_id;
+};
+
+struct mpi3_create_reply_queue_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 flags;
+ u8 reserved0b;
+ __le16 size;
+ __le16 queue_id;
+ __le16 msix_index;
+ __le16 reserved12;
+ __le32 reserved14;
+ __le64 base_address;
+};
+
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_SEGMENTED_MASK (0x80)
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_SEGMENTED_SEGMENTED (0x80)
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_SEGMENTED_CONTIGUOUS (0x00)
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_MASK (0x01)
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_DISABLE (0x00)
+#define MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_ENABLE (0x01)
+struct mpi3_delete_reply_queue_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 queue_id;
+};
+
+struct mpi3_port_enable_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+};
+
+#define MPI3_EVENT_LOG_DATA (0x01)
+#define MPI3_EVENT_CHANGE (0x02)
+#define MPI3_EVENT_GPIO_INTERRUPT (0x04)
+#define MPI3_EVENT_TEMP_THRESHOLD (0x05)
+#define MPI3_EVENT_CABLE_MGMT (0x06)
+#define MPI3_EVENT_DEVICE_ADDED (0x07)
+#define MPI3_EVENT_DEVICE_INFO_CHANGED (0x08)
+#define MPI3_EVENT_PREPARE_FOR_RESET (0x09)
+#define MPI3_EVENT_COMP_IMAGE_ACT_START (0x0a)
+#define MPI3_EVENT_ENCL_DEVICE_ADDED (0x0b)
+#define MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE (0x0c)
+#define MPI3_EVENT_DEVICE_STATUS_CHANGE (0x0d)
+#define MPI3_EVENT_ENERGY_PACK_CHANGE (0x0e)
+#define MPI3_EVENT_SAS_DISCOVERY (0x11)
+#define MPI3_EVENT_SAS_BROADCAST_PRIMITIVE (0x12)
+#define MPI3_EVENT_SAS_NOTIFY_PRIMITIVE (0x13)
+#define MPI3_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE (0x14)
+#define MPI3_EVENT_SAS_INIT_TABLE_OVERFLOW (0x15)
+#define MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST (0x16)
+#define MPI3_EVENT_SAS_PHY_COUNTER (0x18)
+#define MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR (0x19)
+#define MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST (0x20)
+#define MPI3_EVENT_PCIE_ENUMERATION (0x22)
+#define MPI3_EVENT_HARD_RESET_RECEIVED (0x40)
+#define MPI3_EVENT_MIN_PRODUCT_SPECIFIC (0x60)
+#define MPI3_EVENT_MAX_PRODUCT_SPECIFIC (0x7f)
+#define MPI3_EVENT_NOTIFY_EVENTMASK_WORDS (4)
+struct mpi3_event_notification_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ __le16 sas_broadcast_primitive_masks;
+ __le16 sas_notify_primitive_masks;
+ __le32 event_masks[MPI3_EVENT_NOTIFY_EVENTMASK_WORDS];
+};
+
+struct mpi3_event_notification_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+ u8 event_data_length;
+ u8 event;
+ __le16 ioc_change_count;
+ __le32 event_context;
+ __le32 event_data[1];
+};
+
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_MASK (0x01)
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_REQUIRED (0x01)
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_NOT_REQUIRED (0x00)
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_EVENT_ORIGINALITY_MASK (0x02)
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_EVENT_ORIGINALITY_ORIGINAL (0x00)
+#define MPI3_EVENT_NOTIFY_MSGFLAGS_EVENT_ORIGINALITY_REPLAY (0x02)
+struct mpi3_event_data_gpio_interrupt {
+ u8 gpio_num;
+ u8 reserved01[3];
+};
+
+struct mpi3_event_data_temp_threshold {
+ __le16 status;
+ u8 sensor_num;
+ u8 reserved03;
+ __le16 current_temperature;
+ __le16 reserved06;
+ __le32 reserved08;
+ __le32 reserved0c;
+};
+
+#define MPI3_EVENT_TEMP_THRESHOLD_STATUS_THRESHOLD3_EXCEEDED (0x0008)
+#define MPI3_EVENT_TEMP_THRESHOLD_STATUS_THRESHOLD2_EXCEEDED (0x0004)
+#define MPI3_EVENT_TEMP_THRESHOLD_STATUS_THRESHOLD1_EXCEEDED (0x0002)
+#define MPI3_EVENT_TEMP_THRESHOLD_STATUS_THRESHOLD0_EXCEEDED (0x0001)
+struct mpi3_event_data_cable_management {
+ __le32 active_cable_power_requirement;
+ u8 status;
+ u8 receptacle_id;
+ __le16 reserved06;
+};
+
+#define MPI3_EVENT_CABLE_MGMT_ACT_CABLE_PWR_INVALID (0xffffffff)
+#define MPI3_EVENT_CABLE_MGMT_STATUS_INSUFFICIENT_POWER (0x00)
+#define MPI3_EVENT_CABLE_MGMT_STATUS_PRESENT (0x01)
+#define MPI3_EVENT_CABLE_MGMT_STATUS_DEGRADED (0x02)
+struct mpi3_event_ack_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ u8 event;
+ u8 reserved0d[3];
+ __le32 event_context;
+};
+
+struct mpi3_event_data_prepare_for_reset {
+ u8 reason_code;
+ u8 reserved01;
+ __le16 reserved02;
+};
+
+#define MPI3_EVENT_PREPARE_RESET_RC_START (0x01)
+#define MPI3_EVENT_PREPARE_RESET_RC_ABORT (0x02)
+struct mpi3_event_data_comp_image_activation {
+ __le32 reserved00;
+};
+
+struct mpi3_event_data_device_status_change {
+ __le16 task_tag;
+ u8 reason_code;
+ u8 io_unit_port;
+ __le16 parent_dev_handle;
+ __le16 dev_handle;
+ __le64 wwid;
+ u8 lun[8];
+};
+
+#define MPI3_EVENT_DEV_STAT_RC_MOVED (0x01)
+#define MPI3_EVENT_DEV_STAT_RC_HIDDEN (0x02)
+#define MPI3_EVENT_DEV_STAT_RC_NOT_HIDDEN (0x03)
+#define MPI3_EVENT_DEV_STAT_RC_ASYNC_NOTIFICATION (0x04)
+#define MPI3_EVENT_DEV_STAT_RC_INT_DEVICE_RESET_STRT (0x20)
+#define MPI3_EVENT_DEV_STAT_RC_INT_DEVICE_RESET_CMP (0x21)
+#define MPI3_EVENT_DEV_STAT_RC_INT_TASK_ABORT_STRT (0x22)
+#define MPI3_EVENT_DEV_STAT_RC_INT_TASK_ABORT_CMP (0x23)
+#define MPI3_EVENT_DEV_STAT_RC_INT_IT_NEXUS_RESET_STRT (0x24)
+#define MPI3_EVENT_DEV_STAT_RC_INT_IT_NEXUS_RESET_CMP (0x25)
+#define MPI3_EVENT_DEV_STAT_RC_PCIE_HOT_RESET_FAILED (0x30)
+#define MPI3_EVENT_DEV_STAT_RC_EXPANDER_REDUCED_FUNC_STRT (0x40)
+#define MPI3_EVENT_DEV_STAT_RC_EXPANDER_REDUCED_FUNC_CMP (0x41)
+#define MPI3_EVENT_DEV_STAT_RC_VD_NOT_RESPONDING (0x50)
+struct mpi3_event_data_energy_pack_change {
+ __le32 reserved00;
+ __le16 shutdown_timeout;
+ __le16 reserved06;
+};
+
+struct mpi3_event_data_sas_discovery {
+ u8 flags;
+ u8 reason_code;
+ u8 io_unit_port;
+ u8 reserved03;
+ __le32 discovery_status;
+};
+
+#define MPI3_EVENT_SAS_DISC_FLAGS_DEVICE_CHANGE (0x02)
+#define MPI3_EVENT_SAS_DISC_FLAGS_IN_PROGRESS (0x01)
+#define MPI3_EVENT_SAS_DISC_RC_STARTED (0x01)
+#define MPI3_EVENT_SAS_DISC_RC_COMPLETED (0x02)
+#define MPI3_SAS_DISC_STATUS_MAX_ENCLOSURES_EXCEED (0x80000000)
+#define MPI3_SAS_DISC_STATUS_MAX_EXPANDERS_EXCEED (0x40000000)
+#define MPI3_SAS_DISC_STATUS_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI3_SAS_DISC_STATUS_MAX_TOPO_PHYS_EXCEED (0x10000000)
+#define MPI3_SAS_DISC_STATUS_MULTIPLE_DEVICES_IN_SLOT (0x00004000)
+#define MPI3_SAS_DISC_STATUS_SLOT_COUNT_MISMATCH (0x00002000)
+#define MPI3_SAS_DISC_STATUS_TOO_MANY_SLOTS (0x00001000)
+#define MPI3_SAS_DISC_STATUS_EXP_MULTI_SUBTRACTIVE (0x00000800)
+#define MPI3_SAS_DISC_STATUS_MULTI_PORT_DOMAIN (0x00000400)
+#define MPI3_SAS_DISC_STATUS_TABLE_TO_SUBTRACTIVE_LINK (0x00000200)
+#define MPI3_SAS_DISC_STATUS_UNSUPPORTED_DEVICE (0x00000100)
+#define MPI3_SAS_DISC_STATUS_TABLE_LINK (0x00000080)
+#define MPI3_SAS_DISC_STATUS_SUBTRACTIVE_LINK (0x00000040)
+#define MPI3_SAS_DISC_STATUS_SMP_CRC_ERROR (0x00000020)
+#define MPI3_SAS_DISC_STATUS_SMP_FUNCTION_FAILED (0x00000010)
+#define MPI3_SAS_DISC_STATUS_SMP_TIMEOUT (0x00000008)
+#define MPI3_SAS_DISC_STATUS_MULTIPLE_PORTS (0x00000004)
+#define MPI3_SAS_DISC_STATUS_INVALID_SAS_ADDRESS (0x00000002)
+#define MPI3_SAS_DISC_STATUS_LOOP_DETECTED (0x00000001)
+struct mpi3_event_data_sas_broadcast_primitive {
+ u8 phy_num;
+ u8 io_unit_port;
+ u8 port_width;
+ u8 primitive;
+};
+
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_CHANGE (0x01)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_SES (0x02)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_EXPANDER (0x03)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_ASYNCHRONOUS_EVENT (0x04)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_RESERVED3 (0x05)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_RESERVED4 (0x06)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_CHANGE0_RESERVED (0x07)
+#define MPI3_EVENT_BROADCAST_PRIMITIVE_CHANGE1_RESERVED (0x08)
+struct mpi3_event_data_sas_notify_primitive {
+ u8 phy_num;
+ u8 io_unit_port;
+ u8 reserved02;
+ u8 primitive;
+};
+
+#define MPI3_EVENT_NOTIFY_PRIMITIVE_ENABLE_SPINUP (0x01)
+#define MPI3_EVENT_NOTIFY_PRIMITIVE_POWER_LOSS_EXPECTED (0x02)
+#define MPI3_EVENT_NOTIFY_PRIMITIVE_RESERVED1 (0x03)
+#define MPI3_EVENT_NOTIFY_PRIMITIVE_RESERVED2 (0x04)
+#ifndef MPI3_EVENT_SAS_TOPO_PHY_COUNT
+#define MPI3_EVENT_SAS_TOPO_PHY_COUNT (1)
+#endif
+struct mpi3_event_sas_topo_phy_entry {
+ __le16 attached_dev_handle;
+ u8 link_rate;
+ u8 status;
+};
+
+#define MPI3_EVENT_SAS_TOPO_LR_CURRENT_MASK (0xf0)
+#define MPI3_EVENT_SAS_TOPO_LR_CURRENT_SHIFT (4)
+#define MPI3_EVENT_SAS_TOPO_LR_PREV_MASK (0x0f)
+#define MPI3_EVENT_SAS_TOPO_LR_PREV_SHIFT (0)
+#define MPI3_EVENT_SAS_TOPO_LR_UNKNOWN_LINK_RATE (0x00)
+#define MPI3_EVENT_SAS_TOPO_LR_PHY_DISABLED (0x01)
+#define MPI3_EVENT_SAS_TOPO_LR_NEGOTIATION_FAILED (0x02)
+#define MPI3_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE (0x03)
+#define MPI3_EVENT_SAS_TOPO_LR_PORT_SELECTOR (0x04)
+#define MPI3_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS (0x05)
+#define MPI3_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY (0x06)
+#define MPI3_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0a)
+#define MPI3_EVENT_SAS_TOPO_LR_RATE_12_0 (0x0b)
+#define MPI3_EVENT_SAS_TOPO_LR_RATE_22_5 (0x0c)
+#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_MASK (0xc0)
+#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_SHIFT (6)
+#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_ACCESSIBLE (0x00)
+#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_NO_EXIST (0x40)
+#define MPI3_EVENT_SAS_TOPO_PHY_STATUS_VACANT (0x80)
+#define MPI3_EVENT_SAS_TOPO_PHY_RC_MASK (0x0f)
+#define MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING (0x02)
+#define MPI3_EVENT_SAS_TOPO_PHY_RC_PHY_CHANGED (0x03)
+#define MPI3_EVENT_SAS_TOPO_PHY_RC_NO_CHANGE (0x04)
+#define MPI3_EVENT_SAS_TOPO_PHY_RC_DELAY_NOT_RESPONDING (0x05)
+#define MPI3_EVENT_SAS_TOPO_PHY_RC_RESPONDING (0x06)
+struct mpi3_event_data_sas_topology_change_list {
+ __le16 enclosure_handle;
+ __le16 expander_dev_handle;
+ u8 num_phys;
+ u8 reserved05[3];
+ u8 num_entries;
+ u8 start_phy_num;
+ u8 exp_status;
+ u8 io_unit_port;
+ struct mpi3_event_sas_topo_phy_entry phy_entry[MPI3_EVENT_SAS_TOPO_PHY_COUNT];
+};
+
+#define MPI3_EVENT_SAS_TOPO_ES_NO_EXPANDER (0x00)
+#define MPI3_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02)
+#define MPI3_EVENT_SAS_TOPO_ES_RESPONDING (0x03)
+#define MPI3_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING (0x04)
+struct mpi3_event_data_sas_phy_counter {
+ __le64 time_stamp;
+ __le32 reserved08;
+ u8 phy_event_code;
+ u8 phy_num;
+ __le16 reserved0e;
+ __le32 phy_event_info;
+ u8 counter_type;
+ u8 threshold_window;
+ u8 time_units;
+ u8 reserved17;
+ __le32 event_threshold;
+ __le16 threshold_flags;
+ __le16 reserved1e;
+};
+
+struct mpi3_event_data_sas_device_disc_err {
+ __le16 dev_handle;
+ u8 reason_code;
+ u8 io_unit_port;
+ __le32 reserved04;
+ __le64 sas_address;
+};
+
+#define MPI3_EVENT_SAS_DISC_ERR_RC_SMP_FAILED (0x01)
+#define MPI3_EVENT_SAS_DISC_ERR_RC_SMP_TIMEOUT (0x02)
+struct mpi3_event_data_pcie_enumeration {
+ u8 flags;
+ u8 reason_code;
+ u8 io_unit_port;
+ u8 reserved03;
+ __le32 enumeration_status;
+};
+
+#define MPI3_EVENT_PCIE_ENUM_FLAGS_DEVICE_CHANGE (0x02)
+#define MPI3_EVENT_PCIE_ENUM_FLAGS_IN_PROGRESS (0x01)
+#define MPI3_EVENT_PCIE_ENUM_RC_STARTED (0x01)
+#define MPI3_EVENT_PCIE_ENUM_RC_COMPLETED (0x02)
+#define MPI3_EVENT_PCIE_ENUM_ES_MAX_SWITCH_DEPTH_EXCEED (0x80000000)
+#define MPI3_EVENT_PCIE_ENUM_ES_MAX_SWITCHES_EXCEED (0x40000000)
+#define MPI3_EVENT_PCIE_ENUM_ES_MAX_DEVICES_EXCEED (0x20000000)
+#define MPI3_EVENT_PCIE_ENUM_ES_RESOURCES_EXHAUSTED (0x10000000)
+#ifndef MPI3_EVENT_PCIE_TOPO_PORT_COUNT
+#define MPI3_EVENT_PCIE_TOPO_PORT_COUNT (1)
+#endif
+struct mpi3_event_pcie_topo_port_entry {
+ __le16 attached_dev_handle;
+ u8 port_status;
+ u8 reserved03;
+ u8 current_port_info;
+ u8 reserved05;
+ u8 previous_port_info;
+ u8 reserved07;
+};
+
+#define MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING (0x02)
+#define MPI3_EVENT_PCIE_TOPO_PS_PORT_CHANGED (0x03)
+#define MPI3_EVENT_PCIE_TOPO_PS_NO_CHANGE (0x04)
+#define MPI3_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING (0x05)
+#define MPI3_EVENT_PCIE_TOPO_PS_RESPONDING (0x06)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_MASK (0xf0)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_UNKNOWN (0x00)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_1 (0x10)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_2 (0x20)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_4 (0x30)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_8 (0x40)
+#define MPI3_EVENT_PCIE_TOPO_PI_LANES_16 (0x50)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_MASK (0x0f)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_UNKNOWN (0x00)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_DISABLED (0x01)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_2_5 (0x02)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_5_0 (0x03)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_8_0 (0x04)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_16_0 (0x05)
+#define MPI3_EVENT_PCIE_TOPO_PI_RATE_32_0 (0x06)
+struct mpi3_event_data_pcie_topology_change_list {
+ __le16 enclosure_handle;
+ __le16 switch_dev_handle;
+ u8 num_ports;
+ u8 reserved05[3];
+ u8 num_entries;
+ u8 start_port_num;
+ u8 switch_status;
+ u8 io_unit_port;
+ __le32 reserved0c;
+ struct mpi3_event_pcie_topo_port_entry port_entry[MPI3_EVENT_PCIE_TOPO_PORT_COUNT];
+};
+
+#define MPI3_EVENT_PCIE_TOPO_SS_NO_PCIE_SWITCH (0x00)
+#define MPI3_EVENT_PCIE_TOPO_SS_NOT_RESPONDING (0x02)
+#define MPI3_EVENT_PCIE_TOPO_SS_RESPONDING (0x03)
+#define MPI3_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING (0x04)
+struct mpi3_event_data_sas_init_dev_status_change {
+ u8 reason_code;
+ u8 io_unit_port;
+ __le16 dev_handle;
+ __le32 reserved04;
+ __le64 sas_address;
+};
+
+#define MPI3_EVENT_SAS_INIT_RC_ADDED (0x01)
+#define MPI3_EVENT_SAS_INIT_RC_NOT_RESPONDING (0x02)
+struct mpi3_event_data_sas_init_table_overflow {
+ __le16 max_init;
+ __le16 current_init;
+ __le32 reserved04;
+ __le64 sas_address;
+};
+
+struct mpi3_event_data_hard_reset_received {
+ u8 reserved00;
+ u8 io_unit_port;
+ __le16 reserved02;
+};
+
+#define MPI3_PEL_LOCALE_FLAGS_NON_BLOCKING_BOOT_EVENT (0x0200)
+#define MPI3_PEL_LOCALE_FLAGS_BLOCKING_BOOT_EVENT (0x0100)
+#define MPI3_PEL_LOCALE_FLAGS_PCIE (0x0080)
+#define MPI3_PEL_LOCALE_FLAGS_CONFIGURATION (0x0040)
+#define MPI3_PEL_LOCALE_FLAGS_CONTROLER (0x0020)
+#define MPI3_PEL_LOCALE_FLAGS_SAS (0x0010)
+#define MPI3_PEL_LOCALE_FLAGS_EPACK (0x0008)
+#define MPI3_PEL_LOCALE_FLAGS_ENCLOSURE (0x0004)
+#define MPI3_PEL_LOCALE_FLAGS_PD (0x0002)
+#define MPI3_PEL_LOCALE_FLAGS_VD (0x0001)
+#define MPI3_PEL_CLASS_DEBUG (0x00)
+#define MPI3_PEL_CLASS_PROGRESS (0x01)
+#define MPI3_PEL_CLASS_INFORMATIONAL (0x02)
+#define MPI3_PEL_CLASS_WARNING (0x03)
+#define MPI3_PEL_CLASS_CRITICAL (0x04)
+#define MPI3_PEL_CLASS_FATAL (0x05)
+#define MPI3_PEL_CLASS_FAULT (0x06)
+#define MPI3_PEL_CLEARTYPE_CLEAR (0x00)
+#define MPI3_PEL_WAITTIME_INFINITE_WAIT (0x00)
+#define MPI3_PEL_ACTION_GET_SEQNUM (0x01)
+#define MPI3_PEL_ACTION_MARK_CLEAR (0x02)
+#define MPI3_PEL_ACTION_GET_LOG (0x03)
+#define MPI3_PEL_ACTION_GET_COUNT (0x04)
+#define MPI3_PEL_ACTION_WAIT (0x05)
+#define MPI3_PEL_ACTION_ABORT (0x06)
+#define MPI3_PEL_ACTION_GET_PRINT_STRINGS (0x07)
+#define MPI3_PEL_ACTION_ACKNOWLEDGE (0x08)
+#define MPI3_PEL_STATUS_SUCCESS (0x00)
+#define MPI3_PEL_STATUS_NOT_FOUND (0x01)
+#define MPI3_PEL_STATUS_ABORTED (0x02)
+#define MPI3_PEL_STATUS_NOT_READY (0x03)
+struct mpi3_pel_seq {
+ __le32 newest;
+ __le32 oldest;
+ __le32 clear;
+ __le32 shutdown;
+ __le32 boot;
+ __le32 last_acknowledged;
+};
+
+struct mpi3_pel_entry {
+ __le32 sequence_number;
+ __le32 time_stamp[2];
+ __le16 log_code;
+ __le16 arg_type;
+ __le16 locale;
+ u8 class;
+ u8 reserved13;
+ u8 ext_num;
+ u8 num_exts;
+ u8 arg_data_size;
+ u8 fixed_format_size;
+ __le32 reserved18[2];
+ __le32 pel_info[24];
+};
+
+struct mpi3_pel_list {
+ __le32 log_count;
+ __le32 reserved04;
+ struct mpi3_pel_entry entry[1];
+};
+
+struct mpi3_pel_arg_map {
+ u8 arg_type;
+ u8 length;
+ __le16 start_location;
+};
+
+#define MPI3_PEL_ARG_MAP_ARG_TYPE_APPEND_STRING (0x00)
+#define MPI3_PEL_ARG_MAP_ARG_TYPE_INTEGER (0x01)
+#define MPI3_PEL_ARG_MAP_ARG_TYPE_STRING (0x02)
+#define MPI3_PEL_ARG_MAP_ARG_TYPE_BIT_FIELD (0x03)
+struct mpi3_pel_print_string {
+ __le16 log_code;
+ __le16 string_length;
+ u8 num_arg_map;
+ u8 reserved05[3];
+ struct mpi3_pel_arg_map arg_map[1];
+};
+
+struct mpi3_pel_print_string_list {
+ __le32 num_print_strings;
+ __le32 residual_bytes_remain;
+ __le32 reserved08[2];
+ struct mpi3_pel_print_string print_string[1];
+};
+
+#ifndef MPI3_PEL_ACTION_SPECIFIC_MAX
+#define MPI3_PEL_ACTION_SPECIFIC_MAX (1)
+#endif
+struct mpi3_pel_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 action_specific[MPI3_PEL_ACTION_SPECIFIC_MAX];
+};
+
+struct mpi3_pel_req_action_get_sequence_numbers {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 reserved0c[5];
+ union mpi3_sge_union sgl;
+};
+
+struct mpi3_pel_req_action_clear_log_marker {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ u8 clear_type;
+ u8 reserved0d[3];
+};
+
+struct mpi3_pel_req_action_get_log {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 starting_sequence_number;
+ __le16 locale;
+ u8 class;
+ u8 reserved13;
+ __le32 reserved14[3];
+ union mpi3_sge_union sgl;
+};
+
+struct mpi3_pel_req_action_get_count {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 starting_sequence_number;
+ __le16 locale;
+ u8 class;
+ u8 reserved13;
+ __le32 reserved14[3];
+ union mpi3_sge_union sgl;
+};
+
+struct mpi3_pel_req_action_wait {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 starting_sequence_number;
+ __le16 locale;
+ u8 class;
+ u8 reserved13;
+ __le16 wait_time;
+ __le16 reserved16;
+ __le32 reserved18[2];
+};
+
+struct mpi3_pel_req_action_abort {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 reserved0c;
+ __le16 abort_host_tag;
+ __le16 reserved12;
+ __le32 reserved14;
+};
+
+struct mpi3_pel_req_action_get_print_strings {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 reserved0c;
+ __le16 start_log_code;
+ __le16 reserved12;
+ __le32 reserved14[3];
+ union mpi3_sge_union sgl;
+};
+
+struct mpi3_pel_req_action_acknowledge {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 sequence_number;
+ __le32 reserved10;
+};
+
+#define MPI3_PELACKNOWLEDGE_MSGFLAGS_SAFE_MODE_EXIT (0x01)
+struct mpi3_pel_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+ u8 action;
+ u8 reserved11;
+ __le16 reserved12;
+ __le16 pe_log_status;
+ __le16 reserved16;
+ __le32 transfer_length;
+};
+
+struct mpi3_ci_download_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 action;
+ u8 reserved0b;
+ __le32 signature1;
+ __le32 total_image_size;
+ __le32 image_offset;
+ __le32 segment_size;
+ __le32 reserved1c;
+ union mpi3_sge_union sgl;
+};
+
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_LAST_SEGMENT (0x80)
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_FORCE_FMC_ENABLE (0x40)
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_SIGNED_NVDATA (0x20)
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_WRITE_CACHE_FLUSH_MASK (0x03)
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_WRITE_CACHE_FLUSH_FAST (0x00)
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_WRITE_CACHE_FLUSH_MEDIUM (0x01)
+#define MPI3_CI_DOWNLOAD_MSGFLAGS_WRITE_CACHE_FLUSH_SLOW (0x02)
+#define MPI3_CI_DOWNLOAD_ACTION_DOWNLOAD (0x01)
+#define MPI3_CI_DOWNLOAD_ACTION_ONLINE_ACTIVATION (0x02)
+#define MPI3_CI_DOWNLOAD_ACTION_OFFLINE_ACTIVATION (0x03)
+#define MPI3_CI_DOWNLOAD_ACTION_GET_STATUS (0x04)
+struct mpi3_ci_download_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+ u8 flags;
+ u8 cache_dirty;
+ u8 pending_count;
+ u8 reserved13;
+};
+
+#define MPI3_CI_DOWNLOAD_FLAGS_DOWNLOAD_IN_PROGRESS (0x80)
+#define MPI3_CI_DOWNLOAD_FLAGS_KEY_UPDATE_PENDING (0x10)
+#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_MASK (0x0e)
+#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_NOT_NEEDED (0x00)
+#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_AWAITING (0x02)
+#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_ONLINE_PENDING (0x04)
+#define MPI3_CI_DOWNLOAD_FLAGS_ACTIVATION_STATUS_OFFLINE_PENDING (0x06)
+#define MPI3_CI_DOWNLOAD_FLAGS_COMPATIBLE (0x01)
+struct mpi3_ci_upload_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 reserved0a;
+ __le32 signature1;
+ __le32 reserved10;
+ __le32 image_offset;
+ __le32 segment_size;
+ __le32 reserved1c;
+ union mpi3_sge_union sgl;
+};
+
+#define MPI3_CI_UPLOAD_MSGFLAGS_LOCATION_MASK (0x01)
+#define MPI3_CI_UPLOAD_MSGFLAGS_LOCATION_PRIMARY (0x00)
+#define MPI3_CI_UPLOAD_MSGFLAGS_LOCATION_SECONDARY (0x01)
+#define MPI3_CI_UPLOAD_MSGFLAGS_FORMAT_MASK (0x02)
+#define MPI3_CI_UPLOAD_MSGFLAGS_FORMAT_FLASH (0x00)
+#define MPI3_CI_UPLOAD_MSGFLAGS_FORMAT_EXECUTABLE (0x02)
+#define MPI3_CTRL_OP_FORCE_FULL_DISCOVERY (0x01)
+#define MPI3_CTRL_OP_LOOKUP_MAPPING (0x02)
+#define MPI3_CTRL_OP_UPDATE_TIMESTAMP (0x04)
+#define MPI3_CTRL_OP_GET_TIMESTAMP (0x05)
+#define MPI3_CTRL_OP_REMOVE_DEVICE (0x10)
+#define MPI3_CTRL_OP_CLOSE_PERSISTENT_CONNECTION (0x11)
+#define MPI3_CTRL_OP_HIDDEN_ACK (0x12)
+#define MPI3_CTRL_OP_SAS_SEND_PRIMITIVE (0x20)
+#define MPI3_CTRL_OP_SAS_CLEAR_ERROR_LOG (0x21)
+#define MPI3_CTRL_OP_PCIE_CLEAR_ERROR_LOG (0x22)
+#define MPI3_CTRL_OP_LOOKUP_MAPPING_PARAM8_LOOKUP_METHOD_INDEX (0x00)
+#define MPI3_CTRL_OP_UPDATE_TIMESTAMP_PARAM64_TIMESTAMP_INDEX (0x00)
+#define MPI3_CTRL_OP_REMOVE_DEVICE_PARAM16_DEVHANDLE_INDEX (0x00)
+#define MPI3_CTRL_OP_CLOSE_PERSIST_CONN_PARAM16_DEVHANDLE_INDEX (0x00)
+#define MPI3_CTRL_OP_HIDDEN_ACK_PARAM16_DEVHANDLE_INDEX (0x00)
+#define MPI3_CTRL_OP_SAS_SEND_PRIM_PARAM8_PHY_INDEX (0x00)
+#define MPI3_CTRL_OP_SAS_SEND_PRIM_PARAM8_PRIMSEQ_INDEX (0x01)
+#define MPI3_CTRL_OP_SAS_SEND_PRIM_PARAM32_PRIMITIVE_INDEX (0x00)
+#define MPI3_CTRL_OP_SAS_CLEAR_ERR_LOG_PARAM8_PHY_INDEX (0x00)
+#define MPI3_CTRL_OP_PCIE_CLEAR_ERR_LOG_PARAM8_PHY_INDEX (0x00)
+#define MPI3_CTRL_LOOKUP_METHOD_WWID_ADDRESS (0x01)
+#define MPI3_CTRL_LOOKUP_METHOD_ENCLOSURE_SLOT (0x02)
+#define MPI3_CTRL_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03)
+#define MPI3_CTRL_LOOKUP_METHOD_PERSISTENT_ID (0x04)
+#define MPI3_CTRL_LOOKUP_METHOD_WWIDADDR_PARAM16_DEVH_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_WWIDADDR_PARAM64_WWID_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_ENCLSLOT_PARAM16_SLOTNUM_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_ENCLSLOT_PARAM64_ENCLOSURELID_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_SASDEVNAME_PARAM16_DEVH_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_SASDEVNAME_PARAM64_DEVNAME_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_PERSISTID_PARAM16_DEVH_INDEX (0)
+#define MPI3_CTRL_LOOKUP_METHOD_PERSISTID_PARAM16_PERSISTENT_ID_INDEX (1)
+#define MPI3_CTRL_LOOKUP_METHOD_VALUE16_DEVH_INDEX (0)
+#define MPI3_CTRL_GET_TIMESTAMP_VALUE64_TIMESTAMP_INDEX (0)
+#define MPI3_CTRL_PRIMFLAGS_SINGLE (0x01)
+#define MPI3_CTRL_PRIMFLAGS_TRIPLE (0x03)
+#define MPI3_CTRL_PRIMFLAGS_REDUNDANT (0x06)
+struct mpi3_iounit_control_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 reserved0a;
+ u8 operation;
+ __le32 reserved0c;
+ __le64 param64[2];
+ __le32 param32[4];
+ __le16 param16[4];
+ u8 param8[8];
+};
+
+struct mpi3_iounit_control_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+ __le64 value64[2];
+ __le32 value32[4];
+ __le16 value16[4];
+ u8 value8[8];
+};
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_sas.h b/drivers/scsi/mpi3mr/mpi/mpi30_sas.h
new file mode 100644
index 000000000000..ba5018702960
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_sas.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2016-2021 Broadcom Inc. All rights reserved.
+ *
+ */
+#ifndef MPI30_SAS_H
+#define MPI30_SAS_H 1
+#define MPI3_SAS_DEVICE_INFO_SSP_TARGET (0x00000100)
+#define MPI3_SAS_DEVICE_INFO_STP_SATA_TARGET (0x00000080)
+#define MPI3_SAS_DEVICE_INFO_SMP_TARGET (0x00000040)
+#define MPI3_SAS_DEVICE_INFO_SSP_INITIATOR (0x00000020)
+#define MPI3_SAS_DEVICE_INFO_STP_INITIATOR (0x00000010)
+#define MPI3_SAS_DEVICE_INFO_SMP_INITIATOR (0x00000008)
+#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK (0x00000007)
+#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_NO_DEVICE (0x00000000)
+#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_END_DEVICE (0x00000001)
+#define MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_EXPANDER (0x00000002)
+struct mpi3_smp_passthrough_request {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ u8 reserved0a;
+ u8 io_unit_port;
+ __le32 reserved0c[3];
+ __le64 sas_address;
+ struct mpi3_sge_common request_sge;
+ struct mpi3_sge_common response_sge;
+};
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_transport.h b/drivers/scsi/mpi3mr/mpi/mpi30_transport.h
new file mode 100644
index 000000000000..63e4e81d5397
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_transport.h
@@ -0,0 +1,463 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright 2016-2021 Broadcom Inc. All rights reserved.
+ *
+ */
+#ifndef MPI30_TRANSPORT_H
+#define MPI30_TRANSPORT_H 1
+struct mpi3_version_struct {
+ u8 dev;
+ u8 unit;
+ u8 minor;
+ u8 major;
+};
+
+union mpi3_version_union {
+ struct mpi3_version_struct mpi3_version;
+ __le32 word;
+};
+
+#define MPI3_VERSION_MAJOR (3)
+#define MPI3_VERSION_MINOR (0)
+#define MPI3_VERSION_UNIT (0)
+#define MPI3_VERSION_DEV (18)
+struct mpi3_sysif_oper_queue_indexes {
+ __le16 producer_index;
+ __le16 reserved02;
+ __le16 consumer_index;
+ __le16 reserved06;
+};
+
+struct mpi3_sysif_registers {
+ __le64 ioc_information;
+ union mpi3_version_union version;
+ __le32 reserved0c[2];
+ __le32 ioc_configuration;
+ __le32 reserved18;
+ __le32 ioc_status;
+ __le32 reserved20;
+ __le32 admin_queue_num_entries;
+ __le64 admin_request_queue_address;
+ __le64 admin_reply_queue_address;
+ __le32 reserved38[2];
+ __le32 coalesce_control;
+ __le32 reserved44[1007];
+ __le16 admin_request_queue_pi;
+ __le16 reserved1002;
+ __le16 admin_reply_queue_ci;
+ __le16 reserved1006;
+ struct mpi3_sysif_oper_queue_indexes oper_queue_indexes[383];
+ __le32 reserved1c00;
+ __le32 write_sequence;
+ __le32 host_diagnostic;
+ __le32 reserved1c0c;
+ __le32 fault;
+ __le32 fault_info[3];
+ __le32 reserved1c20[4];
+ __le64 hcb_address;
+ __le32 hcb_size;
+ __le32 reserved1c3c;
+ __le32 reply_free_host_index;
+ __le32 sense_buffer_free_host_index;
+ __le32 reserved1c48[2];
+ __le64 diag_rw_data;
+ __le64 diag_rw_address;
+ __le16 diag_rw_control;
+ __le16 diag_rw_status;
+ __le32 reserved1c64[35];
+ __le32 scratchpad[4];
+ __le32 reserved1d00[192];
+ __le32 device_assigned_registers[2048];
+};
+
+#define MPI3_SYSIF_IOC_INFO_LOW_OFFSET (0x00000000)
+#define MPI3_SYSIF_IOC_INFO_HIGH_OFFSET (0x00000004)
+#define MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_MASK (0xff000000)
+#define MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_SHIFT (24)
+#define MPI3_SYSIF_IOC_CONFIG_OFFSET (0x00000014)
+#define MPI3_SYSIF_IOC_CONFIG_OPER_RPY_ENT_SZ (0x00f00000)
+#define MPI3_SYSIF_IOC_CONFIG_OPER_RPY_ENT_SZ_SHIFT (20)
+#define MPI3_SYSIF_IOC_CONFIG_OPER_REQ_ENT_SZ (0x000f0000)
+#define MPI3_SYSIF_IOC_CONFIG_OPER_REQ_ENT_SZ_SHIFT (16)
+#define MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_MASK (0x0000c000)
+#define MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NO (0x00000000)
+#define MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NORMAL (0x00004000)
+#define MPI3_SYSIF_IOC_CONFIG_DEVICE_SHUTDOWN (0x00002000)
+#define MPI3_SYSIF_IOC_CONFIG_DIAG_SAVE (0x00000010)
+#define MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC (0x00000001)
+#define MPI3_SYSIF_IOC_STATUS_OFFSET (0x0000001c)
+#define MPI3_SYSIF_IOC_STATUS_RESET_HISTORY (0x00000010)
+#define MPI3_SYSIF_IOC_STATUS_SHUTDOWN_MASK (0x0000000c)
+#define MPI3_SYSIF_IOC_STATUS_SHUTDOWN_NONE (0x00000000)
+#define MPI3_SYSIF_IOC_STATUS_SHUTDOWN_IN_PROGRESS (0x00000004)
+#define MPI3_SYSIF_IOC_STATUS_SHUTDOWN_COMPLETE (0x00000008)
+#define MPI3_SYSIF_IOC_STATUS_FAULT (0x00000002)
+#define MPI3_SYSIF_IOC_STATUS_READY (0x00000001)
+#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_OFFSET (0x00000024)
+#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_REQ_MASK (0x0fff)
+#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_REPLY_OFFSET (0x00000026)
+#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_REPLY_MASK (0x0fff0000)
+#define MPI3_SYSIF_ADMIN_Q_NUM_ENTRIES_REPLY_SHIFT (16)
+#define MPI3_SYSIF_ADMIN_REQ_Q_ADDR_LOW_OFFSET (0x00000028)
+#define MPI3_SYSIF_ADMIN_REQ_Q_ADDR_HIGH_OFFSET (0x0000002c)
+#define MPI3_SYSIF_ADMIN_REPLY_Q_ADDR_LOW_OFFSET (0x00000030)
+#define MPI3_SYSIF_ADMIN_REPLY_Q_ADDR_HIGH_OFFSET (0x00000034)
+#define MPI3_SYSIF_COALESCE_CONTROL_OFFSET (0x00000040)
+#define MPI3_SYSIF_COALESCE_CONTROL_ENABLE_MASK (0xc0000000)
+#define MPI3_SYSIF_COALESCE_CONTROL_ENABLE_NO_CHANGE (0x00000000)
+#define MPI3_SYSIF_COALESCE_CONTROL_ENABLE_DISABLE (0x40000000)
+#define MPI3_SYSIF_COALESCE_CONTROL_ENABLE_ENABLE (0xc0000000)
+#define MPI3_SYSIF_COALESCE_CONTROL_VALID (0x30000000)
+#define MPI3_SYSIF_COALESCE_CONTROL_QUEUE_ID_MASK (0x00ff0000)
+#define MPI3_SYSIF_COALESCE_CONTROL_QUEUE_ID_SHIFT (16)
+#define MPI3_SYSIF_COALESCE_CONTROL_TIMEOUT_MASK (0x0000ff00)
+#define MPI3_SYSIF_COALESCE_CONTROL_TIMEOUT_SHIFT (8)
+#define MPI3_SYSIF_COALESCE_CONTROL_DEPTH_MASK (0x000000ff)
+#define MPI3_SYSIF_COALESCE_CONTROL_DEPTH_SHIFT (0)
+#define MPI3_SYSIF_ADMIN_REQ_Q_PI_OFFSET (0x00001000)
+#define MPI3_SYSIF_ADMIN_REPLY_Q_CI_OFFSET (0x00001004)
+#define MPI3_SYSIF_OPER_REQ_Q_PI_OFFSET (0x00001008)
+#define MPI3_SYSIF_OPER_REQ_Q_N_PI_OFFSET(n) (MPI3_SYSIF_OPER_REQ_Q_PI_OFFSET + (((n) - 1) * 8))
+#define MPI3_SYSIF_OPER_REPLY_Q_CI_OFFSET (0x0000100c)
+#define MPI3_SYSIF_OPER_REPLY_Q_N_CI_OFFSET(n) (MPI3_SYSIF_OPER_REPLY_Q_CI_OFFSET + (((n) - 1) * 8))
+#define MPI3_SYSIF_WRITE_SEQUENCE_OFFSET (0x00001c04)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_MASK (0x0000000f)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_FLUSH (0x0)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_1ST (0xf)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND (0x4)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_3RD (0xb)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_4TH (0x2)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_5TH (0x7)
+#define MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_6TH (0xd)
+#define MPI3_SYSIF_HOST_DIAG_OFFSET (0x00001c08)
+#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_MASK (0x00000700)
+#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_NO_RESET (0x00000000)
+#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET (0x00000100)
+#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_FLASH_RCVRY_RESET (0x00000200)
+#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_COMPLETE_RESET (0x00000300)
+#define MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT (0x00000700)
+#define MPI3_SYSIF_HOST_DIAG_SAVE_IN_PROGRESS (0x00000080)
+#define MPI3_SYSIF_HOST_DIAG_SECURE_BOOT (0x00000040)
+#define MPI3_SYSIF_HOST_DIAG_CLEAR_INVALID_FW_IMAGE (0x00000020)
+#define MPI3_SYSIF_HOST_DIAG_INVALID_FW_IMAGE (0x00000010)
+#define MPI3_SYSIF_HOST_DIAG_HCBENABLE (0x00000008)
+#define MPI3_SYSIF_HOST_DIAG_HCBMODE (0x00000004)
+#define MPI3_SYSIF_HOST_DIAG_DIAG_RW_ENABLE (0x00000002)
+#define MPI3_SYSIF_HOST_DIAG_DIAG_WRITE_ENABLE (0x00000001)
+#define MPI3_SYSIF_FAULT_OFFSET (0x00001c10)
+#define MPI3_SYSIF_FAULT_FUNC_AREA_MASK (0xff000000)
+#define MPI3_SYSIF_FAULT_FUNC_AREA_SHIFT (24)
+#define MPI3_SYSIF_FAULT_FUNC_AREA_MPI_DEFINED (0x00000000)
+#define MPI3_SYSIF_FAULT_CODE_MASK (0x0000ffff)
+#define MPI3_SYSIF_FAULT_CODE_DIAG_FAULT_RESET (0x0000f000)
+#define MPI3_SYSIF_FAULT_CODE_CI_ACTIVATION_RESET (0x0000f001)
+#define MPI3_SYSIF_FAULT_CODE_SOFT_RESET_IN_PROGRESS (0x0000f002)
+#define MPI3_SYSIF_FAULT_CODE_COMPLETE_RESET_NEEDED (0x0000f003)
+#define MPI3_SYSIF_FAULT_CODE_SAFE_MODE_EXIT (0x0000f004)
+#define MPI3_SYSIF_FAULT_CODE_FACTORY_RESET (0x0000f005)
+#define MPI3_SYSIF_FAULT_INFO0_OFFSET (0x00001c14)
+#define MPI3_SYSIF_FAULT_INFO1_OFFSET (0x00001c18)
+#define MPI3_SYSIF_FAULT_INFO2_OFFSET (0x00001c1c)
+#define MPI3_SYSIF_HCB_ADDRESS_LOW_OFFSET (0x00001c30)
+#define MPI3_SYSIF_HCB_ADDRESS_HIGH_OFFSET (0x00001c34)
+#define MPI3_SYSIF_HCB_SIZE_OFFSET (0x00001c38)
+#define MPI3_SYSIF_HCB_SIZE_SIZE_MASK (0xfffff000)
+#define MPI3_SYSIF_HCB_SIZE_SIZE_SHIFT (12)
+#define MPI3_SYSIF_HCB_SIZE_HCDW_ENABLE (0x00000001)
+#define MPI3_SYSIF_REPLY_FREE_HOST_INDEX_OFFSET (0x00001c40)
+#define MPI3_SYSIF_SENSE_BUF_FREE_HOST_INDEX_OFFSET (0x00001c44)
+#define MPI3_SYSIF_DIAG_RW_DATA_LOW_OFFSET (0x00001c50)
+#define MPI3_SYSIF_DIAG_RW_DATA_HIGH_OFFSET (0x00001c54)
+#define MPI3_SYSIF_DIAG_RW_ADDRESS_LOW_OFFSET (0x00001c58)
+#define MPI3_SYSIF_DIAG_RW_ADDRESS_HIGH_OFFSET (0x00001c5c)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_OFFSET (0x00001c60)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_MASK (0x00000030)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_1BYTE (0x00000000)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_2BYTES (0x00000010)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_4BYTES (0x00000020)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_LEN_8BYTES (0x00000030)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_RESET (0x00000004)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_DIR_MASK (0x00000002)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_DIR_READ (0x00000000)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_DIR_WRITE (0x00000002)
+#define MPI3_SYSIF_DIAG_RW_CONTROL_START (0x00000001)
+#define MPI3_SYSIF_DIAG_RW_STATUS_OFFSET (0x00001c62)
+#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_MASK (0x0000000e)
+#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_SUCCESS (0x00000000)
+#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_INV_ADDR (0x00000002)
+#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_ACC_ERR (0x00000004)
+#define MPI3_SYSIF_DIAG_RW_STATUS_STATUS_PAR_ERR (0x00000006)
+#define MPI3_SYSIF_DIAG_RW_STATUS_BUSY (0x00000001)
+#define MPI3_SYSIF_SCRATCHPAD0_OFFSET (0x00001cf0)
+#define MPI3_SYSIF_SCRATCHPAD1_OFFSET (0x00001cf4)
+#define MPI3_SYSIF_SCRATCHPAD2_OFFSET (0x00001cf8)
+#define MPI3_SYSIF_SCRATCHPAD3_OFFSET (0x00001cfc)
+#define MPI3_SYSIF_DEVICE_ASSIGNED_REGS_OFFSET (0x00002000)
+#define MPI3_SYSIF_DIAG_SAVE_TIMEOUT (60)
+struct mpi3_default_reply_descriptor {
+ __le32 descriptor_type_dependent1[2];
+ __le16 request_queue_ci;
+ __le16 request_queue_id;
+ __le16 descriptor_type_dependent2;
+ __le16 reply_flags;
+};
+
+#define MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK (0x0001)
+#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_MASK (0xf000)
+#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_ADDRESS_REPLY (0x0000)
+#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SUCCESS (0x1000)
+#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_TARGET_COMMAND_BUFFER (0x2000)
+#define MPI3_REPLY_DESCRIPT_FLAGS_TYPE_STATUS (0x3000)
+struct mpi3_address_reply_descriptor {
+ __le64 reply_frame_address;
+ __le16 request_queue_ci;
+ __le16 request_queue_id;
+ __le16 reserved0c;
+ __le16 reply_flags;
+};
+
+struct mpi3_success_reply_descriptor {
+ __le32 reserved00[2];
+ __le16 request_queue_ci;
+ __le16 request_queue_id;
+ __le16 host_tag;
+ __le16 reply_flags;
+};
+
+struct mpi3_target_command_buffer_reply_descriptor {
+ __le32 reserved00;
+ __le16 initiator_dev_handle;
+ u8 phy_num;
+ u8 reserved07;
+ __le16 request_queue_ci;
+ __le16 request_queue_id;
+ __le16 io_index;
+ __le16 reply_flags;
+};
+
+struct mpi3_status_reply_descriptor {
+ __le16 ioc_status;
+ __le16 reserved02;
+ __le32 ioc_log_info;
+ __le16 request_queue_ci;
+ __le16 request_queue_id;
+ __le16 host_tag;
+ __le16 reply_flags;
+};
+
+#define MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL (0x8000)
+#define MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK (0x7fff)
+#define MPI3_REPLY_DESCRIPT_STATUS_IOCLOGINFO_TYPE_MASK (0xf0000000)
+#define MPI3_REPLY_DESCRIPT_STATUS_IOCLOGINFO_TYPE_NO_INFO (0x00000000)
+#define MPI3_REPLY_DESCRIPT_STATUS_IOCLOGINFO_TYPE_SAS (0x30000000)
+#define MPI3_REPLY_DESCRIPT_STATUS_IOCLOGINFO_DATA_MASK (0x0fffffff)
+union mpi3_reply_descriptors_union {
+ struct mpi3_default_reply_descriptor default_reply;
+ struct mpi3_address_reply_descriptor address_reply;
+ struct mpi3_success_reply_descriptor success;
+ struct mpi3_target_command_buffer_reply_descriptor target_command_buffer;
+ struct mpi3_status_reply_descriptor status;
+ __le32 words[4];
+};
+
+struct mpi3_sge_common {
+ __le64 address;
+ __le32 length;
+ u8 reserved0c[3];
+ u8 flags;
+};
+
+struct mpi3_sge_bit_bucket {
+ __le64 reserved00;
+ __le32 length;
+ u8 reserved0c[3];
+ u8 flags;
+};
+
+struct mpi3_sge_extended_eedp {
+ u8 user_data_size;
+ u8 reserved01;
+ __le16 eedp_flags;
+ __le32 secondary_reference_tag;
+ __le16 secondary_application_tag;
+ __le16 application_tag_translation_mask;
+ __le16 reserved0c;
+ u8 extended_operation;
+ u8 flags;
+};
+
+union mpi3_sge_union {
+ struct mpi3_sge_common simple;
+ struct mpi3_sge_common chain;
+ struct mpi3_sge_common last_chain;
+ struct mpi3_sge_bit_bucket bit_bucket;
+ struct mpi3_sge_extended_eedp eedp;
+ __le32 words[4];
+};
+
+#define MPI3_SGE_FLAGS_ELEMENT_TYPE_MASK (0xf0)
+#define MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE (0x00)
+#define MPI3_SGE_FLAGS_ELEMENT_TYPE_BIT_BUCKET (0x10)
+#define MPI3_SGE_FLAGS_ELEMENT_TYPE_CHAIN (0x20)
+#define MPI3_SGE_FLAGS_ELEMENT_TYPE_LAST_CHAIN (0x30)
+#define MPI3_SGE_FLAGS_ELEMENT_TYPE_EXTENDED (0xf0)
+#define MPI3_SGE_FLAGS_END_OF_LIST (0x08)
+#define MPI3_SGE_FLAGS_END_OF_BUFFER (0x04)
+#define MPI3_SGE_FLAGS_DLAS_MASK (0x03)
+#define MPI3_SGE_FLAGS_DLAS_SYSTEM (0x00)
+#define MPI3_SGE_FLAGS_DLAS_IOC_DDR (0x01)
+#define MPI3_SGE_FLAGS_DLAS_IOC_CTL (0x02)
+#define MPI3_SGE_EXT_OPER_EEDP (0x00)
+#define MPI3_EEDPFLAGS_INCR_PRI_REF_TAG (0x8000)
+#define MPI3_EEDPFLAGS_INCR_SEC_REF_TAG (0x4000)
+#define MPI3_EEDPFLAGS_INCR_PRI_APP_TAG (0x2000)
+#define MPI3_EEDPFLAGS_INCR_SEC_APP_TAG (0x1000)
+#define MPI3_EEDPFLAGS_ESC_PASSTHROUGH (0x0800)
+#define MPI3_EEDPFLAGS_CHK_REF_TAG (0x0400)
+#define MPI3_EEDPFLAGS_CHK_APP_TAG (0x0200)
+#define MPI3_EEDPFLAGS_CHK_GUARD (0x0100)
+#define MPI3_EEDPFLAGS_ESC_MODE_MASK (0x00c0)
+#define MPI3_EEDPFLAGS_ESC_MODE_DO_NOT_DISABLE (0x0040)
+#define MPI3_EEDPFLAGS_ESC_MODE_APPTAG_DISABLE (0x0080)
+#define MPI3_EEDPFLAGS_ESC_MODE_APPTAG_REFTAG_DISABLE (0x00c0)
+#define MPI3_EEDPFLAGS_HOST_GUARD_MASK (0x0030)
+#define MPI3_EEDPFLAGS_HOST_GUARD_T10_CRC (0x0000)
+#define MPI3_EEDPFLAGS_HOST_GUARD_IP_CHKSUM (0x0010)
+#define MPI3_EEDPFLAGS_HOST_GUARD_OEM_SPECIFIC (0x0020)
+#define MPI3_EEDPFLAGS_PT_REF_TAG (0x0008)
+#define MPI3_EEDPFLAGS_EEDP_OP_MASK (0x0007)
+#define MPI3_EEDPFLAGS_EEDP_OP_NOOP (0x0000)
+#define MPI3_EEDPFLAGS_EEDP_OP_CHECK (0x0001)
+#define MPI3_EEDPFLAGS_EEDP_OP_STRIP (0x0002)
+#define MPI3_EEDPFLAGS_EEDP_OP_CHECK_REMOVE (0x0003)
+#define MPI3_EEDPFLAGS_EEDP_OP_INSERT (0x0004)
+#define MPI3_EEDPFLAGS_EEDP_OP_REPLACE (0x0006)
+#define MPI3_EEDPFLAGS_EEDP_OP_CHECK_REGEN (0x0007)
+#define MPI3_EEDP_UDS_512 (0x01)
+#define MPI3_EEDP_UDS_520 (0x02)
+#define MPI3_EEDP_UDS_4080 (0x03)
+#define MPI3_EEDP_UDS_4088 (0x04)
+#define MPI3_EEDP_UDS_4096 (0x05)
+#define MPI3_EEDP_UDS_4104 (0x06)
+#define MPI3_EEDP_UDS_4160 (0x07)
+struct mpi3_request_header {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 change_count;
+ __le16 function_dependent;
+};
+
+struct mpi3_default_reply {
+ __le16 host_tag;
+ u8 ioc_use_only02;
+ u8 function;
+ __le16 ioc_use_only04;
+ u8 ioc_use_only06;
+ u8 msg_flags;
+ __le16 ioc_use_only08;
+ __le16 ioc_status;
+ __le32 ioc_log_info;
+};
+
+#define MPI3_HOST_TAG_INVALID (0xffff)
+#define MPI3_FUNCTION_IOC_FACTS (0x01)
+#define MPI3_FUNCTION_IOC_INIT (0x02)
+#define MPI3_FUNCTION_PORT_ENABLE (0x03)
+#define MPI3_FUNCTION_EVENT_NOTIFICATION (0x04)
+#define MPI3_FUNCTION_EVENT_ACK (0x05)
+#define MPI3_FUNCTION_CI_DOWNLOAD (0x06)
+#define MPI3_FUNCTION_CI_UPLOAD (0x07)
+#define MPI3_FUNCTION_IO_UNIT_CONTROL (0x08)
+#define MPI3_FUNCTION_PERSISTENT_EVENT_LOG (0x09)
+#define MPI3_FUNCTION_MGMT_PASSTHROUGH (0x0a)
+#define MPI3_FUNCTION_CONFIG (0x10)
+#define MPI3_FUNCTION_SCSI_IO (0x20)
+#define MPI3_FUNCTION_SCSI_TASK_MGMT (0x21)
+#define MPI3_FUNCTION_SMP_PASSTHROUGH (0x22)
+#define MPI3_FUNCTION_NVME_ENCAPSULATED (0x24)
+#define MPI3_FUNCTION_TARGET_ASSIST (0x30)
+#define MPI3_FUNCTION_TARGET_STATUS_SEND (0x31)
+#define MPI3_FUNCTION_TARGET_MODE_ABORT (0x32)
+#define MPI3_FUNCTION_TARGET_CMD_BUF_POST_BASE (0x33)
+#define MPI3_FUNCTION_TARGET_CMD_BUF_POST_LIST (0x34)
+#define MPI3_FUNCTION_CREATE_REQUEST_QUEUE (0x70)
+#define MPI3_FUNCTION_DELETE_REQUEST_QUEUE (0x71)
+#define MPI3_FUNCTION_CREATE_REPLY_QUEUE (0x72)
+#define MPI3_FUNCTION_DELETE_REPLY_QUEUE (0x73)
+#define MPI3_FUNCTION_TOOLBOX (0x80)
+#define MPI3_FUNCTION_DIAG_BUFFER_POST (0x81)
+#define MPI3_FUNCTION_DIAG_BUFFER_MANAGE (0x82)
+#define MPI3_FUNCTION_DIAG_BUFFER_UPLOAD (0x83)
+#define MPI3_FUNCTION_MIN_IOC_USE_ONLY (0xc0)
+#define MPI3_FUNCTION_MAX_IOC_USE_ONLY (0xef)
+#define MPI3_FUNCTION_MIN_PRODUCT_SPECIFIC (0xf0)
+#define MPI3_FUNCTION_MAX_PRODUCT_SPECIFIC (0xff)
+#define MPI3_IOCSTATUS_LOG_INFO_AVAIL_MASK (0x8000)
+#define MPI3_IOCSTATUS_LOG_INFO_AVAILABLE (0x8000)
+#define MPI3_IOCSTATUS_STATUS_MASK (0x7fff)
+#define MPI3_IOCSTATUS_SUCCESS (0x0000)
+#define MPI3_IOCSTATUS_INVALID_FUNCTION (0x0001)
+#define MPI3_IOCSTATUS_BUSY (0x0002)
+#define MPI3_IOCSTATUS_INVALID_SGL (0x0003)
+#define MPI3_IOCSTATUS_INTERNAL_ERROR (0x0004)
+#define MPI3_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006)
+#define MPI3_IOCSTATUS_INVALID_FIELD (0x0007)
+#define MPI3_IOCSTATUS_INVALID_STATE (0x0008)
+#define MPI3_IOCSTATUS_INSUFFICIENT_POWER (0x000a)
+#define MPI3_IOCSTATUS_INVALID_CHANGE_COUNT (0x000b)
+#define MPI3_IOCSTATUS_FAILURE (0x001f)
+#define MPI3_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020)
+#define MPI3_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021)
+#define MPI3_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022)
+#define MPI3_IOCSTATUS_CONFIG_INVALID_DATA (0x0023)
+#define MPI3_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024)
+#define MPI3_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025)
+#define MPI3_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040)
+#define MPI3_IOCSTATUS_SCSI_TM_NOT_SUPPORTED (0x0041)
+#define MPI3_IOCSTATUS_SCSI_INVALID_DEVHANDLE (0x0042)
+#define MPI3_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043)
+#define MPI3_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044)
+#define MPI3_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045)
+#define MPI3_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046)
+#define MPI3_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047)
+#define MPI3_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048)
+#define MPI3_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049)
+#define MPI3_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004a)
+#define MPI3_IOCSTATUS_SCSI_IOC_TERMINATED (0x004b)
+#define MPI3_IOCSTATUS_SCSI_EXT_TERMINATED (0x004c)
+#define MPI3_IOCSTATUS_EEDP_GUARD_ERROR (0x004d)
+#define MPI3_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004e)
+#define MPI3_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004f)
+#define MPI3_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062)
+#define MPI3_IOCSTATUS_TARGET_ABORTED (0x0063)
+#define MPI3_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064)
+#define MPI3_IOCSTATUS_TARGET_NO_CONNECTION (0x0065)
+#define MPI3_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006a)
+#define MPI3_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006d)
+#define MPI3_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006e)
+#define MPI3_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006f)
+#define MPI3_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT (0x0070)
+#define MPI3_IOCSTATUS_TARGET_NAK_RECEIVED (0x0071)
+#define MPI3_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090)
+#define MPI3_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091)
+#define MPI3_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00a0)
+#define MPI3_IOCSTATUS_CI_UNSUPPORTED (0x00b0)
+#define MPI3_IOCSTATUS_CI_UPDATE_SEQUENCE (0x00b1)
+#define MPI3_IOCSTATUS_CI_VALIDATION_FAILED (0x00b2)
+#define MPI3_IOCSTATUS_CI_UPDATE_PENDING (0x00b3)
+#define MPI3_IOCSTATUS_SECURITY_KEY_REQUIRED (0x00c0)
+#define MPI3_IOCSTATUS_INVALID_QUEUE_ID (0x0f00)
+#define MPI3_IOCSTATUS_INVALID_QUEUE_SIZE (0x0f01)
+#define MPI3_IOCSTATUS_INVALID_MSIX_VECTOR (0x0f02)
+#define MPI3_IOCSTATUS_INVALID_REPLY_QUEUE_ID (0x0f03)
+#define MPI3_IOCSTATUS_INVALID_QUEUE_DELETION (0x0f04)
+#define MPI3_IOCLOGINFO_TYPE_MASK (0xf0000000)
+#define MPI3_IOCLOGINFO_TYPE_SHIFT (28)
+#define MPI3_IOCLOGINFO_TYPE_NONE (0x0)
+#define MPI3_IOCLOGINFO_TYPE_SAS (0x3)
+#define MPI3_IOCLOGINFO_LOG_DATA_MASK (0x0fffffff)
+#endif
diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h
new file mode 100644
index 000000000000..5d5529167350
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi3mr.h
@@ -0,0 +1,900 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Broadcom MPI3 Storage Controllers
+ *
+ * Copyright (C) 2017-2021 Broadcom Inc.
+ * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
+ *
+ */
+
+#ifndef MPI3MR_H_INCLUDED
+#define MPI3MR_H_INCLUDED
+
+#include <linux/blkdev.h>
+#include <linux/blk-mq.h>
+#include <linux/blk-mq-pci.h>
+#include <linux/delay.h>
+#include <linux/dmapool.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/utsname.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+
+#include "mpi/mpi30_transport.h"
+#include "mpi/mpi30_cnfg.h"
+#include "mpi/mpi30_image.h"
+#include "mpi/mpi30_init.h"
+#include "mpi/mpi30_ioc.h"
+#include "mpi/mpi30_sas.h"
+#include "mpi3mr_debug.h"
+
+/* Global list and lock for storing multiple adapters managed by the driver */
+extern spinlock_t mrioc_list_lock;
+extern struct list_head mrioc_list;
+
+#define MPI3MR_DRIVER_VERSION "00.255.45.01"
+#define MPI3MR_DRIVER_RELDATE "12-December-2020"
+
+#define MPI3MR_DRIVER_NAME "mpi3mr"
+#define MPI3MR_DRIVER_LICENSE "GPL"
+#define MPI3MR_DRIVER_AUTHOR "Broadcom Inc. <mpi3mr-linuxdrv.pdl@broadcom.com>"
+#define MPI3MR_DRIVER_DESC "MPI3 Storage Controller Device Driver"
+
+#define MPI3MR_NAME_LENGTH 32
+#define IOCNAME "%s: "
+
+/* Definitions for internal SGL and Chain SGL buffers */
+#define MPI3MR_PAGE_SIZE_4K 4096
+#define MPI3MR_SG_DEPTH (MPI3MR_PAGE_SIZE_4K / sizeof(struct mpi3_sge_common))
+
+/* Definitions for MAX values for shost */
+#define MPI3MR_MAX_CMDS_LUN 7
+#define MPI3MR_MAX_CDB_LENGTH 32
+
+/* Admin queue management definitions */
+#define MPI3MR_ADMIN_REQ_Q_SIZE (2 * MPI3MR_PAGE_SIZE_4K)
+#define MPI3MR_ADMIN_REPLY_Q_SIZE (4 * MPI3MR_PAGE_SIZE_4K)
+#define MPI3MR_ADMIN_REQ_FRAME_SZ 128
+#define MPI3MR_ADMIN_REPLY_FRAME_SZ 16
+
+/* Operational queue management definitions */
+#define MPI3MR_OP_REQ_Q_QD 512
+#define MPI3MR_OP_REP_Q_QD 4096
+#define MPI3MR_OP_REQ_Q_SEG_SIZE 4096
+#define MPI3MR_OP_REP_Q_SEG_SIZE 4096
+#define MPI3MR_MAX_SEG_LIST_SIZE 4096
+
+/* Reserved Host Tag definitions */
+#define MPI3MR_HOSTTAG_INVALID 0xFFFF
+#define MPI3MR_HOSTTAG_INITCMDS 1
+#define MPI3MR_HOSTTAG_IOCTLCMDS 2
+#define MPI3MR_HOSTTAG_BLK_TMS 5
+
+#define MPI3MR_NUM_DEVRMCMD 1
+#define MPI3MR_HOSTTAG_DEVRMCMD_MIN (MPI3MR_HOSTTAG_BLK_TMS + 1)
+#define MPI3MR_HOSTTAG_DEVRMCMD_MAX (MPI3MR_HOSTTAG_DEVRMCMD_MIN + \
+ MPI3MR_NUM_DEVRMCMD - 1)
+
+#define MPI3MR_INTERNAL_CMDS_RESVD MPI3MR_HOSTTAG_DEVRMCMD_MAX
+
+/* Reduced resource count definition for crash kernel */
+#define MPI3MR_HOST_IOS_KDUMP 128
+
+/* command/controller interaction timeout definitions in seconds */
+#define MPI3MR_INTADMCMD_TIMEOUT 10
+#define MPI3MR_PORTENABLE_TIMEOUT 300
+#define MPI3MR_ABORTTM_TIMEOUT 30
+#define MPI3MR_RESETTM_TIMEOUT 30
+#define MPI3MR_RESET_HOST_IOWAIT_TIMEOUT 5
+#define MPI3MR_TSUPDATE_INTERVAL 900
+#define MPI3MR_DEFAULT_SHUTDOWN_TIME 120
+#define MPI3MR_RAID_ERRREC_RESET_TIMEOUT 180
+
+#define MPI3MR_WATCHDOG_INTERVAL 1000 /* in milli seconds */
+
+/* Internal admin command state definitions*/
+#define MPI3MR_CMD_NOTUSED 0x8000
+#define MPI3MR_CMD_COMPLETE 0x0001
+#define MPI3MR_CMD_PENDING 0x0002
+#define MPI3MR_CMD_REPLY_VALID 0x0004
+#define MPI3MR_CMD_RESET 0x0008
+
+/* Definitions for Event replies and sense buffer allocated per controller */
+#define MPI3MR_NUM_EVT_REPLIES 64
+#define MPI3MR_SENSEBUF_SZ 256
+#define MPI3MR_SENSEBUF_FACTOR 3
+#define MPI3MR_CHAINBUF_FACTOR 3
+#define MPI3MR_CHAINBUFDIX_FACTOR 2
+
+/* Invalid target device handle */
+#define MPI3MR_INVALID_DEV_HANDLE 0xFFFF
+
+/* Controller Reset related definitions */
+#define MPI3MR_HOSTDIAG_UNLOCK_RETRY_COUNT 5
+#define MPI3MR_MAX_RESET_RETRY_COUNT 3
+
+/* ResponseCode definitions */
+#define MPI3MR_RI_MASK_RESPCODE (0x000000FF)
+#define MPI3MR_RSP_TM_COMPLETE 0x00
+#define MPI3MR_RSP_INVALID_FRAME 0x02
+#define MPI3MR_RSP_TM_NOT_SUPPORTED 0x04
+#define MPI3MR_RSP_TM_FAILED 0x05
+#define MPI3MR_RSP_TM_SUCCEEDED 0x08
+#define MPI3MR_RSP_TM_INVALID_LUN 0x09
+#define MPI3MR_RSP_TM_OVERLAPPED_TAG 0x0A
+#define MPI3MR_RSP_IO_QUEUED_ON_IOC \
+ MPI3_SCSITASKMGMT_RSPCODE_IO_QUEUED_ON_IOC
+
+#define MPI3MR_DEFAULT_MDTS (128 * 1024)
+/* Command retry count definitions */
+#define MPI3MR_DEV_RMHS_RETRY_COUNT 3
+
+/* Default target device queue depth */
+#define MPI3MR_DEFAULT_SDEV_QD 32
+
+/* Definitions for Threaded IRQ poll*/
+#define MPI3MR_IRQ_POLL_SLEEP 2
+#define MPI3MR_IRQ_POLL_TRIGGER_IOCOUNT 8
+
+/* Definitions for the controller security status*/
+#define MPI3MR_CTLR_SECURITY_STATUS_MASK 0x0C
+#define MPI3MR_CTLR_SECURE_DBG_STATUS_MASK 0x02
+
+#define MPI3MR_INVALID_DEVICE 0x00
+#define MPI3MR_CONFIG_SECURE_DEVICE 0x04
+#define MPI3MR_HARD_SECURE_DEVICE 0x08
+#define MPI3MR_TAMPERED_DEVICE 0x0C
+
+/* SGE Flag definition */
+#define MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST \
+ (MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE | MPI3_SGE_FLAGS_DLAS_SYSTEM | \
+ MPI3_SGE_FLAGS_END_OF_LIST)
+
+/* MSI Index from Reply Queue Index */
+#define REPLY_QUEUE_IDX_TO_MSIX_IDX(qidx, offset) (qidx + offset)
+
+/* IOC State definitions */
+enum mpi3mr_iocstate {
+ MRIOC_STATE_READY = 1,
+ MRIOC_STATE_RESET,
+ MRIOC_STATE_FAULT,
+ MRIOC_STATE_BECOMING_READY,
+ MRIOC_STATE_RESET_REQUESTED,
+ MRIOC_STATE_UNRECOVERABLE,
+};
+
+/* Reset reason code definitions*/
+enum mpi3mr_reset_reason {
+ MPI3MR_RESET_FROM_BRINGUP = 1,
+ MPI3MR_RESET_FROM_FAULT_WATCH = 2,
+ MPI3MR_RESET_FROM_IOCTL = 3,
+ MPI3MR_RESET_FROM_EH_HOS = 4,
+ MPI3MR_RESET_FROM_TM_TIMEOUT = 5,
+ MPI3MR_RESET_FROM_IOCTL_TIMEOUT = 6,
+ MPI3MR_RESET_FROM_MUR_FAILURE = 7,
+ MPI3MR_RESET_FROM_CTLR_CLEANUP = 8,
+ MPI3MR_RESET_FROM_CIACTIV_FAULT = 9,
+ MPI3MR_RESET_FROM_PE_TIMEOUT = 10,
+ MPI3MR_RESET_FROM_TSU_TIMEOUT = 11,
+ MPI3MR_RESET_FROM_DELREQQ_TIMEOUT = 12,
+ MPI3MR_RESET_FROM_DELREPQ_TIMEOUT = 13,
+ MPI3MR_RESET_FROM_CREATEREPQ_TIMEOUT = 14,
+ MPI3MR_RESET_FROM_CREATEREQQ_TIMEOUT = 15,
+ MPI3MR_RESET_FROM_IOCFACTS_TIMEOUT = 16,
+ MPI3MR_RESET_FROM_IOCINIT_TIMEOUT = 17,
+ MPI3MR_RESET_FROM_EVTNOTIFY_TIMEOUT = 18,
+ MPI3MR_RESET_FROM_EVTACK_TIMEOUT = 19,
+ MPI3MR_RESET_FROM_CIACTVRST_TIMER = 20,
+ MPI3MR_RESET_FROM_GETPKGVER_TIMEOUT = 21,
+ MPI3MR_RESET_FROM_PELABORT_TIMEOUT = 22,
+ MPI3MR_RESET_FROM_SYSFS = 23,
+ MPI3MR_RESET_FROM_SYSFS_TIMEOUT = 24
+};
+
+/**
+ * struct mpi3mr_compimg_ver - replica of component image
+ * version defined in mpi30_image.h in host endianness
+ *
+ */
+struct mpi3mr_compimg_ver {
+ u16 build_num;
+ u16 cust_id;
+ u8 ph_minor;
+ u8 ph_major;
+ u8 gen_minor;
+ u8 gen_major;
+};
+
+/**
+ * struct mpi3mr_ioc_facs - replica of component image version
+ * defined in mpi30_ioc.h in host endianness
+ *
+ */
+struct mpi3mr_ioc_facts {
+ u32 ioc_capabilities;
+ struct mpi3mr_compimg_ver fw_ver;
+ u32 mpi_version;
+ u16 max_reqs;
+ u16 product_id;
+ u16 op_req_sz;
+ u16 reply_sz;
+ u16 exceptions;
+ u16 max_perids;
+ u16 max_pds;
+ u16 max_sasexpanders;
+ u16 max_sasinitiators;
+ u16 max_enclosures;
+ u16 max_pcie_switches;
+ u16 max_nvme;
+ u16 max_vds;
+ u16 max_hpds;
+ u16 max_advhpds;
+ u16 max_raidpds;
+ u16 min_devhandle;
+ u16 max_devhandle;
+ u16 max_op_req_q;
+ u16 max_op_reply_q;
+ u16 shutdown_timeout;
+ u8 ioc_num;
+ u8 who_init;
+ u16 max_msix_vectors;
+ u8 personality;
+ u8 dma_mask;
+ u8 protocol_flags;
+ u8 sge_mod_mask;
+ u8 sge_mod_value;
+ u8 sge_mod_shift;
+};
+
+/**
+ * struct segments - memory descriptor structure to store
+ * virtual and dma addresses for operational queue segments.
+ *
+ * @segment: virtual address
+ * @segment_dma: dma address
+ */
+struct segments {
+ void *segment;
+ dma_addr_t segment_dma;
+};
+
+/**
+ * struct op_req_qinfo - Operational Request Queue Information
+ *
+ * @ci: consumer index
+ * @pi: producer index
+ * @num_request: Maximum number of entries in the queue
+ * @qid: Queue Id starting from 1
+ * @reply_qid: Associated reply queue Id
+ * @num_segments: Number of discontiguous memory segments
+ * @segment_qd: Depth of each segments
+ * @q_lock: Concurrent queue access lock
+ * @q_segments: Segment descriptor pointer
+ * @q_segment_list: Segment list base virtual address
+ * @q_segment_list_dma: Segment list base DMA address
+ */
+struct op_req_qinfo {
+ u16 ci;
+ u16 pi;
+ u16 num_requests;
+ u16 qid;
+ u16 reply_qid;
+ u16 num_segments;
+ u16 segment_qd;
+ spinlock_t q_lock;
+ struct segments *q_segments;
+ void *q_segment_list;
+ dma_addr_t q_segment_list_dma;
+};
+
+/**
+ * struct op_reply_qinfo - Operational Reply Queue Information
+ *
+ * @ci: consumer index
+ * @qid: Queue Id starting from 1
+ * @num_replies: Maximum number of entries in the queue
+ * @num_segments: Number of discontiguous memory segments
+ * @segment_qd: Depth of each segments
+ * @q_segments: Segment descriptor pointer
+ * @q_segment_list: Segment list base virtual address
+ * @q_segment_list_dma: Segment list base DMA address
+ * @ephase: Expected phased identifier for the reply queue
+ * @pend_ios: Number of IOs pending in HW for this queue
+ * @enable_irq_poll: Flag to indicate polling is enabled
+ * @in_use: Queue is handled by poll/ISR
+ */
+struct op_reply_qinfo {
+ u16 ci;
+ u16 qid;
+ u16 num_replies;
+ u16 num_segments;
+ u16 segment_qd;
+ struct segments *q_segments;
+ void *q_segment_list;
+ dma_addr_t q_segment_list_dma;
+ u8 ephase;
+ atomic_t pend_ios;
+ bool enable_irq_poll;
+ atomic_t in_use;
+};
+
+/**
+ * struct mpi3mr_intr_info - Interrupt cookie information
+ *
+ * @mrioc: Adapter instance reference
+ * @msix_index: MSIx index
+ * @op_reply_q: Associated operational reply queue
+ * @name: Dev name for the irq claiming device
+ */
+struct mpi3mr_intr_info {
+ struct mpi3mr_ioc *mrioc;
+ u16 msix_index;
+ struct op_reply_qinfo *op_reply_q;
+ char name[MPI3MR_NAME_LENGTH];
+};
+
+/**
+ * struct tgt_dev_sas_sata - SAS/SATA device specific
+ * information cached from firmware given data
+ *
+ * @sas_address: World wide unique SAS address
+ * @dev_info: Device information bits
+ */
+struct tgt_dev_sas_sata {
+ u64 sas_address;
+ u16 dev_info;
+};
+
+/**
+ * struct tgt_dev_pcie - PCIe device specific information cached
+ * from firmware given data
+ *
+ * @mdts: Maximum data transfer size
+ * @capb: Device capabilities
+ * @pgsz: Device page size
+ * @abort_to: Timeout for abort TM
+ * @reset_to: Timeout for Target/LUN reset TM
+ */
+struct tgt_dev_pcie {
+ u32 mdts;
+ u16 capb;
+ u8 pgsz;
+ u8 abort_to;
+ u8 reset_to;
+};
+
+/**
+ * struct tgt_dev_volume - virtual device specific information
+ * cached from firmware given data
+ *
+ * @state: State of the VD
+ */
+struct tgt_dev_volume {
+ u8 state;
+};
+
+/**
+ * union _form_spec_inf - union of device specific information
+ */
+union _form_spec_inf {
+ struct tgt_dev_sas_sata sas_sata_inf;
+ struct tgt_dev_pcie pcie_inf;
+ struct tgt_dev_volume vol_inf;
+};
+
+
+
+/**
+ * struct mpi3mr_tgt_dev - target device data structure
+ *
+ * @list: List pointer
+ * @starget: Scsi_target pointer
+ * @dev_handle: FW device handle
+ * @parent_handle: FW parent device handle
+ * @slot: Slot number
+ * @encl_handle: FW enclosure handle
+ * @perst_id: FW assigned Persistent ID
+ * @dev_type: SAS/SATA/PCIE device type
+ * @is_hidden: Should be exposed to upper layers or not
+ * @host_exposed: Already exposed to host or not
+ * @q_depth: Device specific Queue Depth
+ * @wwid: World wide ID
+ * @dev_spec: Device type specific information
+ * @ref_count: Reference count
+ */
+struct mpi3mr_tgt_dev {
+ struct list_head list;
+ struct scsi_target *starget;
+ u16 dev_handle;
+ u16 parent_handle;
+ u16 slot;
+ u16 encl_handle;
+ u16 perst_id;
+ u8 dev_type;
+ u8 is_hidden;
+ u8 host_exposed;
+ u16 q_depth;
+ u64 wwid;
+ union _form_spec_inf dev_spec;
+ struct kref ref_count;
+};
+
+/**
+ * mpi3mr_tgtdev_get - k reference incrementor
+ * @s: Target device reference
+ *
+ * Increment target device reference count.
+ */
+static inline void mpi3mr_tgtdev_get(struct mpi3mr_tgt_dev *s)
+{
+ kref_get(&s->ref_count);
+}
+
+/**
+ * mpi3mr_free_tgtdev - target device memory dealloctor
+ * @r: k reference pointer of the target device
+ *
+ * Free target device memory when no reference.
+ */
+static inline void mpi3mr_free_tgtdev(struct kref *r)
+{
+ kfree(container_of(r, struct mpi3mr_tgt_dev, ref_count));
+}
+
+/**
+ * mpi3mr_tgtdev_put - k reference decrementor
+ * @s: Target device reference
+ *
+ * Decrement target device reference count.
+ */
+static inline void mpi3mr_tgtdev_put(struct mpi3mr_tgt_dev *s)
+{
+ kref_put(&s->ref_count, mpi3mr_free_tgtdev);
+}
+
+
+/**
+ * struct mpi3mr_stgt_priv_data - SCSI target private structure
+ *
+ * @starget: Scsi_target pointer
+ * @dev_handle: FW device handle
+ * @perst_id: FW assigned Persistent ID
+ * @num_luns: Number of Logical Units
+ * @block_io: I/O blocked to the device or not
+ * @dev_removed: Device removed in the Firmware
+ * @dev_removedelay: Device is waiting to be removed in FW
+ * @dev_type: Device type
+ * @tgt_dev: Internal target device pointer
+ */
+struct mpi3mr_stgt_priv_data {
+ struct scsi_target *starget;
+ u16 dev_handle;
+ u16 perst_id;
+ u32 num_luns;
+ atomic_t block_io;
+ u8 dev_removed;
+ u8 dev_removedelay;
+ u8 dev_type;
+ struct mpi3mr_tgt_dev *tgt_dev;
+};
+
+/**
+ * struct mpi3mr_stgt_priv_data - SCSI device private structure
+ *
+ * @tgt_priv_data: Scsi_target private data pointer
+ * @lun_id: LUN ID of the device
+ * @ncq_prio_enable: NCQ priority enable for SATA device
+ */
+struct mpi3mr_sdev_priv_data {
+ struct mpi3mr_stgt_priv_data *tgt_priv_data;
+ u32 lun_id;
+ u8 ncq_prio_enable;
+};
+
+/**
+ * struct mpi3mr_drv_cmd - Internal command tracker
+ *
+ * @mutex: Command mutex
+ * @done: Completeor for wakeup
+ * @reply: Firmware reply for internal commands
+ * @sensebuf: Sensebuf for SCSI IO commands
+ * @iou_rc: IO Unit control reason code
+ * @state: Command State
+ * @dev_handle: Firmware handle for device specific commands
+ * @ioc_status: IOC status from the firmware
+ * @ioc_loginfo:IOC log info from the firmware
+ * @is_waiting: Is the command issued in block mode
+ * @retry_count: Retry count for retriable commands
+ * @host_tag: Host tag used by the command
+ * @callback: Callback for non blocking commands
+ */
+struct mpi3mr_drv_cmd {
+ struct mutex mutex;
+ struct completion done;
+ void *reply;
+ u8 *sensebuf;
+ u8 iou_rc;
+ u16 state;
+ u16 dev_handle;
+ u16 ioc_status;
+ u32 ioc_loginfo;
+ u8 is_waiting;
+ u8 retry_count;
+ u16 host_tag;
+
+ void (*callback)(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd);
+};
+
+
+/**
+ * struct chain_element - memory descriptor structure to store
+ * virtual and dma addresses for chain elements.
+ *
+ * @addr: virtual address
+ * @dma_addr: dma address
+ */
+struct chain_element {
+ void *addr;
+ dma_addr_t dma_addr;
+};
+
+/**
+ * struct scmd_priv - SCSI command private data
+ *
+ * @host_tag: Host tag specific to operational queue
+ * @in_lld_scope: Command in LLD scope or not
+ * @meta_sg_valid: DIX command with meta data SGL or not
+ * @scmd: SCSI Command pointer
+ * @req_q_idx: Operational request queue index
+ * @chain_idx: Chain frame index
+ * @meta_chain_idx: Chain frame index of meta data SGL
+ * @mpi3mr_scsiio_req: MPI SCSI IO request
+ */
+struct scmd_priv {
+ u16 host_tag;
+ u8 in_lld_scope;
+ u8 meta_sg_valid;
+ struct scsi_cmnd *scmd;
+ u16 req_q_idx;
+ int chain_idx;
+ int meta_chain_idx;
+ u8 mpi3mr_scsiio_req[MPI3MR_ADMIN_REQ_FRAME_SZ];
+};
+
+/**
+ * struct mpi3mr_ioc - Adapter anchor structure stored in shost
+ * private data
+ *
+ * @list: List pointer
+ * @pdev: PCI device pointer
+ * @shost: Scsi_Host pointer
+ * @id: Controller ID
+ * @cpu_count: Number of online CPUs
+ * @irqpoll_sleep: usleep unit used in threaded isr irqpoll
+ * @name: Controller ASCII name
+ * @driver_name: Driver ASCII name
+ * @sysif_regs: System interface registers virtual address
+ * @sysif_regs_phys: System interface registers physical address
+ * @bars: PCI BARS
+ * @dma_mask: DMA mask
+ * @msix_count: Number of MSIX vectors used
+ * @intr_enabled: Is interrupts enabled
+ * @num_admin_req: Number of admin requests
+ * @admin_req_q_sz: Admin request queue size
+ * @admin_req_pi: Admin request queue producer index
+ * @admin_req_ci: Admin request queue consumer index
+ * @admin_req_base: Admin request queue base virtual address
+ * @admin_req_dma: Admin request queue base dma address
+ * @admin_req_lock: Admin queue access lock
+ * @num_admin_replies: Number of admin replies
+ * @admin_reply_q_sz: Admin reply queue size
+ * @admin_reply_ci: Admin reply queue consumer index
+ * @admin_reply_ephase:Admin reply queue expected phase
+ * @admin_reply_base: Admin reply queue base virtual address
+ * @admin_reply_dma: Admin reply queue base dma address
+ * @ready_timeout: Controller ready timeout
+ * @intr_info: Interrupt cookie pointer
+ * @intr_info_count: Number of interrupt cookies
+ * @num_queues: Number of operational queues
+ * @num_op_req_q: Number of operational request queues
+ * @req_qinfo: Operational request queue info pointer
+ * @num_op_reply_q: Number of operational reply queues
+ * @op_reply_qinfo: Operational reply queue info pointer
+ * @init_cmds: Command tracker for initialization commands
+ * @facts: Cached IOC facts data
+ * @op_reply_desc_sz: Operational reply descriptor size
+ * @num_reply_bufs: Number of reply buffers allocated
+ * @reply_buf_pool: Reply buffer pool
+ * @reply_buf: Reply buffer base virtual address
+ * @reply_buf_dma: Reply buffer DMA address
+ * @reply_buf_dma_max_address: Reply DMA address max limit
+ * @reply_free_qsz: Reply free queue size
+ * @reply_free_q_pool: Reply free queue pool
+ * @reply_free_q: Reply free queue base virtual address
+ * @reply_free_q_dma: Reply free queue base DMA address
+ * @reply_free_queue_lock: Reply free queue lock
+ * @reply_free_queue_host_index: Reply free queue host index
+ * @num_sense_bufs: Number of sense buffers
+ * @sense_buf_pool: Sense buffer pool
+ * @sense_buf: Sense buffer base virtual address
+ * @sense_buf_dma: Sense buffer base DMA address
+ * @sense_buf_q_sz: Sense buffer queue size
+ * @sense_buf_q_pool: Sense buffer queue pool
+ * @sense_buf_q: Sense buffer queue virtual address
+ * @sense_buf_q_dma: Sense buffer queue DMA address
+ * @sbq_lock: Sense buffer queue lock
+ * @sbq_host_index: Sense buffer queuehost index
+ * @event_masks: Event mask bitmap
+ * @fwevt_worker_name: Firmware event worker thread name
+ * @fwevt_worker_thread: Firmware event worker thread
+ * @fwevt_lock: Firmware event lock
+ * @fwevt_list: Firmware event list
+ * @watchdog_work_q_name: Fault watchdog worker thread name
+ * @watchdog_work_q: Fault watchdog worker thread
+ * @watchdog_work: Fault watchdog work
+ * @watchdog_lock: Fault watchdog lock
+ * @is_driver_loading: Is driver still loading
+ * @scan_started: Async scan started
+ * @scan_failed: Asycn scan failed
+ * @stop_drv_processing: Stop all command processing
+ * @max_host_ios: Maximum host I/O count
+ * @chain_buf_count: Chain buffer count
+ * @chain_buf_pool: Chain buffer pool
+ * @chain_sgl_list: Chain SGL list
+ * @chain_bitmap_sz: Chain buffer allocator bitmap size
+ * @chain_bitmap: Chain buffer allocator bitmap
+ * @chain_buf_lock: Chain buffer list lock
+ * @host_tm_cmds: Command tracker for task management commands
+ * @dev_rmhs_cmds: Command tracker for device removal commands
+ * @devrem_bitmap_sz: Device removal bitmap size
+ * @devrem_bitmap: Device removal bitmap
+ * @dev_handle_bitmap_sz: Device handle bitmap size
+ * @removepend_bitmap: Remove pending bitmap
+ * @delayed_rmhs_list: Delayed device removal list
+ * @ts_update_counter: Timestamp update counter
+ * @fault_dbg: Fault debug flag
+ * @reset_in_progress: Reset in progress flag
+ * @unrecoverable: Controller unrecoverable flag
+ * @reset_mutex: Controller reset mutex
+ * @reset_waitq: Controller reset wait queue
+ * @diagsave_timeout: Diagnostic information save timeout
+ * @logging_level: Controller debug logging level
+ * @flush_io_count: I/O count to flush after reset
+ * @current_event: Firmware event currently in process
+ * @driver_info: Driver, Kernel, OS information to firmware
+ * @change_count: Topology change count
+ * @op_reply_q_offset: Operational reply queue offset with MSIx
+ */
+struct mpi3mr_ioc {
+ struct list_head list;
+ struct pci_dev *pdev;
+ struct Scsi_Host *shost;
+ u8 id;
+ int cpu_count;
+ bool enable_segqueue;
+ u32 irqpoll_sleep;
+
+ char name[MPI3MR_NAME_LENGTH];
+ char driver_name[MPI3MR_NAME_LENGTH];
+
+ volatile struct mpi3_sysif_registers __iomem *sysif_regs;
+ resource_size_t sysif_regs_phys;
+ int bars;
+ u64 dma_mask;
+
+ u16 msix_count;
+ u8 intr_enabled;
+
+ u16 num_admin_req;
+ u32 admin_req_q_sz;
+ u16 admin_req_pi;
+ u16 admin_req_ci;
+ void *admin_req_base;
+ dma_addr_t admin_req_dma;
+ spinlock_t admin_req_lock;
+
+ u16 num_admin_replies;
+ u32 admin_reply_q_sz;
+ u16 admin_reply_ci;
+ u8 admin_reply_ephase;
+ void *admin_reply_base;
+ dma_addr_t admin_reply_dma;
+
+ u32 ready_timeout;
+
+ struct mpi3mr_intr_info *intr_info;
+ u16 intr_info_count;
+
+ u16 num_queues;
+ u16 num_op_req_q;
+ struct op_req_qinfo *req_qinfo;
+
+ u16 num_op_reply_q;
+ struct op_reply_qinfo *op_reply_qinfo;
+
+ struct mpi3mr_drv_cmd init_cmds;
+ struct mpi3mr_ioc_facts facts;
+ u16 op_reply_desc_sz;
+
+ u32 num_reply_bufs;
+ struct dma_pool *reply_buf_pool;
+ u8 *reply_buf;
+ dma_addr_t reply_buf_dma;
+ dma_addr_t reply_buf_dma_max_address;
+
+ u16 reply_free_qsz;
+ struct dma_pool *reply_free_q_pool;
+ __le64 *reply_free_q;
+ dma_addr_t reply_free_q_dma;
+ spinlock_t reply_free_queue_lock;
+ u32 reply_free_queue_host_index;
+
+ u32 num_sense_bufs;
+ struct dma_pool *sense_buf_pool;
+ u8 *sense_buf;
+ dma_addr_t sense_buf_dma;
+
+ u16 sense_buf_q_sz;
+ struct dma_pool *sense_buf_q_pool;
+ __le64 *sense_buf_q;
+ dma_addr_t sense_buf_q_dma;
+ spinlock_t sbq_lock;
+ u32 sbq_host_index;
+ u32 event_masks[MPI3_EVENT_NOTIFY_EVENTMASK_WORDS];
+
+ char fwevt_worker_name[MPI3MR_NAME_LENGTH];
+ struct workqueue_struct *fwevt_worker_thread;
+ spinlock_t fwevt_lock;
+ struct list_head fwevt_list;
+
+ char watchdog_work_q_name[20];
+ struct workqueue_struct *watchdog_work_q;
+ struct delayed_work watchdog_work;
+ spinlock_t watchdog_lock;
+
+ u8 is_driver_loading;
+ u8 scan_started;
+ u16 scan_failed;
+ u8 stop_drv_processing;
+
+ u16 max_host_ios;
+ spinlock_t tgtdev_lock;
+ struct list_head tgtdev_list;
+
+ u32 chain_buf_count;
+ struct dma_pool *chain_buf_pool;
+ struct chain_element *chain_sgl_list;
+ u16 chain_bitmap_sz;
+ void *chain_bitmap;
+ spinlock_t chain_buf_lock;
+
+ struct mpi3mr_drv_cmd host_tm_cmds;
+ struct mpi3mr_drv_cmd dev_rmhs_cmds[MPI3MR_NUM_DEVRMCMD];
+ u16 devrem_bitmap_sz;
+ void *devrem_bitmap;
+ u16 dev_handle_bitmap_sz;
+ void *removepend_bitmap;
+ struct list_head delayed_rmhs_list;
+
+ u32 ts_update_counter;
+ u8 fault_dbg;
+ u8 reset_in_progress;
+ u8 unrecoverable;
+ struct mutex reset_mutex;
+ wait_queue_head_t reset_waitq;
+
+ u16 diagsave_timeout;
+ int logging_level;
+ u16 flush_io_count;
+
+ struct mpi3mr_fwevt *current_event;
+ struct mpi3_driver_info_layout driver_info;
+ u16 change_count;
+ u16 op_reply_q_offset;
+};
+
+/**
+ * struct mpi3mr_fwevt - Firmware event structure.
+ *
+ * @list: list head
+ * @work: Work structure
+ * @mrioc: Adapter instance reference
+ * @event_id: MPI3 firmware event ID
+ * @send_ack: Event acknowledgment required or not
+ * @process_evt: Bottomhalf processing required or not
+ * @evt_ctx: Event context to send in Ack
+ * @ref_count: kref count
+ * @event_data: Actual MPI3 event data
+ */
+struct mpi3mr_fwevt {
+ struct list_head list;
+ struct work_struct work;
+ struct mpi3mr_ioc *mrioc;
+ u16 event_id;
+ bool send_ack;
+ bool process_evt;
+ u32 evt_ctx;
+ struct kref ref_count;
+ char event_data[0] __aligned(4);
+};
+
+
+/**
+ * struct delayed_dev_rmhs_node - Delayed device removal node
+ *
+ * @list: list head
+ * @handle: Device handle
+ * @iou_rc: IO Unit Control Reason Code
+ */
+struct delayed_dev_rmhs_node {
+ struct list_head list;
+ u16 handle;
+ u8 iou_rc;
+};
+
+int mpi3mr_setup_resources(struct mpi3mr_ioc *mrioc);
+void mpi3mr_cleanup_resources(struct mpi3mr_ioc *mrioc);
+int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc, u8 re_init);
+void mpi3mr_cleanup_ioc(struct mpi3mr_ioc *mrioc, u8 re_init);
+int mpi3mr_issue_port_enable(struct mpi3mr_ioc *mrioc, u8 async);
+int mpi3mr_admin_request_post(struct mpi3mr_ioc *mrioc, void *admin_req,
+u16 admin_req_sz, u8 ignore_reset);
+int mpi3mr_op_request_post(struct mpi3mr_ioc *mrioc,
+ struct op_req_qinfo *opreqq, u8 *req);
+void mpi3mr_add_sg_single(void *paddr, u8 flags, u32 length,
+ dma_addr_t dma_addr);
+void mpi3mr_build_zero_len_sge(void *paddr);
+void *mpi3mr_get_sensebuf_virt_addr(struct mpi3mr_ioc *mrioc,
+ dma_addr_t phys_addr);
+void *mpi3mr_get_reply_virt_addr(struct mpi3mr_ioc *mrioc,
+ dma_addr_t phys_addr);
+void mpi3mr_repost_sense_buf(struct mpi3mr_ioc *mrioc,
+ u64 sense_buf_dma);
+
+void mpi3mr_os_handle_events(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply);
+void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc,
+ struct mpi3_default_reply_descriptor *reply_desc,
+ u64 *reply_dma, u16 qidx);
+void mpi3mr_start_watchdog(struct mpi3mr_ioc *mrioc);
+void mpi3mr_stop_watchdog(struct mpi3mr_ioc *mrioc);
+
+int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc,
+ u32 reset_reason, u8 snapdump);
+int mpi3mr_diagfault_reset_handler(struct mpi3mr_ioc *mrioc,
+ u32 reset_reason);
+void mpi3mr_ioc_disable_intr(struct mpi3mr_ioc *mrioc);
+void mpi3mr_ioc_enable_intr(struct mpi3mr_ioc *mrioc);
+
+enum mpi3mr_iocstate mpi3mr_get_iocstate(struct mpi3mr_ioc *mrioc);
+int mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event,
+ u32 event_ctx);
+
+void mpi3mr_wait_for_host_io(struct mpi3mr_ioc *mrioc, u32 timeout);
+void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc);
+void mpi3mr_flush_host_io(struct mpi3mr_ioc *mrioc);
+void mpi3mr_invalidate_devhandles(struct mpi3mr_ioc *mrioc);
+void mpi3mr_rfresh_tgtdevs(struct mpi3mr_ioc *mrioc);
+void mpi3mr_flush_delayed_rmhs_list(struct mpi3mr_ioc *mrioc);
+
+#endif /*MPI3MR_H_INCLUDED*/
diff --git a/drivers/scsi/mpi3mr/mpi3mr_debug.h b/drivers/scsi/mpi3mr/mpi3mr_debug.h
new file mode 100644
index 000000000000..c085bb048d41
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi3mr_debug.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Driver for Broadcom MPI3 Storage Controllers
+ *
+ * Copyright (C) 2017-2021 Broadcom Inc.
+ * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
+ *
+ */
+
+#ifndef MPI3SAS_DEBUG_H_INCLUDED
+
+#define MPI3SAS_DEBUG_H_INCLUDED
+
+/*
+ * debug levels
+ */
+#define MPI3_DEBUG 0x00000001
+#define MPI3_DEBUG_MSG_FRAME 0x00000002
+#define MPI3_DEBUG_SG 0x00000004
+#define MPI3_DEBUG_EVENTS 0x00000008
+#define MPI3_DEBUG_EVENT_WORK_TASK 0x00000010
+#define MPI3_DEBUG_INIT 0x00000020
+#define MPI3_DEBUG_EXIT 0x00000040
+#define MPI3_DEBUG_FAIL 0x00000080
+#define MPI3_DEBUG_TM 0x00000100
+#define MPI3_DEBUG_REPLY 0x00000200
+#define MPI3_DEBUG_HANDSHAKE 0x00000400
+#define MPI3_DEBUG_CONFIG 0x00000800
+#define MPI3_DEBUG_DL 0x00001000
+#define MPI3_DEBUG_RESET 0x00002000
+#define MPI3_DEBUG_SCSI 0x00004000
+#define MPI3_DEBUG_IOCTL 0x00008000
+#define MPI3_DEBUG_CSMISAS 0x00010000
+#define MPI3_DEBUG_SAS 0x00020000
+#define MPI3_DEBUG_TRANSPORT 0x00040000
+#define MPI3_DEBUG_TASK_SET_FULL 0x00080000
+#define MPI3_DEBUG_TRIGGER_DIAG 0x00200000
+
+
+/*
+ * debug macros
+ */
+
+#define ioc_err(ioc, fmt, ...) \
+ pr_err("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
+#define ioc_notice(ioc, fmt, ...) \
+ pr_notice("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
+#define ioc_warn(ioc, fmt, ...) \
+ pr_warn("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
+#define ioc_info(ioc, fmt, ...) \
+ pr_info("%s: " fmt, (ioc)->name, ##__VA_ARGS__)
+
+
+#define dbgprint(IOC, FMT, ...) \
+ do { \
+ if (IOC->logging_level & MPI3_DEBUG) \
+ pr_info("%s: " FMT, (IOC)->name, ##__VA_ARGS__); \
+ } while (0)
+
+#endif /* MPT3SAS_DEBUG_H_INCLUDED */
diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
new file mode 100644
index 000000000000..acb2be62080a
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
@@ -0,0 +1,3956 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Broadcom MPI3 Storage Controllers
+ *
+ * Copyright (C) 2017-2021 Broadcom Inc.
+ * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
+ *
+ */
+
+#include "mpi3mr.h"
+#include <linux/io-64-nonatomic-lo-hi.h>
+extern int prot_mask;
+
+#if defined(writeq) && defined(CONFIG_64BIT)
+static inline void mpi3mr_writeq(__u64 b, volatile void __iomem *addr)
+{
+ writeq(b, addr);
+}
+#else
+static inline void mpi3mr_writeq(__u64 b, volatile void __iomem *addr)
+{
+ __u64 data_out = b;
+
+ writel((u32)(data_out), addr);
+ writel((u32)(data_out >> 32), (addr + 4));
+}
+#endif
+
+static inline bool
+mpi3mr_check_req_qfull(struct op_req_qinfo *op_req_q)
+{
+ u16 pi, ci, max_entries;
+ bool is_qfull = false;
+
+ pi = op_req_q->pi;
+ ci = READ_ONCE(op_req_q->ci);
+ max_entries = op_req_q->num_requests;
+
+ if ((ci == (pi + 1)) || ((!ci) && (pi == (max_entries - 1))))
+ is_qfull = true;
+
+ return is_qfull;
+}
+
+static void mpi3mr_sync_irqs(struct mpi3mr_ioc *mrioc)
+{
+ u16 i, max_vectors;
+
+ max_vectors = mrioc->intr_info_count;
+
+ for (i = 0; i < max_vectors; i++)
+ synchronize_irq(pci_irq_vector(mrioc->pdev, i));
+}
+
+void mpi3mr_ioc_disable_intr(struct mpi3mr_ioc *mrioc)
+{
+ mrioc->intr_enabled = 0;
+ mpi3mr_sync_irqs(mrioc);
+}
+
+void mpi3mr_ioc_enable_intr(struct mpi3mr_ioc *mrioc)
+{
+ mrioc->intr_enabled = 1;
+}
+
+static void mpi3mr_cleanup_isr(struct mpi3mr_ioc *mrioc)
+{
+ u16 i;
+
+ mpi3mr_ioc_disable_intr(mrioc);
+
+ if (!mrioc->intr_info)
+ return;
+
+ for (i = 0; i < mrioc->intr_info_count; i++)
+ free_irq(pci_irq_vector(mrioc->pdev, i),
+ (mrioc->intr_info + i));
+
+ kfree(mrioc->intr_info);
+ mrioc->intr_info = NULL;
+ mrioc->intr_info_count = 0;
+ pci_free_irq_vectors(mrioc->pdev);
+}
+
+void mpi3mr_add_sg_single(void *paddr, u8 flags, u32 length,
+ dma_addr_t dma_addr)
+{
+ struct mpi3_sge_common *sgel = paddr;
+
+ sgel->flags = flags;
+ sgel->length = cpu_to_le32(length);
+ sgel->address = cpu_to_le64(dma_addr);
+}
+
+void mpi3mr_build_zero_len_sge(void *paddr)
+{
+ u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
+
+ mpi3mr_add_sg_single(paddr, sgl_flags, 0, -1);
+}
+
+void *mpi3mr_get_reply_virt_addr(struct mpi3mr_ioc *mrioc,
+ dma_addr_t phys_addr)
+{
+ if (!phys_addr)
+ return NULL;
+
+ if ((phys_addr < mrioc->reply_buf_dma) ||
+ (phys_addr > mrioc->reply_buf_dma_max_address))
+ return NULL;
+
+ return mrioc->reply_buf + (phys_addr - mrioc->reply_buf_dma);
+}
+
+void *mpi3mr_get_sensebuf_virt_addr(struct mpi3mr_ioc *mrioc,
+ dma_addr_t phys_addr)
+{
+ if (!phys_addr)
+ return NULL;
+
+ return mrioc->sense_buf + (phys_addr - mrioc->sense_buf_dma);
+}
+
+static void mpi3mr_repost_reply_buf(struct mpi3mr_ioc *mrioc,
+ u64 reply_dma)
+{
+ u32 old_idx = 0;
+
+ spin_lock(&mrioc->reply_free_queue_lock);
+ old_idx = mrioc->reply_free_queue_host_index;
+ mrioc->reply_free_queue_host_index = (
+ (mrioc->reply_free_queue_host_index ==
+ (mrioc->reply_free_qsz - 1)) ? 0 :
+ (mrioc->reply_free_queue_host_index + 1));
+ mrioc->reply_free_q[old_idx] = cpu_to_le64(reply_dma);
+ writel(mrioc->reply_free_queue_host_index,
+ &mrioc->sysif_regs->reply_free_host_index);
+ spin_unlock(&mrioc->reply_free_queue_lock);
+}
+
+void mpi3mr_repost_sense_buf(struct mpi3mr_ioc *mrioc,
+ u64 sense_buf_dma)
+{
+ u32 old_idx = 0;
+
+ spin_lock(&mrioc->sbq_lock);
+ old_idx = mrioc->sbq_host_index;
+ mrioc->sbq_host_index = ((mrioc->sbq_host_index ==
+ (mrioc->sense_buf_q_sz - 1)) ? 0 :
+ (mrioc->sbq_host_index + 1));
+ mrioc->sense_buf_q[old_idx] = cpu_to_le64(sense_buf_dma);
+ writel(mrioc->sbq_host_index,
+ &mrioc->sysif_regs->sense_buffer_free_host_index);
+ spin_unlock(&mrioc->sbq_lock);
+}
+
+static void mpi3mr_print_event_data(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ char *desc = NULL;
+ u16 event;
+
+ event = event_reply->event;
+
+ switch (event) {
+ case MPI3_EVENT_LOG_DATA:
+ desc = "Log Data";
+ break;
+ case MPI3_EVENT_CHANGE:
+ desc = "Event Change";
+ break;
+ case MPI3_EVENT_GPIO_INTERRUPT:
+ desc = "GPIO Interrupt";
+ break;
+ case MPI3_EVENT_TEMP_THRESHOLD:
+ desc = "Temperature Threshold";
+ break;
+ case MPI3_EVENT_CABLE_MGMT:
+ desc = "Cable Management";
+ break;
+ case MPI3_EVENT_ENERGY_PACK_CHANGE:
+ desc = "Energy Pack Change";
+ break;
+ case MPI3_EVENT_DEVICE_ADDED:
+ {
+ struct mpi3_device_page0 *event_data =
+ (struct mpi3_device_page0 *)event_reply->event_data;
+ ioc_info(mrioc, "Device Added: dev=0x%04x Form=0x%x\n",
+ event_data->dev_handle, event_data->device_form);
+ return;
+ }
+ case MPI3_EVENT_DEVICE_INFO_CHANGED:
+ {
+ struct mpi3_device_page0 *event_data =
+ (struct mpi3_device_page0 *)event_reply->event_data;
+ ioc_info(mrioc, "Device Info Changed: dev=0x%04x Form=0x%x\n",
+ event_data->dev_handle, event_data->device_form);
+ return;
+ }
+ case MPI3_EVENT_DEVICE_STATUS_CHANGE:
+ {
+ struct mpi3_event_data_device_status_change *event_data =
+ (struct mpi3_event_data_device_status_change *)event_reply->event_data;
+ ioc_info(mrioc, "Device status Change: dev=0x%04x RC=0x%x\n",
+ event_data->dev_handle, event_data->reason_code);
+ return;
+ }
+ case MPI3_EVENT_SAS_DISCOVERY:
+ {
+ struct mpi3_event_data_sas_discovery *event_data =
+ (struct mpi3_event_data_sas_discovery *)event_reply->event_data;
+ ioc_info(mrioc, "SAS Discovery: (%s) status (0x%08x)\n",
+ (event_data->reason_code == MPI3_EVENT_SAS_DISC_RC_STARTED) ?
+ "start" : "stop",
+ le32_to_cpu(event_data->discovery_status));
+ return;
+ }
+ case MPI3_EVENT_SAS_BROADCAST_PRIMITIVE:
+ desc = "SAS Broadcast Primitive";
+ break;
+ case MPI3_EVENT_SAS_NOTIFY_PRIMITIVE:
+ desc = "SAS Notify Primitive";
+ break;
+ case MPI3_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE:
+ desc = "SAS Init Device Status Change";
+ break;
+ case MPI3_EVENT_SAS_INIT_TABLE_OVERFLOW:
+ desc = "SAS Init Table Overflow";
+ break;
+ case MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ desc = "SAS Topology Change List";
+ break;
+ case MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE:
+ desc = "Enclosure Device Status Change";
+ break;
+ case MPI3_EVENT_HARD_RESET_RECEIVED:
+ desc = "Hard Reset Received";
+ break;
+ case MPI3_EVENT_SAS_PHY_COUNTER:
+ desc = "SAS PHY Counter";
+ break;
+ case MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR:
+ desc = "SAS Device Discovery Error";
+ break;
+ case MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
+ desc = "PCIE Topology Change List";
+ break;
+ case MPI3_EVENT_PCIE_ENUMERATION:
+ {
+ struct mpi3_event_data_pcie_enumeration *event_data =
+ (struct mpi3_event_data_pcie_enumeration *)event_reply->event_data;
+ ioc_info(mrioc, "PCIE Enumeration: (%s)",
+ (event_data->reason_code ==
+ MPI3_EVENT_PCIE_ENUM_RC_STARTED) ? "start" : "stop");
+ if (event_data->enumeration_status)
+ ioc_info(mrioc, "enumeration_status(0x%08x)\n",
+ le32_to_cpu(event_data->enumeration_status));
+ return;
+ }
+ case MPI3_EVENT_PREPARE_FOR_RESET:
+ desc = "Prepare For Reset";
+ break;
+ }
+
+ if (!desc)
+ return;
+
+ ioc_info(mrioc, "%s\n", desc);
+}
+
+static void mpi3mr_handle_events(struct mpi3mr_ioc *mrioc,
+ struct mpi3_default_reply *def_reply)
+{
+ struct mpi3_event_notification_reply *event_reply =
+ (struct mpi3_event_notification_reply *)def_reply;
+
+ mrioc->change_count = le16_to_cpu(event_reply->ioc_change_count);
+ mpi3mr_print_event_data(mrioc, event_reply);
+ mpi3mr_os_handle_events(mrioc, event_reply);
+}
+
+static struct mpi3mr_drv_cmd *
+mpi3mr_get_drv_cmd(struct mpi3mr_ioc *mrioc, u16 host_tag,
+ struct mpi3_default_reply *def_reply)
+{
+ u16 idx;
+
+ switch (host_tag) {
+ case MPI3MR_HOSTTAG_INITCMDS:
+ return &mrioc->init_cmds;
+ case MPI3MR_HOSTTAG_BLK_TMS:
+ return &mrioc->host_tm_cmds;
+ case MPI3MR_HOSTTAG_INVALID:
+ if (def_reply && def_reply->function ==
+ MPI3_FUNCTION_EVENT_NOTIFICATION)
+ mpi3mr_handle_events(mrioc, def_reply);
+ return NULL;
+ default:
+ break;
+ }
+ if (host_tag >= MPI3MR_HOSTTAG_DEVRMCMD_MIN &&
+ host_tag <= MPI3MR_HOSTTAG_DEVRMCMD_MAX) {
+ idx = host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN;
+ return &mrioc->dev_rmhs_cmds[idx];
+ }
+
+ return NULL;
+}
+
+static void mpi3mr_process_admin_reply_desc(struct mpi3mr_ioc *mrioc,
+ struct mpi3_default_reply_descriptor *reply_desc, u64 *reply_dma)
+{
+ u16 reply_desc_type, host_tag = 0;
+ u16 ioc_status = MPI3_IOCSTATUS_SUCCESS;
+ u32 ioc_loginfo = 0;
+ struct mpi3_status_reply_descriptor *status_desc;
+ struct mpi3_address_reply_descriptor *addr_desc;
+ struct mpi3_success_reply_descriptor *success_desc;
+ struct mpi3_default_reply *def_reply = NULL;
+ struct mpi3mr_drv_cmd *cmdptr = NULL;
+ struct mpi3_scsi_io_reply *scsi_reply;
+ u8 *sense_buf = NULL;
+
+ *reply_dma = 0;
+ reply_desc_type = le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_TYPE_MASK;
+ switch (reply_desc_type) {
+ case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_STATUS:
+ status_desc = (struct mpi3_status_reply_descriptor *)reply_desc;
+ host_tag = le16_to_cpu(status_desc->host_tag);
+ ioc_status = le16_to_cpu(status_desc->ioc_status);
+ if (ioc_status &
+ MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL)
+ ioc_loginfo = le32_to_cpu(status_desc->ioc_log_info);
+ ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK;
+ break;
+ case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_ADDRESS_REPLY:
+ addr_desc = (struct mpi3_address_reply_descriptor *)reply_desc;
+ *reply_dma = le64_to_cpu(addr_desc->reply_frame_address);
+ def_reply = mpi3mr_get_reply_virt_addr(mrioc, *reply_dma);
+ if (!def_reply)
+ goto out;
+ host_tag = le16_to_cpu(def_reply->host_tag);
+ ioc_status = le16_to_cpu(def_reply->ioc_status);
+ if (ioc_status &
+ MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL)
+ ioc_loginfo = le32_to_cpu(def_reply->ioc_log_info);
+ ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK;
+ if (def_reply->function == MPI3_FUNCTION_SCSI_IO) {
+ scsi_reply = (struct mpi3_scsi_io_reply *)def_reply;
+ sense_buf = mpi3mr_get_sensebuf_virt_addr(mrioc,
+ le64_to_cpu(scsi_reply->sense_data_buffer_address));
+ }
+ break;
+ case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SUCCESS:
+ success_desc = (struct mpi3_success_reply_descriptor *)reply_desc;
+ host_tag = le16_to_cpu(success_desc->host_tag);
+ break;
+ default:
+ break;
+ }
+
+ cmdptr = mpi3mr_get_drv_cmd(mrioc, host_tag, def_reply);
+ if (cmdptr) {
+ if (cmdptr->state & MPI3MR_CMD_PENDING) {
+ cmdptr->state |= MPI3MR_CMD_COMPLETE;
+ cmdptr->ioc_loginfo = ioc_loginfo;
+ cmdptr->ioc_status = ioc_status;
+ cmdptr->state &= ~MPI3MR_CMD_PENDING;
+ if (def_reply) {
+ cmdptr->state |= MPI3MR_CMD_REPLY_VALID;
+ memcpy((u8 *)cmdptr->reply, (u8 *)def_reply,
+ mrioc->facts.reply_sz);
+ }
+ if (cmdptr->is_waiting) {
+ complete(&cmdptr->done);
+ cmdptr->is_waiting = 0;
+ } else if (cmdptr->callback)
+ cmdptr->callback(mrioc, cmdptr);
+ }
+ }
+out:
+ if (sense_buf)
+ mpi3mr_repost_sense_buf(mrioc,
+ le64_to_cpu(scsi_reply->sense_data_buffer_address));
+}
+
+static int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc)
+{
+ u32 exp_phase = mrioc->admin_reply_ephase;
+ u32 admin_reply_ci = mrioc->admin_reply_ci;
+ u32 num_admin_replies = 0;
+ u64 reply_dma = 0;
+ struct mpi3_default_reply_descriptor *reply_desc;
+
+ reply_desc = (struct mpi3_default_reply_descriptor *)mrioc->admin_reply_base +
+ admin_reply_ci;
+
+ if ((le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase)
+ return 0;
+
+ do {
+ mrioc->admin_req_ci = le16_to_cpu(reply_desc->request_queue_ci);
+ mpi3mr_process_admin_reply_desc(mrioc, reply_desc, &reply_dma);
+ if (reply_dma)
+ mpi3mr_repost_reply_buf(mrioc, reply_dma);
+ num_admin_replies++;
+ if (++admin_reply_ci == mrioc->num_admin_replies) {
+ admin_reply_ci = 0;
+ exp_phase ^= 1;
+ }
+ reply_desc =
+ (struct mpi3_default_reply_descriptor *)mrioc->admin_reply_base +
+ admin_reply_ci;
+ if ((le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase)
+ break;
+ } while (1);
+
+ writel(admin_reply_ci, &mrioc->sysif_regs->admin_reply_queue_ci);
+ mrioc->admin_reply_ci = admin_reply_ci;
+ mrioc->admin_reply_ephase = exp_phase;
+
+ return num_admin_replies;
+}
+
+/**
+ * mpi3mr_get_reply_desc - get reply descriptor frame corresponding to
+ * queue's consumer index from operational reply descriptor queue.
+ * @op_reply_q: op_reply_qinfo object
+ * @reply_ci: operational reply descriptor's queue consumer index
+ *
+ * Returns reply descriptor frame address
+ */
+static inline struct mpi3_default_reply_descriptor *
+mpi3mr_get_reply_desc(struct op_reply_qinfo *op_reply_q, u32 reply_ci)
+{
+ void *segment_base_addr;
+ struct segments *segments = op_reply_q->q_segments;
+ struct mpi3_default_reply_descriptor *reply_desc = NULL;
+
+ segment_base_addr =
+ segments[reply_ci / op_reply_q->segment_qd].segment;
+ reply_desc = (struct mpi3_default_reply_descriptor *)segment_base_addr +
+ (reply_ci % op_reply_q->segment_qd);
+ return reply_desc;
+}
+
+static int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_intr_info *intr_info)
+{
+ struct op_reply_qinfo *op_reply_q = intr_info->op_reply_q;
+ struct op_req_qinfo *op_req_q;
+ u32 exp_phase;
+ u32 reply_ci;
+ u32 num_op_reply = 0;
+ u64 reply_dma = 0;
+ struct mpi3_default_reply_descriptor *reply_desc;
+ u16 req_q_idx = 0, reply_qidx;
+
+ reply_qidx = op_reply_q->qid - 1;
+
+ if (!atomic_add_unless(&op_reply_q->in_use, 1, 1))
+ return 0;
+
+ exp_phase = op_reply_q->ephase;
+ reply_ci = op_reply_q->ci;
+
+ reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci);
+ if ((le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) {
+ atomic_dec(&op_reply_q->in_use);
+ return 0;
+ }
+
+ do {
+ req_q_idx = le16_to_cpu(reply_desc->request_queue_id) - 1;
+ op_req_q = &mrioc->req_qinfo[req_q_idx];
+
+ WRITE_ONCE(op_req_q->ci, le16_to_cpu(reply_desc->request_queue_ci));
+ mpi3mr_process_op_reply_desc(mrioc, reply_desc, &reply_dma,
+ reply_qidx);
+ atomic_dec(&op_reply_q->pend_ios);
+ if (reply_dma)
+ mpi3mr_repost_reply_buf(mrioc, reply_dma);
+ num_op_reply++;
+
+ if (++reply_ci == op_reply_q->num_replies) {
+ reply_ci = 0;
+ exp_phase ^= 1;
+ }
+
+ reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci);
+
+ if ((le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase)
+ break;
+ /*
+ * Exit completion loop to avoid CPU lockup
+ * Ensure remaining completion happens from threaded ISR.
+ */
+ if (num_op_reply > mrioc->max_host_ios) {
+ intr_info->op_reply_q->enable_irq_poll = true;
+ break;
+ }
+
+ } while (1);
+
+ writel(reply_ci,
+ &mrioc->sysif_regs->oper_queue_indexes[reply_qidx].consumer_index);
+ op_reply_q->ci = reply_ci;
+ op_reply_q->ephase = exp_phase;
+
+ atomic_dec(&op_reply_q->in_use);
+ return num_op_reply;
+}
+
+static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata)
+{
+ struct mpi3mr_intr_info *intr_info = privdata;
+ struct mpi3mr_ioc *mrioc;
+ u16 midx;
+ u32 num_admin_replies = 0, num_op_reply = 0;
+
+ if (!intr_info)
+ return IRQ_NONE;
+
+ mrioc = intr_info->mrioc;
+
+ if (!mrioc->intr_enabled)
+ return IRQ_NONE;
+
+ midx = intr_info->msix_index;
+
+ if (!midx)
+ num_admin_replies = mpi3mr_process_admin_reply_q(mrioc);
+ if (intr_info->op_reply_q)
+ num_op_reply = mpi3mr_process_op_reply_q(mrioc, intr_info);
+
+ if (num_admin_replies || num_op_reply)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static irqreturn_t mpi3mr_isr(int irq, void *privdata)
+{
+ struct mpi3mr_intr_info *intr_info = privdata;
+ struct mpi3mr_ioc *mrioc;
+ u16 midx;
+ int ret;
+
+ if (!intr_info)
+ return IRQ_NONE;
+
+ mrioc = intr_info->mrioc;
+ midx = intr_info->msix_index;
+ /* Call primary ISR routine */
+ ret = mpi3mr_isr_primary(irq, privdata);
+
+ /*
+ * If more IOs are expected, schedule IRQ polling thread.
+ * Otherwise exit from ISR.
+ */
+ if (!intr_info->op_reply_q)
+ return ret;
+
+ if (!intr_info->op_reply_q->enable_irq_poll ||
+ !atomic_read(&intr_info->op_reply_q->pend_ios))
+ return ret;
+
+ disable_irq_nosync(pci_irq_vector(mrioc->pdev, midx));
+
+ return IRQ_WAKE_THREAD;
+}
+
+/**
+ * mpi3mr_isr_poll - Reply queue polling routine
+ * @irq: IRQ
+ * @privdata: Interrupt info
+ *
+ * poll for pending I/O completions in a loop until pending I/Os
+ * present or controller queue depth I/Os are processed.
+ *
+ * Return: IRQ_NONE or IRQ_HANDLED
+ */
+static irqreturn_t mpi3mr_isr_poll(int irq, void *privdata)
+{
+ struct mpi3mr_intr_info *intr_info = privdata;
+ struct mpi3mr_ioc *mrioc;
+ u16 midx;
+ u32 num_op_reply = 0;
+
+ if (!intr_info || !intr_info->op_reply_q)
+ return IRQ_NONE;
+
+ mrioc = intr_info->mrioc;
+ midx = intr_info->msix_index;
+
+ /* Poll for pending IOs completions */
+ do {
+ if (!mrioc->intr_enabled)
+ break;
+
+ if (!midx)
+ mpi3mr_process_admin_reply_q(mrioc);
+ if (intr_info->op_reply_q)
+ num_op_reply +=
+ mpi3mr_process_op_reply_q(mrioc, intr_info);
+
+ usleep_range(mrioc->irqpoll_sleep, 10 * mrioc->irqpoll_sleep);
+
+ } while (atomic_read(&intr_info->op_reply_q->pend_ios) &&
+ (num_op_reply < mrioc->max_host_ios));
+
+ intr_info->op_reply_q->enable_irq_poll = false;
+ enable_irq(pci_irq_vector(mrioc->pdev, midx));
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * mpi3mr_request_irq - Request IRQ and register ISR
+ * @mrioc: Adapter instance reference
+ * @index: IRQ vector index
+ *
+ * Request threaded ISR with primary ISR and secondary
+ *
+ * Return: 0 on success and non zero on failures.
+ */
+static inline int mpi3mr_request_irq(struct mpi3mr_ioc *mrioc, u16 index)
+{
+ struct pci_dev *pdev = mrioc->pdev;
+ struct mpi3mr_intr_info *intr_info = mrioc->intr_info + index;
+ int retval = 0;
+
+ intr_info->mrioc = mrioc;
+ intr_info->msix_index = index;
+ intr_info->op_reply_q = NULL;
+
+ snprintf(intr_info->name, MPI3MR_NAME_LENGTH, "%s%d-msix%d",
+ mrioc->driver_name, mrioc->id, index);
+
+ retval = request_threaded_irq(pci_irq_vector(pdev, index), mpi3mr_isr,
+ mpi3mr_isr_poll, IRQF_SHARED, intr_info->name, intr_info);
+ if (retval) {
+ ioc_err(mrioc, "%s: Unable to allocate interrupt %d!\n",
+ intr_info->name, pci_irq_vector(pdev, index));
+ return retval;
+ }
+
+ return retval;
+}
+
+/**
+ * mpi3mr_setup_isr - Setup ISR for the controller
+ * @mrioc: Adapter instance reference
+ * @setup_one: Request one IRQ or more
+ *
+ * Allocate IRQ vectors and call mpi3mr_request_irq to setup ISR
+ *
+ * Return: 0 on success and non zero on failures.
+ */
+static int mpi3mr_setup_isr(struct mpi3mr_ioc *mrioc, u8 setup_one)
+{
+ unsigned int irq_flags = PCI_IRQ_MSIX;
+ u16 max_vectors = 0, i;
+ int retval = 0;
+ struct irq_affinity desc = { .pre_vectors = 1};
+
+ mpi3mr_cleanup_isr(mrioc);
+
+ if (setup_one || reset_devices)
+ max_vectors = 1;
+ else {
+ max_vectors =
+ min_t(int, mrioc->cpu_count + 1, mrioc->msix_count);
+
+ ioc_info(mrioc,
+ "MSI-X vectors supported: %d, no of cores: %d,",
+ mrioc->msix_count, mrioc->cpu_count);
+ ioc_info(mrioc,
+ "MSI-x vectors requested: %d\n", max_vectors);
+ }
+
+ irq_flags |= PCI_IRQ_AFFINITY | PCI_IRQ_ALL_TYPES;
+
+ mrioc->op_reply_q_offset = (max_vectors > 1) ? 1 : 0;
+ i = pci_alloc_irq_vectors_affinity(mrioc->pdev,
+ 1, max_vectors, irq_flags, &desc);
+ if (i <= 0) {
+ ioc_err(mrioc, "Cannot alloc irq vectors\n");
+ goto out_failed;
+ }
+ if (i != max_vectors) {
+ ioc_info(mrioc,
+ "allocated vectors (%d) are less than configured (%d)\n",
+ i, max_vectors);
+ /*
+ * If only one MSI-x is allocated, then MSI-x 0 will be shared
+ * between Admin queue and operational queue
+ */
+ if (i == 1)
+ mrioc->op_reply_q_offset = 0;
+
+ max_vectors = i;
+ }
+ mrioc->intr_info = kzalloc(sizeof(struct mpi3mr_intr_info) * max_vectors,
+ GFP_KERNEL);
+ if (!mrioc->intr_info) {
+ retval = -1;
+ pci_free_irq_vectors(mrioc->pdev);
+ goto out_failed;
+ }
+ for (i = 0; i < max_vectors; i++) {
+ retval = mpi3mr_request_irq(mrioc, i);
+ if (retval) {
+ mrioc->intr_info_count = i;
+ goto out_failed;
+ }
+ }
+ mrioc->intr_info_count = max_vectors;
+ mpi3mr_ioc_enable_intr(mrioc);
+ return retval;
+out_failed:
+ mpi3mr_cleanup_isr(mrioc);
+
+ return retval;
+}
+
+static const struct {
+ enum mpi3mr_iocstate value;
+ char *name;
+} mrioc_states[] = {
+ { MRIOC_STATE_READY, "ready" },
+ { MRIOC_STATE_FAULT, "fault" },
+ { MRIOC_STATE_RESET, "reset" },
+ { MRIOC_STATE_BECOMING_READY, "becoming ready" },
+ { MRIOC_STATE_RESET_REQUESTED, "reset requested" },
+ { MRIOC_STATE_UNRECOVERABLE, "unrecoverable error" },
+};
+
+static const char *mpi3mr_iocstate_name(enum mpi3mr_iocstate mrioc_state)
+{
+ int i;
+ char *name = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(mrioc_states); i++) {
+ if (mrioc_states[i].value == mrioc_state) {
+ name = mrioc_states[i].name;
+ break;
+ }
+ }
+ return name;
+}
+
+/* Reset reason to name mapper structure*/
+static const struct {
+ enum mpi3mr_reset_reason value;
+ char *name;
+} mpi3mr_reset_reason_codes[] = {
+ { MPI3MR_RESET_FROM_BRINGUP, "timeout in bringup" },
+ { MPI3MR_RESET_FROM_FAULT_WATCH, "fault" },
+ { MPI3MR_RESET_FROM_IOCTL, "application invocation" },
+ { MPI3MR_RESET_FROM_EH_HOS, "error handling" },
+ { MPI3MR_RESET_FROM_TM_TIMEOUT, "TM timeout" },
+ { MPI3MR_RESET_FROM_IOCTL_TIMEOUT, "IOCTL timeout" },
+ { MPI3MR_RESET_FROM_MUR_FAILURE, "MUR failure" },
+ { MPI3MR_RESET_FROM_CTLR_CLEANUP, "timeout in controller cleanup" },
+ { MPI3MR_RESET_FROM_CIACTIV_FAULT, "component image activation fault" },
+ { MPI3MR_RESET_FROM_PE_TIMEOUT, "port enable timeout" },
+ { MPI3MR_RESET_FROM_TSU_TIMEOUT, "time stamp update timeout" },
+ { MPI3MR_RESET_FROM_DELREQQ_TIMEOUT, "delete request queue timeout" },
+ { MPI3MR_RESET_FROM_DELREPQ_TIMEOUT, "delete reply queue timeout" },
+ {
+ MPI3MR_RESET_FROM_CREATEREPQ_TIMEOUT,
+ "create request queue timeout"
+ },
+ {
+ MPI3MR_RESET_FROM_CREATEREQQ_TIMEOUT,
+ "create reply queue timeout"
+ },
+ { MPI3MR_RESET_FROM_IOCFACTS_TIMEOUT, "IOC facts timeout" },
+ { MPI3MR_RESET_FROM_IOCINIT_TIMEOUT, "IOC init timeout" },
+ { MPI3MR_RESET_FROM_EVTNOTIFY_TIMEOUT, "event notify timeout" },
+ { MPI3MR_RESET_FROM_EVTACK_TIMEOUT, "event acknowledgment timeout" },
+ {
+ MPI3MR_RESET_FROM_CIACTVRST_TIMER,
+ "component image activation timeout"
+ },
+ {
+ MPI3MR_RESET_FROM_GETPKGVER_TIMEOUT,
+ "get package version timeout"
+ },
+ { MPI3MR_RESET_FROM_SYSFS, "sysfs invocation" },
+ { MPI3MR_RESET_FROM_SYSFS_TIMEOUT, "sysfs TM timeout" },
+};
+
+/**
+ * mpi3mr_reset_rc_name - get reset reason code name
+ * @reason_code: reset reason code value
+ *
+ * Map reset reason to an NULL terminated ASCII string
+ *
+ * Return: name corresponding to reset reason value or NULL.
+ */
+static const char *mpi3mr_reset_rc_name(enum mpi3mr_reset_reason reason_code)
+{
+ int i;
+ char *name = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(mpi3mr_reset_reason_codes); i++) {
+ if (mpi3mr_reset_reason_codes[i].value == reason_code) {
+ name = mpi3mr_reset_reason_codes[i].name;
+ break;
+ }
+ }
+ return name;
+}
+
+/* Reset type to name mapper structure*/
+static const struct {
+ u16 reset_type;
+ char *name;
+} mpi3mr_reset_types[] = {
+ { MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET, "soft" },
+ { MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, "diag fault" },
+};
+
+/**
+ * mpi3mr_reset_type_name - get reset type name
+ * @reset_type: reset type value
+ *
+ * Map reset type to an NULL terminated ASCII string
+ *
+ * Return: name corresponding to reset type value or NULL.
+ */
+static const char *mpi3mr_reset_type_name(u16 reset_type)
+{
+ int i;
+ char *name = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(mpi3mr_reset_types); i++) {
+ if (mpi3mr_reset_types[i].reset_type == reset_type) {
+ name = mpi3mr_reset_types[i].name;
+ break;
+ }
+ }
+ return name;
+}
+
+/**
+ * mpi3mr_print_fault_info - Display fault information
+ * @mrioc: Adapter instance reference
+ *
+ * Display the controller fault information if there is a
+ * controller fault.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_print_fault_info(struct mpi3mr_ioc *mrioc)
+{
+ u32 ioc_status, code, code1, code2, code3;
+
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+
+ if (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) {
+ code = readl(&mrioc->sysif_regs->fault);
+ code1 = readl(&mrioc->sysif_regs->fault_info[0]);
+ code2 = readl(&mrioc->sysif_regs->fault_info[1]);
+ code3 = readl(&mrioc->sysif_regs->fault_info[2]);
+
+ ioc_info(mrioc,
+ "fault code(0x%08X): Additional code: (0x%08X:0x%08X:0x%08X)\n",
+ code, code1, code2, code3);
+ }
+}
+
+/**
+ * mpi3mr_get_iocstate - Get IOC State
+ * @mrioc: Adapter instance reference
+ *
+ * Return a proper IOC state enum based on the IOC status and
+ * IOC configuration and unrcoverable state of the controller.
+ *
+ * Return: Current IOC state.
+ */
+enum mpi3mr_iocstate mpi3mr_get_iocstate(struct mpi3mr_ioc *mrioc)
+{
+ u32 ioc_status, ioc_config;
+ u8 ready, enabled;
+
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+
+ if (mrioc->unrecoverable)
+ return MRIOC_STATE_UNRECOVERABLE;
+ if (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT)
+ return MRIOC_STATE_FAULT;
+
+ ready = (ioc_status & MPI3_SYSIF_IOC_STATUS_READY);
+ enabled = (ioc_config & MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC);
+
+ if (ready && enabled)
+ return MRIOC_STATE_READY;
+ if ((!ready) && (!enabled))
+ return MRIOC_STATE_RESET;
+ if ((!ready) && (enabled))
+ return MRIOC_STATE_BECOMING_READY;
+
+ return MRIOC_STATE_RESET_REQUESTED;
+}
+
+/**
+ * mpi3mr_clear_reset_history - clear reset history
+ * @mrioc: Adapter instance reference
+ *
+ * Write the reset history bit in IOC status to clear the bit,
+ * if it is already set.
+ *
+ * Return: Nothing.
+ */
+static inline void mpi3mr_clear_reset_history(struct mpi3mr_ioc *mrioc)
+{
+ u32 ioc_status;
+
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if (ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY)
+ writel(ioc_status, &mrioc->sysif_regs->ioc_status);
+}
+
+/**
+ * mpi3mr_issue_and_process_mur - Message unit Reset handler
+ * @mrioc: Adapter instance reference
+ * @reset_reason: Reset reason code
+ *
+ * Issue Message unit Reset to the controller and wait for it to
+ * be complete.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int mpi3mr_issue_and_process_mur(struct mpi3mr_ioc *mrioc,
+ u32 reset_reason)
+{
+ u32 ioc_config, timeout, ioc_status;
+ int retval = -1;
+
+ ioc_info(mrioc, "Issuing Message unit Reset(MUR)\n");
+ if (mrioc->unrecoverable) {
+ ioc_info(mrioc, "IOC is unrecoverable MUR not issued\n");
+ return retval;
+ }
+ mpi3mr_clear_reset_history(mrioc);
+ writel(reset_reason, &mrioc->sysif_regs->scratchpad[0]);
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ ioc_config &= ~MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC;
+ writel(ioc_config, &mrioc->sysif_regs->ioc_configuration);
+
+ timeout = mrioc->ready_timeout * 10;
+ do {
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if ((ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY)) {
+ mpi3mr_clear_reset_history(mrioc);
+ ioc_config =
+ readl(&mrioc->sysif_regs->ioc_configuration);
+ if (!((ioc_status & MPI3_SYSIF_IOC_STATUS_READY) ||
+ (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) ||
+ (ioc_config & MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC))) {
+ retval = 0;
+ break;
+ }
+ }
+ msleep(100);
+ } while (--timeout);
+
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+
+ ioc_info(mrioc, "Base IOC Sts/Config after %s MUR is (0x%x)/(0x%x)\n",
+ (!retval) ? "successful" : "failed", ioc_status, ioc_config);
+ return retval;
+}
+
+/**
+ * mpi3mr_bring_ioc_ready - Bring controller to ready state
+ * @mrioc: Adapter instance reference
+ *
+ * Set Enable IOC bit in IOC configuration register and wait for
+ * the controller to become ready.
+ *
+ * Return: 0 on success, -1 on failure.
+ */
+static int mpi3mr_bring_ioc_ready(struct mpi3mr_ioc *mrioc)
+{
+ u32 ioc_config, timeout;
+ enum mpi3mr_iocstate current_state;
+
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ ioc_config |= MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC;
+ writel(ioc_config, &mrioc->sysif_regs->ioc_configuration);
+
+ timeout = mrioc->ready_timeout * 10;
+ do {
+ current_state = mpi3mr_get_iocstate(mrioc);
+ if (current_state == MRIOC_STATE_READY)
+ return 0;
+ msleep(100);
+ } while (--timeout);
+
+ return -1;
+}
+
+/**
+ * mpi3mr_soft_reset_success - Check softreset is success or not
+ * @ioc_status: IOC status register value
+ * @ioc_config: IOC config register value
+ *
+ * Check whether the soft reset is successful or not based on
+ * IOC status and IOC config register values.
+ *
+ * Return: True when the soft reset is success, false otherwise.
+ */
+static inline bool
+mpi3mr_soft_reset_success(u32 ioc_status, u32 ioc_config)
+{
+ if (!((ioc_status & MPI3_SYSIF_IOC_STATUS_READY) ||
+ (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) ||
+ (ioc_config & MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC)))
+ return true;
+ return false;
+}
+
+/**
+ * mpi3mr_diagfault_success - Check diag fault is success or not
+ * @mrioc: Adapter reference
+ * @ioc_status: IOC status register value
+ *
+ * Check whether the controller hit diag reset fault code.
+ *
+ * Return: True when there is diag fault, false otherwise.
+ */
+static inline bool mpi3mr_diagfault_success(struct mpi3mr_ioc *mrioc,
+ u32 ioc_status)
+{
+ u32 fault;
+
+ if (!(ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT))
+ return false;
+ fault = readl(&mrioc->sysif_regs->fault) & MPI3_SYSIF_FAULT_CODE_MASK;
+ if (fault == MPI3_SYSIF_FAULT_CODE_DIAG_FAULT_RESET)
+ return true;
+ return false;
+}
+
+/**
+ * mpi3mr_set_diagsave - Set diag save bit for snapdump
+ * @mrioc: Adapter reference
+ *
+ * Set diag save bit in IOC configuration register to enable
+ * snapdump.
+ *
+ * Return: Nothing.
+ */
+static inline void mpi3mr_set_diagsave(struct mpi3mr_ioc *mrioc)
+{
+ u32 ioc_config;
+
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ ioc_config |= MPI3_SYSIF_IOC_CONFIG_DIAG_SAVE;
+ writel(ioc_config, &mrioc->sysif_regs->ioc_configuration);
+}
+
+/**
+ * mpi3mr_issue_reset - Issue reset to the controller
+ * @mrioc: Adapter reference
+ * @reset_type: Reset type
+ * @reset_reason: Reset reason code
+ *
+ * Unlock the host diagnostic registers and write the specific
+ * reset type to that, wait for reset acknowledgment from the
+ * controller, if the reset is not successful retry for the
+ * predefined number of times.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type,
+ u32 reset_reason)
+{
+ int retval = -1;
+ u8 unlock_retry_count, reset_retry_count = 0;
+ u32 host_diagnostic, timeout, ioc_status, ioc_config;
+
+ pci_cfg_access_lock(mrioc->pdev);
+ if ((reset_type != MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET) &&
+ (reset_type != MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT))
+ goto out;
+ if (mrioc->unrecoverable)
+ goto out;
+retry_reset:
+ unlock_retry_count = 0;
+ mpi3mr_clear_reset_history(mrioc);
+ do {
+ ioc_info(mrioc,
+ "Write magic sequence to unlock host diag register (retry=%d)\n",
+ ++unlock_retry_count);
+ if (unlock_retry_count >= MPI3MR_HOSTDIAG_UNLOCK_RETRY_COUNT) {
+ writel(reset_reason, &mrioc->sysif_regs->scratchpad[0]);
+ mrioc->unrecoverable = 1;
+ goto out;
+ }
+
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_FLUSH,
+ &mrioc->sysif_regs->write_sequence);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_1ST,
+ &mrioc->sysif_regs->write_sequence);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND,
+ &mrioc->sysif_regs->write_sequence);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_3RD,
+ &mrioc->sysif_regs->write_sequence);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_4TH,
+ &mrioc->sysif_regs->write_sequence);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_5TH,
+ &mrioc->sysif_regs->write_sequence);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_6TH,
+ &mrioc->sysif_regs->write_sequence);
+ usleep_range(1000, 1100);
+ host_diagnostic = readl(&mrioc->sysif_regs->host_diagnostic);
+ ioc_info(mrioc,
+ "wrote magic sequence: retry_count(%d), host_diagnostic(0x%08x)\n",
+ unlock_retry_count, host_diagnostic);
+ } while (!(host_diagnostic & MPI3_SYSIF_HOST_DIAG_DIAG_WRITE_ENABLE));
+
+ writel(reset_reason, &mrioc->sysif_regs->scratchpad[0]);
+ ioc_info(mrioc, "%s reset due to %s(0x%x)\n",
+ mpi3mr_reset_type_name(reset_type),
+ mpi3mr_reset_rc_name(reset_reason), reset_reason);
+ writel(host_diagnostic | reset_type,
+ &mrioc->sysif_regs->host_diagnostic);
+ timeout = mrioc->ready_timeout * 10;
+ if (reset_type == MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET) {
+ do {
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if (ioc_status &
+ MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) {
+ mpi3mr_clear_reset_history(mrioc);
+ ioc_config =
+ readl(&mrioc->sysif_regs->ioc_configuration);
+ if (mpi3mr_soft_reset_success(ioc_status,
+ ioc_config)) {
+ retval = 0;
+ break;
+ }
+ }
+ msleep(100);
+ } while (--timeout);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND,
+ &mrioc->sysif_regs->write_sequence);
+ } else if (reset_type == MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT) {
+ do {
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if (mpi3mr_diagfault_success(mrioc, ioc_status)) {
+ retval = 0;
+ break;
+ }
+ msleep(100);
+ } while (--timeout);
+ mpi3mr_clear_reset_history(mrioc);
+ writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND,
+ &mrioc->sysif_regs->write_sequence);
+ }
+ if (retval && ((++reset_retry_count) < MPI3MR_MAX_RESET_RETRY_COUNT)) {
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ ioc_info(mrioc,
+ "Base IOC Sts/Config after reset try %d is (0x%x)/(0x%x)\n",
+ reset_retry_count, ioc_status, ioc_config);
+ goto retry_reset;
+ }
+
+out:
+ pci_cfg_access_unlock(mrioc->pdev);
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+
+ ioc_info(mrioc,
+ "Base IOC Sts/Config after %s reset is (0x%x)/(0x%x)\n",
+ (!retval) ? "successful" : "failed", ioc_status,
+ ioc_config);
+ return retval;
+}
+
+/**
+ * mpi3mr_admin_request_post - Post request to admin queue
+ * @mrioc: Adapter reference
+ * @admin_req: MPI3 request
+ * @admin_req_sz: Request size
+ * @ignore_reset: Ignore reset in process
+ *
+ * Post the MPI3 request into admin request queue and
+ * inform the controller, if the queue is full return
+ * appropriate error.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_admin_request_post(struct mpi3mr_ioc *mrioc, void *admin_req,
+ u16 admin_req_sz, u8 ignore_reset)
+{
+ u16 areq_pi = 0, areq_ci = 0, max_entries = 0;
+ int retval = 0;
+ unsigned long flags;
+ u8 *areq_entry;
+
+ if (mrioc->unrecoverable) {
+ ioc_err(mrioc, "%s : Unrecoverable controller\n", __func__);
+ return -EFAULT;
+ }
+
+ spin_lock_irqsave(&mrioc->admin_req_lock, flags);
+ areq_pi = mrioc->admin_req_pi;
+ areq_ci = mrioc->admin_req_ci;
+ max_entries = mrioc->num_admin_req;
+ if ((areq_ci == (areq_pi + 1)) || ((!areq_ci) &&
+ (areq_pi == (max_entries - 1)))) {
+ ioc_err(mrioc, "AdminReqQ full condition detected\n");
+ retval = -EAGAIN;
+ goto out;
+ }
+ if (!ignore_reset && mrioc->reset_in_progress) {
+ ioc_err(mrioc, "AdminReqQ submit reset in progress\n");
+ retval = -EAGAIN;
+ goto out;
+ }
+ areq_entry = (u8 *)mrioc->admin_req_base +
+ (areq_pi * MPI3MR_ADMIN_REQ_FRAME_SZ);
+ memset(areq_entry, 0, MPI3MR_ADMIN_REQ_FRAME_SZ);
+ memcpy(areq_entry, (u8 *)admin_req, admin_req_sz);
+
+ if (++areq_pi == max_entries)
+ areq_pi = 0;
+ mrioc->admin_req_pi = areq_pi;
+
+ writel(mrioc->admin_req_pi, &mrioc->sysif_regs->admin_request_queue_pi);
+
+out:
+ spin_unlock_irqrestore(&mrioc->admin_req_lock, flags);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_free_op_req_q_segments - free request memory segments
+ * @mrioc: Adapter instance reference
+ * @q_idx: operational request queue index
+ *
+ * Free memory segments allocated for operational request queue
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_free_op_req_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx)
+{
+ u16 j;
+ int size;
+ struct segments *segments;
+
+ segments = mrioc->req_qinfo[q_idx].q_segments;
+ if (!segments)
+ return;
+
+ if (mrioc->enable_segqueue) {
+ size = MPI3MR_OP_REQ_Q_SEG_SIZE;
+ if (mrioc->req_qinfo[q_idx].q_segment_list) {
+ dma_free_coherent(&mrioc->pdev->dev,
+ MPI3MR_MAX_SEG_LIST_SIZE,
+ mrioc->req_qinfo[q_idx].q_segment_list,
+ mrioc->req_qinfo[q_idx].q_segment_list_dma);
+ mrioc->op_reply_qinfo[q_idx].q_segment_list = NULL;
+ }
+ } else
+ size = mrioc->req_qinfo[q_idx].num_requests *
+ mrioc->facts.op_req_sz;
+
+ for (j = 0; j < mrioc->req_qinfo[q_idx].num_segments; j++) {
+ if (!segments[j].segment)
+ continue;
+ dma_free_coherent(&mrioc->pdev->dev,
+ size, segments[j].segment, segments[j].segment_dma);
+ segments[j].segment = NULL;
+ }
+ kfree(mrioc->req_qinfo[q_idx].q_segments);
+ mrioc->req_qinfo[q_idx].q_segments = NULL;
+ mrioc->req_qinfo[q_idx].qid = 0;
+}
+
+/**
+ * mpi3mr_free_op_reply_q_segments - free reply memory segments
+ * @mrioc: Adapter instance reference
+ * @q_idx: operational reply queue index
+ *
+ * Free memory segments allocated for operational reply queue
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_free_op_reply_q_segments(struct mpi3mr_ioc *mrioc, u16 q_idx)
+{
+ u16 j;
+ int size;
+ struct segments *segments;
+
+ segments = mrioc->op_reply_qinfo[q_idx].q_segments;
+ if (!segments)
+ return;
+
+ if (mrioc->enable_segqueue) {
+ size = MPI3MR_OP_REP_Q_SEG_SIZE;
+ if (mrioc->op_reply_qinfo[q_idx].q_segment_list) {
+ dma_free_coherent(&mrioc->pdev->dev,
+ MPI3MR_MAX_SEG_LIST_SIZE,
+ mrioc->op_reply_qinfo[q_idx].q_segment_list,
+ mrioc->op_reply_qinfo[q_idx].q_segment_list_dma);
+ mrioc->op_reply_qinfo[q_idx].q_segment_list = NULL;
+ }
+ } else
+ size = mrioc->op_reply_qinfo[q_idx].segment_qd *
+ mrioc->op_reply_desc_sz;
+
+ for (j = 0; j < mrioc->op_reply_qinfo[q_idx].num_segments; j++) {
+ if (!segments[j].segment)
+ continue;
+ dma_free_coherent(&mrioc->pdev->dev,
+ size, segments[j].segment, segments[j].segment_dma);
+ segments[j].segment = NULL;
+ }
+
+ kfree(mrioc->op_reply_qinfo[q_idx].q_segments);
+ mrioc->op_reply_qinfo[q_idx].q_segments = NULL;
+ mrioc->op_reply_qinfo[q_idx].qid = 0;
+}
+
+/**
+ * mpi3mr_delete_op_reply_q - delete operational reply queue
+ * @mrioc: Adapter instance reference
+ * @qidx: operational reply queue index
+ *
+ * Delete operatinal reply queue by issuing MPI request
+ * through admin queue.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_delete_op_reply_q(struct mpi3mr_ioc *mrioc, u16 qidx)
+{
+ struct mpi3_delete_reply_queue_request delq_req;
+ int retval = 0;
+ u16 reply_qid = 0, midx;
+
+ reply_qid = mrioc->op_reply_qinfo[qidx].qid;
+
+ midx = REPLY_QUEUE_IDX_TO_MSIX_IDX(qidx, mrioc->op_reply_q_offset);
+
+ if (!reply_qid) {
+ retval = -1;
+ ioc_err(mrioc, "Issue DelRepQ: called with invalid ReqQID\n");
+ goto out;
+ }
+
+ memset(&delq_req, 0, sizeof(delq_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Issue DelRepQ: Init command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ delq_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ delq_req.function = MPI3_FUNCTION_DELETE_REPLY_QUEUE;
+ delq_req.queue_id = cpu_to_le16(reply_qid);
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &delq_req, sizeof(delq_req),
+ 1);
+ if (retval) {
+ ioc_err(mrioc, "Issue DelRepQ: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "Issue DelRepQ: command timed out\n");
+ mpi3mr_set_diagsave(mrioc);
+ mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT,
+ MPI3MR_RESET_FROM_DELREPQ_TIMEOUT);
+ mrioc->unrecoverable = 1;
+
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "Issue DelRepQ: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+ mrioc->intr_info[midx].op_reply_q = NULL;
+
+ mpi3mr_free_op_reply_q_segments(mrioc, qidx);
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+out:
+
+ return retval;
+}
+
+/**
+ * mpi3mr_alloc_op_reply_q_segments -Alloc segmented reply pool
+ * @mrioc: Adapter instance reference
+ * @qidx: request queue index
+ *
+ * Allocate segmented memory pools for operational reply
+ * queue.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_alloc_op_reply_q_segments(struct mpi3mr_ioc *mrioc, u16 qidx)
+{
+ struct op_reply_qinfo *op_reply_q = mrioc->op_reply_qinfo + qidx;
+ int i, size;
+ u64 *q_segment_list_entry = NULL;
+ struct segments *segments;
+
+ if (mrioc->enable_segqueue) {
+ op_reply_q->segment_qd =
+ MPI3MR_OP_REP_Q_SEG_SIZE / mrioc->op_reply_desc_sz;
+
+ size = MPI3MR_OP_REP_Q_SEG_SIZE;
+
+ op_reply_q->q_segment_list = dma_alloc_coherent(&mrioc->pdev->dev,
+ MPI3MR_MAX_SEG_LIST_SIZE, &op_reply_q->q_segment_list_dma,
+ GFP_KERNEL);
+ if (!op_reply_q->q_segment_list)
+ return -ENOMEM;
+ q_segment_list_entry = (u64 *)op_reply_q->q_segment_list;
+ } else {
+ op_reply_q->segment_qd = op_reply_q->num_replies;
+ size = op_reply_q->num_replies * mrioc->op_reply_desc_sz;
+ }
+
+ op_reply_q->num_segments = DIV_ROUND_UP(op_reply_q->num_replies,
+ op_reply_q->segment_qd);
+
+ op_reply_q->q_segments = kcalloc(op_reply_q->num_segments,
+ sizeof(struct segments), GFP_KERNEL);
+ if (!op_reply_q->q_segments)
+ return -ENOMEM;
+
+ segments = op_reply_q->q_segments;
+ for (i = 0; i < op_reply_q->num_segments; i++) {
+ segments[i].segment =
+ dma_alloc_coherent(&mrioc->pdev->dev,
+ size, &segments[i].segment_dma, GFP_KERNEL);
+ if (!segments[i].segment)
+ return -ENOMEM;
+ if (mrioc->enable_segqueue)
+ q_segment_list_entry[i] =
+ (unsigned long)segments[i].segment_dma;
+ }
+
+ return 0;
+}
+
+/**
+ * mpi3mr_alloc_op_req_q_segments - Alloc segmented req pool.
+ * @mrioc: Adapter instance reference
+ * @qidx: request queue index
+ *
+ * Allocate segmented memory pools for operational request
+ * queue.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_alloc_op_req_q_segments(struct mpi3mr_ioc *mrioc, u16 qidx)
+{
+ struct op_req_qinfo *op_req_q = mrioc->req_qinfo + qidx;
+ int i, size;
+ u64 *q_segment_list_entry = NULL;
+ struct segments *segments;
+
+ if (mrioc->enable_segqueue) {
+ op_req_q->segment_qd =
+ MPI3MR_OP_REQ_Q_SEG_SIZE / mrioc->facts.op_req_sz;
+
+ size = MPI3MR_OP_REQ_Q_SEG_SIZE;
+
+ op_req_q->q_segment_list = dma_alloc_coherent(&mrioc->pdev->dev,
+ MPI3MR_MAX_SEG_LIST_SIZE, &op_req_q->q_segment_list_dma,
+ GFP_KERNEL);
+ if (!op_req_q->q_segment_list)
+ return -ENOMEM;
+ q_segment_list_entry = (u64 *)op_req_q->q_segment_list;
+
+ } else {
+ op_req_q->segment_qd = op_req_q->num_requests;
+ size = op_req_q->num_requests * mrioc->facts.op_req_sz;
+ }
+
+ op_req_q->num_segments = DIV_ROUND_UP(op_req_q->num_requests,
+ op_req_q->segment_qd);
+
+ op_req_q->q_segments = kcalloc(op_req_q->num_segments,
+ sizeof(struct segments), GFP_KERNEL);
+ if (!op_req_q->q_segments)
+ return -ENOMEM;
+
+ segments = op_req_q->q_segments;
+ for (i = 0; i < op_req_q->num_segments; i++) {
+ segments[i].segment =
+ dma_alloc_coherent(&mrioc->pdev->dev,
+ size, &segments[i].segment_dma, GFP_KERNEL);
+ if (!segments[i].segment)
+ return -ENOMEM;
+ if (mrioc->enable_segqueue)
+ q_segment_list_entry[i] =
+ (unsigned long)segments[i].segment_dma;
+ }
+
+ return 0;
+}
+
+/**
+ * mpi3mr_create_op_reply_q - create operational reply queue
+ * @mrioc: Adapter instance reference
+ * @qidx: operational reply queue index
+ *
+ * Create operatinal reply queue by issuing MPI request
+ * through admin queue.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_create_op_reply_q(struct mpi3mr_ioc *mrioc, u16 qidx)
+{
+ struct mpi3_create_reply_queue_request create_req;
+ struct op_reply_qinfo *op_reply_q = mrioc->op_reply_qinfo + qidx;
+ int retval = 0;
+ u16 reply_qid = 0, midx;
+
+ reply_qid = op_reply_q->qid;
+
+ midx = REPLY_QUEUE_IDX_TO_MSIX_IDX(qidx, mrioc->op_reply_q_offset);
+
+ if (reply_qid) {
+ retval = -1;
+ ioc_err(mrioc, "CreateRepQ: called for duplicate qid %d\n",
+ reply_qid);
+
+ return retval;
+ }
+
+ reply_qid = qidx + 1;
+ op_reply_q->num_replies = MPI3MR_OP_REP_Q_QD;
+ op_reply_q->ci = 0;
+ op_reply_q->ephase = 1;
+ atomic_set(&op_reply_q->pend_ios, 0);
+ atomic_set(&op_reply_q->in_use, 0);
+ op_reply_q->enable_irq_poll = false;
+
+ if (!op_reply_q->q_segments) {
+ retval = mpi3mr_alloc_op_reply_q_segments(mrioc, qidx);
+ if (retval) {
+ mpi3mr_free_op_reply_q_segments(mrioc, qidx);
+ goto out;
+ }
+ }
+
+ memset(&create_req, 0, sizeof(create_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "CreateRepQ: Init command is in use\n");
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ create_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ create_req.function = MPI3_FUNCTION_CREATE_REPLY_QUEUE;
+ create_req.queue_id = cpu_to_le16(reply_qid);
+ create_req.flags = MPI3_CREATE_REPLY_QUEUE_FLAGS_INT_ENABLE_ENABLE;
+ create_req.msix_index = cpu_to_le16(mrioc->intr_info[midx].msix_index);
+ if (mrioc->enable_segqueue) {
+ create_req.flags |=
+ MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_SEGMENTED;
+ create_req.base_address = cpu_to_le64(
+ op_reply_q->q_segment_list_dma);
+ } else
+ create_req.base_address = cpu_to_le64(
+ op_reply_q->q_segments[0].segment_dma);
+
+ create_req.size = cpu_to_le16(op_reply_q->num_replies);
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &create_req,
+ sizeof(create_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "CreateRepQ: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "CreateRepQ: command timed out\n");
+ mpi3mr_set_diagsave(mrioc);
+ mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT,
+ MPI3MR_RESET_FROM_CREATEREPQ_TIMEOUT);
+ mrioc->unrecoverable = 1;
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "CreateRepQ: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+ op_reply_q->qid = reply_qid;
+ mrioc->intr_info[midx].op_reply_q = op_reply_q;
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+out:
+
+ return retval;
+}
+
+/**
+ * mpi3mr_create_op_req_q - create operational request queue
+ * @mrioc: Adapter instance reference
+ * @idx: operational request queue index
+ * @reply_qid: Reply queue ID
+ *
+ * Create operatinal request queue by issuing MPI request
+ * through admin queue.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_create_op_req_q(struct mpi3mr_ioc *mrioc, u16 idx,
+ u16 reply_qid)
+{
+ struct mpi3_create_request_queue_request create_req;
+ struct op_req_qinfo *op_req_q = mrioc->req_qinfo + idx;
+ int retval = 0;
+ u16 req_qid = 0;
+
+ req_qid = op_req_q->qid;
+
+ if (req_qid) {
+ retval = -1;
+ ioc_err(mrioc, "CreateReqQ: called for duplicate qid %d\n",
+ req_qid);
+
+ return retval;
+ }
+ req_qid = idx + 1;
+
+ op_req_q->num_requests = MPI3MR_OP_REQ_Q_QD;
+ op_req_q->ci = 0;
+ op_req_q->pi = 0;
+ op_req_q->reply_qid = reply_qid;
+ spin_lock_init(&op_req_q->q_lock);
+
+ if (!op_req_q->q_segments) {
+ retval = mpi3mr_alloc_op_req_q_segments(mrioc, idx);
+ if (retval) {
+ mpi3mr_free_op_req_q_segments(mrioc, idx);
+ goto out;
+ }
+ }
+
+ memset(&create_req, 0, sizeof(create_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "CreateReqQ: Init command is in use\n");
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ create_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ create_req.function = MPI3_FUNCTION_CREATE_REQUEST_QUEUE;
+ create_req.queue_id = cpu_to_le16(req_qid);
+ if (mrioc->enable_segqueue) {
+ create_req.flags =
+ MPI3_CREATE_REQUEST_QUEUE_FLAGS_SEGMENTED_SEGMENTED;
+ create_req.base_address = cpu_to_le64(
+ op_req_q->q_segment_list_dma);
+ } else
+ create_req.base_address = cpu_to_le64(
+ op_req_q->q_segments[0].segment_dma);
+ create_req.reply_queue_id = cpu_to_le16(reply_qid);
+ create_req.size = cpu_to_le16(op_req_q->num_requests);
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &create_req,
+ sizeof(create_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "CreateReqQ: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "CreateReqQ: command timed out\n");
+ mpi3mr_set_diagsave(mrioc);
+ if (mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT,
+ MPI3MR_RESET_FROM_CREATEREQQ_TIMEOUT))
+ mrioc->unrecoverable = 1;
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "CreateReqQ: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+ op_req_q->qid = req_qid;
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+out:
+
+ return retval;
+}
+
+/**
+ * mpi3mr_create_op_queues - create operational queue pairs
+ * @mrioc: Adapter instance reference
+ *
+ * Allocate memory for operational queue meta data and call
+ * create request and reply queue functions.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static int mpi3mr_create_op_queues(struct mpi3mr_ioc *mrioc)
+{
+ int retval = 0;
+ u16 num_queues = 0, i = 0, msix_count_op_q = 1;
+
+ num_queues = min_t(int, mrioc->facts.max_op_reply_q,
+ mrioc->facts.max_op_req_q);
+
+ msix_count_op_q =
+ mrioc->intr_info_count - mrioc->op_reply_q_offset;
+ if (!mrioc->num_queues)
+ mrioc->num_queues = min_t(int, num_queues, msix_count_op_q);
+ num_queues = mrioc->num_queues;
+ ioc_info(mrioc, "Trying to create %d Operational Q pairs\n",
+ num_queues);
+
+ if (!mrioc->req_qinfo) {
+ mrioc->req_qinfo = kcalloc(num_queues,
+ sizeof(struct op_req_qinfo), GFP_KERNEL);
+ if (!mrioc->req_qinfo) {
+ retval = -1;
+ goto out_failed;
+ }
+
+ mrioc->op_reply_qinfo = kzalloc(sizeof(struct op_reply_qinfo) *
+ num_queues, GFP_KERNEL);
+ if (!mrioc->op_reply_qinfo) {
+ retval = -1;
+ goto out_failed;
+ }
+ }
+
+ if (mrioc->enable_segqueue)
+ ioc_info(mrioc,
+ "allocating operational queues through segmented queues\n");
+
+ for (i = 0; i < num_queues; i++) {
+ if (mpi3mr_create_op_reply_q(mrioc, i)) {
+ ioc_err(mrioc, "Cannot create OP RepQ %d\n", i);
+ break;
+ }
+ if (mpi3mr_create_op_req_q(mrioc, i,
+ mrioc->op_reply_qinfo[i].qid)) {
+ ioc_err(mrioc, "Cannot create OP ReqQ %d\n", i);
+ mpi3mr_delete_op_reply_q(mrioc, i);
+ break;
+ }
+ }
+
+ if (i == 0) {
+ /* Not even one queue is created successfully*/
+ retval = -1;
+ goto out_failed;
+ }
+ mrioc->num_op_reply_q = mrioc->num_op_req_q = i;
+ ioc_info(mrioc, "Successfully created %d Operational Q pairs\n",
+ mrioc->num_op_reply_q);
+
+ return retval;
+out_failed:
+ kfree(mrioc->req_qinfo);
+ mrioc->req_qinfo = NULL;
+
+ kfree(mrioc->op_reply_qinfo);
+ mrioc->op_reply_qinfo = NULL;
+
+ return retval;
+}
+
+/**
+ * mpi3mr_op_request_post - Post request to operational queue
+ * @mrioc: Adapter reference
+ * @op_req_q: Operational request queue info
+ * @req: MPI3 request
+ *
+ * Post the MPI3 request into operational request queue and
+ * inform the controller, if the queue is full return
+ * appropriate error.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_op_request_post(struct mpi3mr_ioc *mrioc,
+ struct op_req_qinfo *op_req_q, u8 *req)
+{
+ u16 pi = 0, max_entries, reply_qidx = 0, midx;
+ int retval = 0;
+ unsigned long flags;
+ u8 *req_entry;
+ void *segment_base_addr;
+ u16 req_sz = mrioc->facts.op_req_sz;
+ struct segments *segments = op_req_q->q_segments;
+
+ reply_qidx = op_req_q->reply_qid - 1;
+
+ if (mrioc->unrecoverable)
+ return -EFAULT;
+
+ spin_lock_irqsave(&op_req_q->q_lock, flags);
+ pi = op_req_q->pi;
+ max_entries = op_req_q->num_requests;
+
+ if (mpi3mr_check_req_qfull(op_req_q)) {
+ midx = REPLY_QUEUE_IDX_TO_MSIX_IDX(
+ reply_qidx, mrioc->op_reply_q_offset);
+ mpi3mr_process_op_reply_q(mrioc, &mrioc->intr_info[midx]);
+
+ if (mpi3mr_check_req_qfull(op_req_q)) {
+ retval = -EAGAIN;
+ goto out;
+ }
+ }
+
+ if (mrioc->reset_in_progress) {
+ ioc_err(mrioc, "OpReqQ submit reset in progress\n");
+ retval = -EAGAIN;
+ goto out;
+ }
+
+ segment_base_addr = segments[pi / op_req_q->segment_qd].segment;
+ req_entry = (u8 *)segment_base_addr +
+ ((pi % op_req_q->segment_qd) * req_sz);
+
+ memset(req_entry, 0, req_sz);
+ memcpy(req_entry, req, MPI3MR_ADMIN_REQ_FRAME_SZ);
+
+ if (++pi == max_entries)
+ pi = 0;
+ op_req_q->pi = pi;
+
+ if (atomic_inc_return(&mrioc->op_reply_qinfo[reply_qidx].pend_ios)
+ > MPI3MR_IRQ_POLL_TRIGGER_IOCOUNT)
+ mrioc->op_reply_qinfo[reply_qidx].enable_irq_poll = true;
+
+ writel(op_req_q->pi,
+ &mrioc->sysif_regs->oper_queue_indexes[reply_qidx].producer_index);
+
+out:
+ spin_unlock_irqrestore(&op_req_q->q_lock, flags);
+ return retval;
+}
+
+/**
+ * mpi3mr_sync_timestamp - Issue time stamp sync request
+ * @mrioc: Adapter reference
+ *
+ * Issue IO unit control MPI request to synchornize firmware
+ * timestamp with host time.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_sync_timestamp(struct mpi3mr_ioc *mrioc)
+{
+ ktime_t current_time;
+ struct mpi3_iounit_control_request iou_ctrl;
+ int retval = 0;
+
+ memset(&iou_ctrl, 0, sizeof(iou_ctrl));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Issue IOUCTL time_stamp: command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ iou_ctrl.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ iou_ctrl.function = MPI3_FUNCTION_IO_UNIT_CONTROL;
+ iou_ctrl.operation = MPI3_CTRL_OP_UPDATE_TIMESTAMP;
+ current_time = ktime_get_real();
+ iou_ctrl.param64[0] = cpu_to_le64(ktime_to_ms(current_time));
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &iou_ctrl,
+ sizeof(iou_ctrl), 0);
+ if (retval) {
+ ioc_err(mrioc, "Issue IOUCTL time_stamp: Admin Post failed\n");
+ goto out_unlock;
+ }
+
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "Issue IOUCTL time_stamp: command timed out\n");
+ mrioc->init_cmds.is_waiting = 0;
+ mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_TSU_TIMEOUT, 1);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "Issue IOUCTL time_stamp: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+
+out:
+ return retval;
+}
+
+/**
+ * mpi3mr_watchdog_work - watchdog thread to monitor faults
+ * @work: work struct
+ *
+ * Watch dog work periodically executed (1 second interval) to
+ * monitor firmware fault and to issue periodic timer sync to
+ * the firmware.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_watchdog_work(struct work_struct *work)
+{
+ struct mpi3mr_ioc *mrioc =
+ container_of(work, struct mpi3mr_ioc, watchdog_work.work);
+ unsigned long flags;
+ enum mpi3mr_iocstate ioc_state;
+ u32 fault, host_diagnostic;
+
+ if (mrioc->ts_update_counter++ >= MPI3MR_TSUPDATE_INTERVAL) {
+ mrioc->ts_update_counter = 0;
+ mpi3mr_sync_timestamp(mrioc);
+ }
+
+ /*Check for fault state every one second and issue Soft reset*/
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ if (ioc_state == MRIOC_STATE_FAULT) {
+ fault = readl(&mrioc->sysif_regs->fault) &
+ MPI3_SYSIF_FAULT_CODE_MASK;
+ host_diagnostic = readl(&mrioc->sysif_regs->host_diagnostic);
+ if (host_diagnostic & MPI3_SYSIF_HOST_DIAG_SAVE_IN_PROGRESS) {
+ if (!mrioc->diagsave_timeout) {
+ mpi3mr_print_fault_info(mrioc);
+ ioc_warn(mrioc, "Diag save in progress\n");
+ }
+ if ((mrioc->diagsave_timeout++) <=
+ MPI3_SYSIF_DIAG_SAVE_TIMEOUT)
+ goto schedule_work;
+ } else
+ mpi3mr_print_fault_info(mrioc);
+ mrioc->diagsave_timeout = 0;
+
+ if (fault == MPI3_SYSIF_FAULT_CODE_FACTORY_RESET) {
+ ioc_info(mrioc,
+ "Factory Reset fault occurred marking controller as unrecoverable"
+ );
+ mrioc->unrecoverable = 1;
+ goto out;
+ }
+
+ if ((fault == MPI3_SYSIF_FAULT_CODE_DIAG_FAULT_RESET) ||
+ (fault == MPI3_SYSIF_FAULT_CODE_SOFT_RESET_IN_PROGRESS) ||
+ (mrioc->reset_in_progress))
+ goto out;
+ if (fault == MPI3_SYSIF_FAULT_CODE_CI_ACTIVATION_RESET)
+ mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_CIACTIV_FAULT, 0);
+ else
+ mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_FAULT_WATCH, 0);
+ }
+
+schedule_work:
+ spin_lock_irqsave(&mrioc->watchdog_lock, flags);
+ if (mrioc->watchdog_work_q)
+ queue_delayed_work(mrioc->watchdog_work_q,
+ &mrioc->watchdog_work,
+ msecs_to_jiffies(MPI3MR_WATCHDOG_INTERVAL));
+ spin_unlock_irqrestore(&mrioc->watchdog_lock, flags);
+out:
+ return;
+}
+
+/**
+ * mpi3mr_start_watchdog - Start watchdog
+ * @mrioc: Adapter instance reference
+ *
+ * Create and start the watchdog thread to monitor controller
+ * faults.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_start_watchdog(struct mpi3mr_ioc *mrioc)
+{
+ if (mrioc->watchdog_work_q)
+ return;
+
+ INIT_DELAYED_WORK(&mrioc->watchdog_work, mpi3mr_watchdog_work);
+ snprintf(mrioc->watchdog_work_q_name,
+ sizeof(mrioc->watchdog_work_q_name), "watchdog_%s%d", mrioc->name,
+ mrioc->id);
+ mrioc->watchdog_work_q =
+ create_singlethread_workqueue(mrioc->watchdog_work_q_name);
+ if (!mrioc->watchdog_work_q) {
+ ioc_err(mrioc, "%s: failed (line=%d)\n", __func__, __LINE__);
+ return;
+ }
+
+ if (mrioc->watchdog_work_q)
+ queue_delayed_work(mrioc->watchdog_work_q,
+ &mrioc->watchdog_work,
+ msecs_to_jiffies(MPI3MR_WATCHDOG_INTERVAL));
+}
+
+/**
+ * mpi3mr_stop_watchdog - Stop watchdog
+ * @mrioc: Adapter instance reference
+ *
+ * Stop the watchdog thread created to monitor controller
+ * faults.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_stop_watchdog(struct mpi3mr_ioc *mrioc)
+{
+ unsigned long flags;
+ struct workqueue_struct *wq;
+
+ spin_lock_irqsave(&mrioc->watchdog_lock, flags);
+ wq = mrioc->watchdog_work_q;
+ mrioc->watchdog_work_q = NULL;
+ spin_unlock_irqrestore(&mrioc->watchdog_lock, flags);
+ if (wq) {
+ if (!cancel_delayed_work_sync(&mrioc->watchdog_work))
+ flush_workqueue(wq);
+ destroy_workqueue(wq);
+ }
+}
+
+/**
+ * mpi3mr_kill_ioc - Kill the controller
+ * @mrioc: Adapter instance reference
+ * @reason: reason for the failure.
+ *
+ * If fault debug is enabled, display the fault info else issue
+ * diag fault and freeze the system for controller debug
+ * purpose.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_kill_ioc(struct mpi3mr_ioc *mrioc, u32 reason)
+{
+ enum mpi3mr_iocstate ioc_state;
+
+ if (!mrioc->fault_dbg)
+ return;
+
+ dump_stack();
+
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ if (ioc_state == MRIOC_STATE_FAULT)
+ mpi3mr_print_fault_info(mrioc);
+ else {
+ ioc_err(mrioc, "Firmware is halted due to the reason %d\n",
+ reason);
+ mpi3mr_diagfault_reset_handler(mrioc, reason);
+ }
+ if (mrioc->fault_dbg == 2)
+ for (;;)
+ ;
+ else
+ panic("panic in %s\n", __func__);
+}
+
+/**
+ * mpi3mr_setup_admin_qpair - Setup admin queue pair
+ * @mrioc: Adapter instance reference
+ *
+ * Allocate memory for admin queue pair if required and register
+ * the admin queue with the controller.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static int mpi3mr_setup_admin_qpair(struct mpi3mr_ioc *mrioc)
+{
+ int retval = 0;
+ u32 num_admin_entries = 0;
+
+ mrioc->admin_req_q_sz = MPI3MR_ADMIN_REQ_Q_SIZE;
+ mrioc->num_admin_req = mrioc->admin_req_q_sz /
+ MPI3MR_ADMIN_REQ_FRAME_SZ;
+ mrioc->admin_req_ci = mrioc->admin_req_pi = 0;
+ mrioc->admin_req_base = NULL;
+
+ mrioc->admin_reply_q_sz = MPI3MR_ADMIN_REPLY_Q_SIZE;
+ mrioc->num_admin_replies = mrioc->admin_reply_q_sz /
+ MPI3MR_ADMIN_REPLY_FRAME_SZ;
+ mrioc->admin_reply_ci = 0;
+ mrioc->admin_reply_ephase = 1;
+ mrioc->admin_reply_base = NULL;
+
+ if (!mrioc->admin_req_base) {
+ mrioc->admin_req_base = dma_alloc_coherent(&mrioc->pdev->dev,
+ mrioc->admin_req_q_sz, &mrioc->admin_req_dma, GFP_KERNEL);
+
+ if (!mrioc->admin_req_base) {
+ retval = -1;
+ goto out_failed;
+ }
+
+ mrioc->admin_reply_base = dma_alloc_coherent(&mrioc->pdev->dev,
+ mrioc->admin_reply_q_sz, &mrioc->admin_reply_dma,
+ GFP_KERNEL);
+
+ if (!mrioc->admin_reply_base) {
+ retval = -1;
+ goto out_failed;
+ }
+ }
+
+ num_admin_entries = (mrioc->num_admin_replies << 16) |
+ (mrioc->num_admin_req);
+ writel(num_admin_entries, &mrioc->sysif_regs->admin_queue_num_entries);
+ mpi3mr_writeq(mrioc->admin_req_dma,
+ &mrioc->sysif_regs->admin_request_queue_address);
+ mpi3mr_writeq(mrioc->admin_reply_dma,
+ &mrioc->sysif_regs->admin_reply_queue_address);
+ writel(mrioc->admin_req_pi, &mrioc->sysif_regs->admin_request_queue_pi);
+ writel(mrioc->admin_reply_ci, &mrioc->sysif_regs->admin_reply_queue_ci);
+ return retval;
+
+out_failed:
+
+ if (mrioc->admin_reply_base) {
+ dma_free_coherent(&mrioc->pdev->dev, mrioc->admin_reply_q_sz,
+ mrioc->admin_reply_base, mrioc->admin_reply_dma);
+ mrioc->admin_reply_base = NULL;
+ }
+ if (mrioc->admin_req_base) {
+ dma_free_coherent(&mrioc->pdev->dev, mrioc->admin_req_q_sz,
+ mrioc->admin_req_base, mrioc->admin_req_dma);
+ mrioc->admin_req_base = NULL;
+ }
+ return retval;
+}
+
+/**
+ * mpi3mr_issue_iocfacts - Send IOC Facts
+ * @mrioc: Adapter instance reference
+ * @facts_data: Cached IOC facts data
+ *
+ * Issue IOC Facts MPI request through admin queue and wait for
+ * the completion of it or time out.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static int mpi3mr_issue_iocfacts(struct mpi3mr_ioc *mrioc,
+ struct mpi3_ioc_facts_data *facts_data)
+{
+ struct mpi3_ioc_facts_request iocfacts_req;
+ void *data = NULL;
+ dma_addr_t data_dma;
+ u32 data_len = sizeof(*facts_data);
+ int retval = 0;
+ u8 sgl_flags = MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST;
+
+ data = dma_alloc_coherent(&mrioc->pdev->dev, data_len, &data_dma,
+ GFP_KERNEL);
+
+ if (!data) {
+ retval = -1;
+ goto out;
+ }
+
+ memset(&iocfacts_req, 0, sizeof(iocfacts_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Issue IOCFacts: Init command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ iocfacts_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ iocfacts_req.function = MPI3_FUNCTION_IOC_FACTS;
+
+ mpi3mr_add_sg_single(&iocfacts_req.sgl, sgl_flags, data_len,
+ data_dma);
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &iocfacts_req,
+ sizeof(iocfacts_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "Issue IOCFacts: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "Issue IOCFacts: command timed out\n");
+ mpi3mr_set_diagsave(mrioc);
+ mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT,
+ MPI3MR_RESET_FROM_IOCFACTS_TIMEOUT);
+ mrioc->unrecoverable = 1;
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "Issue IOCFacts: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+ memcpy(facts_data, (u8 *)data, data_len);
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+
+out:
+ if (data)
+ dma_free_coherent(&mrioc->pdev->dev, data_len, data, data_dma);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_check_reset_dma_mask - Process IOC facts data
+ * @mrioc: Adapter instance reference
+ *
+ * Check whether the new DMA mask requested through IOCFacts by
+ * firmware needs to be set, if so set it .
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static inline int mpi3mr_check_reset_dma_mask(struct mpi3mr_ioc *mrioc)
+{
+ struct pci_dev *pdev = mrioc->pdev;
+ int r;
+ u64 facts_dma_mask = DMA_BIT_MASK(mrioc->facts.dma_mask);
+
+ if (!mrioc->facts.dma_mask || (mrioc->dma_mask <= facts_dma_mask))
+ return 0;
+
+ ioc_info(mrioc, "Changing DMA mask from 0x%016llx to 0x%016llx\n",
+ mrioc->dma_mask, facts_dma_mask);
+
+ r = dma_set_mask_and_coherent(&pdev->dev, facts_dma_mask);
+ if (r) {
+ ioc_err(mrioc, "Setting DMA mask to 0x%016llx failed: %d\n",
+ facts_dma_mask, r);
+ return r;
+ }
+ mrioc->dma_mask = facts_dma_mask;
+ return r;
+}
+
+/**
+ * mpi3mr_process_factsdata - Process IOC facts data
+ * @mrioc: Adapter instance reference
+ * @facts_data: Cached IOC facts data
+ *
+ * Convert IOC facts data into cpu endianness and cache it in
+ * the driver .
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_process_factsdata(struct mpi3mr_ioc *mrioc,
+ struct mpi3_ioc_facts_data *facts_data)
+{
+ u32 ioc_config, req_sz, facts_flags;
+
+ if ((le16_to_cpu(facts_data->ioc_facts_data_length)) !=
+ (sizeof(*facts_data) / 4)) {
+ ioc_warn(mrioc,
+ "IOCFactsdata length mismatch driver_sz(%zu) firmware_sz(%d)\n",
+ sizeof(*facts_data),
+ le16_to_cpu(facts_data->ioc_facts_data_length) * 4);
+ }
+
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ req_sz = 1 << ((ioc_config & MPI3_SYSIF_IOC_CONFIG_OPER_REQ_ENT_SZ) >>
+ MPI3_SYSIF_IOC_CONFIG_OPER_REQ_ENT_SZ_SHIFT);
+ if (le16_to_cpu(facts_data->ioc_request_frame_size) != (req_sz / 4)) {
+ ioc_err(mrioc,
+ "IOCFacts data reqFrameSize mismatch hw_size(%d) firmware_sz(%d)\n",
+ req_sz / 4, le16_to_cpu(facts_data->ioc_request_frame_size));
+ }
+
+ memset(&mrioc->facts, 0, sizeof(mrioc->facts));
+
+ facts_flags = le32_to_cpu(facts_data->flags);
+ mrioc->facts.op_req_sz = req_sz;
+ mrioc->op_reply_desc_sz = 1 << ((ioc_config &
+ MPI3_SYSIF_IOC_CONFIG_OPER_RPY_ENT_SZ) >>
+ MPI3_SYSIF_IOC_CONFIG_OPER_RPY_ENT_SZ_SHIFT);
+
+ mrioc->facts.ioc_num = facts_data->ioc_number;
+ mrioc->facts.who_init = facts_data->who_init;
+ mrioc->facts.max_msix_vectors = le16_to_cpu(facts_data->max_msix_vectors);
+ mrioc->facts.personality = (facts_flags &
+ MPI3_IOCFACTS_FLAGS_PERSONALITY_MASK);
+ mrioc->facts.dma_mask = (facts_flags &
+ MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_MASK) >>
+ MPI3_IOCFACTS_FLAGS_DMA_ADDRESS_WIDTH_SHIFT;
+ mrioc->facts.protocol_flags = facts_data->protocol_flags;
+ mrioc->facts.mpi_version = le32_to_cpu(facts_data->mpi_version.word);
+ mrioc->facts.max_reqs = le16_to_cpu(facts_data->max_outstanding_request);
+ mrioc->facts.product_id = le16_to_cpu(facts_data->product_id);
+ mrioc->facts.reply_sz = le16_to_cpu(facts_data->reply_frame_size) * 4;
+ mrioc->facts.exceptions = le16_to_cpu(facts_data->ioc_exceptions);
+ mrioc->facts.max_perids = le16_to_cpu(facts_data->max_persistent_id);
+ mrioc->facts.max_pds = le16_to_cpu(facts_data->max_pds);
+ mrioc->facts.max_vds = le16_to_cpu(facts_data->max_vds);
+ mrioc->facts.max_hpds = le16_to_cpu(facts_data->max_host_pds);
+ mrioc->facts.max_advhpds = le16_to_cpu(facts_data->max_advanced_host_pds);
+ mrioc->facts.max_raidpds = le16_to_cpu(facts_data->max_raid_pds);
+ mrioc->facts.max_nvme = le16_to_cpu(facts_data->max_nvme);
+ mrioc->facts.max_pcie_switches =
+ le16_to_cpu(facts_data->max_pc_ie_switches);
+ mrioc->facts.max_sasexpanders =
+ le16_to_cpu(facts_data->max_sas_expanders);
+ mrioc->facts.max_sasinitiators =
+ le16_to_cpu(facts_data->max_sas_initiators);
+ mrioc->facts.max_enclosures = le16_to_cpu(facts_data->max_enclosures);
+ mrioc->facts.min_devhandle = le16_to_cpu(facts_data->min_dev_handle);
+ mrioc->facts.max_devhandle = le16_to_cpu(facts_data->max_dev_handle);
+ mrioc->facts.max_op_req_q =
+ le16_to_cpu(facts_data->max_operational_request_queues);
+ mrioc->facts.max_op_reply_q =
+ le16_to_cpu(facts_data->max_operational_reply_queues);
+ mrioc->facts.ioc_capabilities =
+ le32_to_cpu(facts_data->ioc_capabilities);
+ mrioc->facts.fw_ver.build_num =
+ le16_to_cpu(facts_data->fw_version.build_num);
+ mrioc->facts.fw_ver.cust_id =
+ le16_to_cpu(facts_data->fw_version.customer_id);
+ mrioc->facts.fw_ver.ph_minor = facts_data->fw_version.phase_minor;
+ mrioc->facts.fw_ver.ph_major = facts_data->fw_version.phase_major;
+ mrioc->facts.fw_ver.gen_minor = facts_data->fw_version.gen_minor;
+ mrioc->facts.fw_ver.gen_major = facts_data->fw_version.gen_major;
+ mrioc->msix_count = min_t(int, mrioc->msix_count,
+ mrioc->facts.max_msix_vectors);
+ mrioc->facts.sge_mod_mask = facts_data->sge_modifier_mask;
+ mrioc->facts.sge_mod_value = facts_data->sge_modifier_value;
+ mrioc->facts.sge_mod_shift = facts_data->sge_modifier_shift;
+ mrioc->facts.shutdown_timeout =
+ le16_to_cpu(facts_data->shutdown_timeout);
+
+ ioc_info(mrioc, "ioc_num(%d), maxopQ(%d), maxopRepQ(%d), maxdh(%d),",
+ mrioc->facts.ioc_num, mrioc->facts.max_op_req_q,
+ mrioc->facts.max_op_reply_q, mrioc->facts.max_devhandle);
+ ioc_info(mrioc,
+ "maxreqs(%d), mindh(%d) maxPDs(%d) maxvectors(%d) maxperids(%d)\n",
+ mrioc->facts.max_reqs, mrioc->facts.min_devhandle,
+ mrioc->facts.max_pds, mrioc->facts.max_msix_vectors,
+ mrioc->facts.max_perids);
+ ioc_info(mrioc, "SGEModMask 0x%x SGEModVal 0x%x SGEModShift 0x%x ",
+ mrioc->facts.sge_mod_mask, mrioc->facts.sge_mod_value,
+ mrioc->facts.sge_mod_shift);
+ ioc_info(mrioc, "DMA mask %d InitialPE status 0x%x\n",
+ mrioc->facts.dma_mask, (facts_flags &
+ MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_MASK));
+
+ mrioc->max_host_ios = mrioc->facts.max_reqs - MPI3MR_INTERNAL_CMDS_RESVD;
+
+ if (reset_devices)
+ mrioc->max_host_ios = min_t(int, mrioc->max_host_ios,
+ MPI3MR_HOST_IOS_KDUMP);
+}
+
+/**
+ * mpi3mr_alloc_reply_sense_bufs - Send IOC Init
+ * @mrioc: Adapter instance reference
+ *
+ * Allocate and initialize the reply free buffers, sense
+ * buffers, reply free queue and sense buffer queue.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static int mpi3mr_alloc_reply_sense_bufs(struct mpi3mr_ioc *mrioc)
+{
+ int retval = 0;
+ u32 sz, i;
+ dma_addr_t phy_addr;
+
+ if (mrioc->init_cmds.reply)
+ goto post_reply_sbuf;
+
+ mrioc->init_cmds.reply = kzalloc(mrioc->facts.reply_sz, GFP_KERNEL);
+ if (!mrioc->init_cmds.reply)
+ goto out_failed;
+
+ for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) {
+ mrioc->dev_rmhs_cmds[i].reply = kzalloc(mrioc->facts.reply_sz,
+ GFP_KERNEL);
+ if (!mrioc->dev_rmhs_cmds[i].reply)
+ goto out_failed;
+ }
+
+ mrioc->host_tm_cmds.reply = kzalloc(mrioc->facts.reply_sz, GFP_KERNEL);
+ if (!mrioc->host_tm_cmds.reply)
+ goto out_failed;
+
+ mrioc->dev_handle_bitmap_sz = mrioc->facts.max_devhandle / 8;
+ if (mrioc->facts.max_devhandle % 8)
+ mrioc->dev_handle_bitmap_sz++;
+ mrioc->removepend_bitmap = kzalloc(mrioc->dev_handle_bitmap_sz,
+ GFP_KERNEL);
+ if (!mrioc->removepend_bitmap)
+ goto out_failed;
+
+ mrioc->devrem_bitmap_sz = MPI3MR_NUM_DEVRMCMD / 8;
+ if (MPI3MR_NUM_DEVRMCMD % 8)
+ mrioc->devrem_bitmap_sz++;
+ mrioc->devrem_bitmap = kzalloc(mrioc->devrem_bitmap_sz,
+ GFP_KERNEL);
+ if (!mrioc->devrem_bitmap)
+ goto out_failed;
+
+ mrioc->num_reply_bufs = mrioc->facts.max_reqs + MPI3MR_NUM_EVT_REPLIES;
+ mrioc->reply_free_qsz = mrioc->num_reply_bufs + 1;
+ mrioc->num_sense_bufs = mrioc->facts.max_reqs / MPI3MR_SENSEBUF_FACTOR;
+ mrioc->sense_buf_q_sz = mrioc->num_sense_bufs + 1;
+
+ /* reply buffer pool, 16 byte align */
+ sz = mrioc->num_reply_bufs * mrioc->facts.reply_sz;
+ mrioc->reply_buf_pool = dma_pool_create("reply_buf pool",
+ &mrioc->pdev->dev, sz, 16, 0);
+ if (!mrioc->reply_buf_pool) {
+ ioc_err(mrioc, "reply buf pool: dma_pool_create failed\n");
+ goto out_failed;
+ }
+
+ mrioc->reply_buf = dma_pool_zalloc(mrioc->reply_buf_pool, GFP_KERNEL,
+ &mrioc->reply_buf_dma);
+ if (!mrioc->reply_buf)
+ goto out_failed;
+
+ mrioc->reply_buf_dma_max_address = mrioc->reply_buf_dma + sz;
+
+ /* reply free queue, 8 byte align */
+ sz = mrioc->reply_free_qsz * 8;
+ mrioc->reply_free_q_pool = dma_pool_create("reply_free_q pool",
+ &mrioc->pdev->dev, sz, 8, 0);
+ if (!mrioc->reply_free_q_pool) {
+ ioc_err(mrioc, "reply_free_q pool: dma_pool_create failed\n");
+ goto out_failed;
+ }
+ mrioc->reply_free_q = dma_pool_zalloc(mrioc->reply_free_q_pool,
+ GFP_KERNEL, &mrioc->reply_free_q_dma);
+ if (!mrioc->reply_free_q)
+ goto out_failed;
+
+ /* sense buffer pool, 4 byte align */
+ sz = mrioc->num_sense_bufs * MPI3MR_SENSEBUF_SZ;
+ mrioc->sense_buf_pool = dma_pool_create("sense_buf pool",
+ &mrioc->pdev->dev, sz, 4, 0);
+ if (!mrioc->sense_buf_pool) {
+ ioc_err(mrioc, "sense_buf pool: dma_pool_create failed\n");
+ goto out_failed;
+ }
+ mrioc->sense_buf = dma_pool_zalloc(mrioc->sense_buf_pool, GFP_KERNEL,
+ &mrioc->sense_buf_dma);
+ if (!mrioc->sense_buf)
+ goto out_failed;
+
+ /* sense buffer queue, 8 byte align */
+ sz = mrioc->sense_buf_q_sz * 8;
+ mrioc->sense_buf_q_pool = dma_pool_create("sense_buf_q pool",
+ &mrioc->pdev->dev, sz, 8, 0);
+ if (!mrioc->sense_buf_q_pool) {
+ ioc_err(mrioc, "sense_buf_q pool: dma_pool_create failed\n");
+ goto out_failed;
+ }
+ mrioc->sense_buf_q = dma_pool_zalloc(mrioc->sense_buf_q_pool,
+ GFP_KERNEL, &mrioc->sense_buf_q_dma);
+ if (!mrioc->sense_buf_q)
+ goto out_failed;
+
+post_reply_sbuf:
+ sz = mrioc->num_reply_bufs * mrioc->facts.reply_sz;
+ ioc_info(mrioc,
+ "reply buf pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB), reply_dma(0x%llx)\n",
+ mrioc->reply_buf, mrioc->num_reply_bufs, mrioc->facts.reply_sz,
+ (sz / 1024), (unsigned long long)mrioc->reply_buf_dma);
+ sz = mrioc->reply_free_qsz * 8;
+ ioc_info(mrioc,
+ "reply_free_q pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB), reply_dma(0x%llx)\n",
+ mrioc->reply_free_q, mrioc->reply_free_qsz, 8, (sz / 1024),
+ (unsigned long long)mrioc->reply_free_q_dma);
+ sz = mrioc->num_sense_bufs * MPI3MR_SENSEBUF_SZ;
+ ioc_info(mrioc,
+ "sense_buf pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB), sense_dma(0x%llx)\n",
+ mrioc->sense_buf, mrioc->num_sense_bufs, MPI3MR_SENSEBUF_SZ,
+ (sz / 1024), (unsigned long long)mrioc->sense_buf_dma);
+ sz = mrioc->sense_buf_q_sz * 8;
+ ioc_info(mrioc,
+ "sense_buf_q pool(0x%p): depth(%d), frame_size(%d), pool_size(%d kB), sense_dma(0x%llx)\n",
+ mrioc->sense_buf_q, mrioc->sense_buf_q_sz, 8, (sz / 1024),
+ (unsigned long long)mrioc->sense_buf_q_dma);
+
+ /* initialize Reply buffer Queue */
+ for (i = 0, phy_addr = mrioc->reply_buf_dma;
+ i < mrioc->num_reply_bufs; i++, phy_addr += mrioc->facts.reply_sz)
+ mrioc->reply_free_q[i] = cpu_to_le64(phy_addr);
+ mrioc->reply_free_q[i] = cpu_to_le64(0);
+
+ /* initialize Sense Buffer Queue */
+ for (i = 0, phy_addr = mrioc->sense_buf_dma;
+ i < mrioc->num_sense_bufs; i++, phy_addr += MPI3MR_SENSEBUF_SZ)
+ mrioc->sense_buf_q[i] = cpu_to_le64(phy_addr);
+ mrioc->sense_buf_q[i] = cpu_to_le64(0);
+ return retval;
+
+out_failed:
+ retval = -1;
+ return retval;
+}
+
+/**
+ * mpi3mr_issue_iocinit - Send IOC Init
+ * @mrioc: Adapter instance reference
+ *
+ * Issue IOC Init MPI request through admin queue and wait for
+ * the completion of it or time out.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static int mpi3mr_issue_iocinit(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3_ioc_init_request iocinit_req;
+ struct mpi3_driver_info_layout *drv_info;
+ dma_addr_t data_dma;
+ u32 data_len = sizeof(*drv_info);
+ int retval = 0;
+ ktime_t current_time;
+
+ drv_info = dma_alloc_coherent(&mrioc->pdev->dev, data_len, &data_dma,
+ GFP_KERNEL);
+ if (!drv_info) {
+ retval = -1;
+ goto out;
+ }
+ drv_info->information_length = cpu_to_le32(data_len);
+ strncpy(drv_info->driver_signature, "Broadcom", sizeof(drv_info->driver_signature));
+ strncpy(drv_info->os_name, utsname()->sysname, sizeof(drv_info->os_name));
+ drv_info->os_name[sizeof(drv_info->os_name) - 1] = 0;
+ strncpy(drv_info->os_version, utsname()->release, sizeof(drv_info->os_version));
+ drv_info->os_version[sizeof(drv_info->os_version) - 1] = 0;
+ strncpy(drv_info->driver_name, MPI3MR_DRIVER_NAME, sizeof(drv_info->driver_name));
+ strncpy(drv_info->driver_version, MPI3MR_DRIVER_VERSION, sizeof(drv_info->driver_version));
+ strncpy(drv_info->driver_release_date, MPI3MR_DRIVER_RELDATE, sizeof(drv_info->driver_release_date));
+ drv_info->driver_capabilities = 0;
+ memcpy((u8 *)&mrioc->driver_info, (u8 *)drv_info,
+ sizeof(mrioc->driver_info));
+
+ memset(&iocinit_req, 0, sizeof(iocinit_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Issue IOCInit: Init command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ iocinit_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ iocinit_req.function = MPI3_FUNCTION_IOC_INIT;
+ iocinit_req.mpi_version.mpi3_version.dev = MPI3_VERSION_DEV;
+ iocinit_req.mpi_version.mpi3_version.unit = MPI3_VERSION_UNIT;
+ iocinit_req.mpi_version.mpi3_version.major = MPI3_VERSION_MAJOR;
+ iocinit_req.mpi_version.mpi3_version.minor = MPI3_VERSION_MINOR;
+ iocinit_req.who_init = MPI3_WHOINIT_HOST_DRIVER;
+ iocinit_req.reply_free_queue_depth = cpu_to_le16(mrioc->reply_free_qsz);
+ iocinit_req.reply_free_queue_address =
+ cpu_to_le64(mrioc->reply_free_q_dma);
+ iocinit_req.sense_buffer_length = cpu_to_le16(MPI3MR_SENSEBUF_SZ);
+ iocinit_req.sense_buffer_free_queue_depth =
+ cpu_to_le16(mrioc->sense_buf_q_sz);
+ iocinit_req.sense_buffer_free_queue_address =
+ cpu_to_le64(mrioc->sense_buf_q_dma);
+ iocinit_req.driver_information_address = cpu_to_le64(data_dma);
+
+ current_time = ktime_get_real();
+ iocinit_req.time_stamp = cpu_to_le64(ktime_to_ms(current_time));
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &iocinit_req,
+ sizeof(iocinit_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "Issue IOCInit: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ mpi3mr_set_diagsave(mrioc);
+ mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT,
+ MPI3MR_RESET_FROM_IOCINIT_TIMEOUT);
+ mrioc->unrecoverable = 1;
+ ioc_err(mrioc, "Issue IOCInit: command timed out\n");
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "Issue IOCInit: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+
+out:
+ if (drv_info)
+ dma_free_coherent(&mrioc->pdev->dev, data_len, drv_info,
+ data_dma);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_unmask_events - Unmask events in event mask bitmap
+ * @mrioc: Adapter instance reference
+ * @event: MPI event ID
+ *
+ * Un mask the specific event by resetting the event_mask
+ * bitmap.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static void mpi3mr_unmask_events(struct mpi3mr_ioc *mrioc, u16 event)
+{
+ u32 desired_event;
+ u8 word;
+
+ if (event >= 128)
+ return;
+
+ desired_event = (1 << (event % 32));
+ word = event / 32;
+
+ mrioc->event_masks[word] &= ~desired_event;
+}
+
+/**
+ * mpi3mr_issue_event_notification - Send event notification
+ * @mrioc: Adapter instance reference
+ *
+ * Issue event notification MPI request through admin queue and
+ * wait for the completion of it or time out.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+static int mpi3mr_issue_event_notification(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3_event_notification_request evtnotify_req;
+ int retval = 0;
+ u8 i;
+
+ memset(&evtnotify_req, 0, sizeof(evtnotify_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Issue EvtNotify: Init command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ evtnotify_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ evtnotify_req.function = MPI3_FUNCTION_EVENT_NOTIFICATION;
+ for (i = 0; i < MPI3_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
+ evtnotify_req.event_masks[i] =
+ cpu_to_le32(mrioc->event_masks[i]);
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &evtnotify_req,
+ sizeof(evtnotify_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "Issue EvtNotify: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "Issue EvtNotify: command timed out\n");
+ mpi3mr_set_diagsave(mrioc);
+ mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT,
+ MPI3MR_RESET_FROM_EVTNOTIFY_TIMEOUT);
+ mrioc->unrecoverable = 1;
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "Issue EvtNotify: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+out:
+ return retval;
+}
+
+/**
+ * mpi3mr_send_event_ack - Send event acknowledgment
+ * @mrioc: Adapter instance reference
+ * @event: MPI3 event ID
+ * @event_ctx: Event context
+ *
+ * Send event acknowledgment through admin queue and wait for
+ * it to complete.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+int mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event,
+ u32 event_ctx)
+{
+ struct mpi3_event_ack_request evtack_req;
+ int retval = 0;
+
+ memset(&evtack_req, 0, sizeof(evtack_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Send EvtAck: Init command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ evtack_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ evtack_req.function = MPI3_FUNCTION_EVENT_ACK;
+ evtack_req.event = event;
+ evtack_req.event_context = cpu_to_le32(event_ctx);
+
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &evtack_req,
+ sizeof(evtack_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "Send EvtAck: Admin Post failed\n");
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "Issue EvtNotify: command timed out\n");
+ mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_EVTACK_TIMEOUT, 1);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "Send EvtAck: Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+out:
+ return retval;
+}
+
+/**
+ * mpi3mr_alloc_chain_bufs - Allocate chain buffers
+ * @mrioc: Adapter instance reference
+ *
+ * Allocate chain buffers and set a bitmap to indicate free
+ * chain buffers. Chain buffers are used to pass the SGE
+ * information along with MPI3 SCSI IO requests for host I/O.
+ *
+ * Return: 0 on success, non-zero on failure
+ */
+static int mpi3mr_alloc_chain_bufs(struct mpi3mr_ioc *mrioc)
+{
+ int retval = 0;
+ u32 sz, i;
+ u16 num_chains;
+
+ num_chains = mrioc->max_host_ios / MPI3MR_CHAINBUF_FACTOR;
+
+ if (prot_mask & (SHOST_DIX_TYPE0_PROTECTION
+ | SHOST_DIX_TYPE1_PROTECTION
+ | SHOST_DIX_TYPE2_PROTECTION
+ | SHOST_DIX_TYPE3_PROTECTION))
+ num_chains += (num_chains / MPI3MR_CHAINBUFDIX_FACTOR);
+
+ mrioc->chain_buf_count = num_chains;
+ sz = sizeof(struct chain_element) * num_chains;
+ mrioc->chain_sgl_list = kzalloc(sz, GFP_KERNEL);
+ if (!mrioc->chain_sgl_list)
+ goto out_failed;
+
+ sz = MPI3MR_PAGE_SIZE_4K;
+ mrioc->chain_buf_pool = dma_pool_create("chain_buf pool",
+ &mrioc->pdev->dev, sz, 16, 0);
+ if (!mrioc->chain_buf_pool) {
+ ioc_err(mrioc, "chain buf pool: dma_pool_create failed\n");
+ goto out_failed;
+ }
+
+ for (i = 0; i < num_chains; i++) {
+ mrioc->chain_sgl_list[i].addr =
+ dma_pool_zalloc(mrioc->chain_buf_pool, GFP_KERNEL,
+ &mrioc->chain_sgl_list[i].dma_addr);
+
+ if (!mrioc->chain_sgl_list[i].addr)
+ goto out_failed;
+ }
+ mrioc->chain_bitmap_sz = num_chains / 8;
+ if (num_chains % 8)
+ mrioc->chain_bitmap_sz++;
+ mrioc->chain_bitmap = kzalloc(mrioc->chain_bitmap_sz, GFP_KERNEL);
+ if (!mrioc->chain_bitmap)
+ goto out_failed;
+ return retval;
+out_failed:
+ retval = -1;
+ return retval;
+}
+
+/**
+ * mpi3mr_port_enable_complete - Mark port enable complete
+ * @mrioc: Adapter instance reference
+ * @drv_cmd: Internal command tracker
+ *
+ * Call back for asynchronous port enable request sets the
+ * driver command to indicate port enable request is complete.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_port_enable_complete(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd)
+{
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ drv_cmd->callback = NULL;
+ mrioc->scan_failed = drv_cmd->ioc_status;
+ mrioc->scan_started = 0;
+}
+
+/**
+ * mpi3mr_issue_port_enable - Issue Port Enable
+ * @mrioc: Adapter instance reference
+ * @async: Flag to wait for completion or not
+ *
+ * Issue Port Enable MPI request through admin queue and if the
+ * async flag is not set wait for the completion of the port
+ * enable or time out.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+int mpi3mr_issue_port_enable(struct mpi3mr_ioc *mrioc, u8 async)
+{
+ struct mpi3_port_enable_request pe_req;
+ int retval = 0;
+ u32 pe_timeout = MPI3MR_PORTENABLE_TIMEOUT;
+
+ memset(&pe_req, 0, sizeof(pe_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "Issue PortEnable: Init command is in use\n");
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ goto out;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ if (async) {
+ mrioc->init_cmds.is_waiting = 0;
+ mrioc->init_cmds.callback = mpi3mr_port_enable_complete;
+ } else {
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ init_completion(&mrioc->init_cmds.done);
+ }
+ pe_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ pe_req.function = MPI3_FUNCTION_PORT_ENABLE;
+
+ retval = mpi3mr_admin_request_post(mrioc, &pe_req, sizeof(pe_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "Issue PortEnable: Admin Post failed\n");
+ goto out_unlock;
+ }
+ if (!async) {
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (pe_timeout * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "Issue PortEnable: command timed out\n");
+ retval = -1;
+ mrioc->scan_failed = MPI3_IOCSTATUS_INTERNAL_ERROR;
+ mpi3mr_set_diagsave(mrioc);
+ mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT,
+ MPI3MR_RESET_FROM_PE_TIMEOUT);
+ mrioc->unrecoverable = 1;
+ goto out_unlock;
+ }
+ mpi3mr_port_enable_complete(mrioc, &mrioc->init_cmds);
+ }
+out_unlock:
+ mutex_unlock(&mrioc->init_cmds.mutex);
+out:
+ return retval;
+}
+
+/* Protocol type to name mapper structure*/
+static const struct {
+ u8 protocol;
+ char *name;
+} mpi3mr_protocols[] = {
+ { MPI3_IOCFACTS_PROTOCOL_SCSI_INITIATOR, "Initiator" },
+ { MPI3_IOCFACTS_PROTOCOL_SCSI_TARGET, "Target" },
+ { MPI3_IOCFACTS_PROTOCOL_NVME, "NVMe attachment" },
+};
+
+/* Capability to name mapper structure*/
+static const struct {
+ u32 capability;
+ char *name;
+} mpi3mr_capabilities[] = {
+ { MPI3_IOCFACTS_CAPABILITY_RAID_CAPABLE, "RAID" },
+};
+
+/**
+ * mpi3mr_print_ioc_info - Display controller information
+ * @mrioc: Adapter instance reference
+ *
+ * Display controller personalit, capability, supported
+ * protocols etc.
+ *
+ * Return: Nothing
+ */
+static void
+mpi3mr_print_ioc_info(struct mpi3mr_ioc *mrioc)
+{
+ int i = 0, bytes_wrote = 0;
+ char personality[16];
+ char protocol[50] = {0};
+ char capabilities[100] = {0};
+ bool is_string_nonempty = false;
+ struct mpi3mr_compimg_ver *fwver = &mrioc->facts.fw_ver;
+
+ switch (mrioc->facts.personality) {
+ case MPI3_IOCFACTS_FLAGS_PERSONALITY_EHBA:
+ strncpy(personality, "Enhanced HBA", sizeof(personality));
+ break;
+ case MPI3_IOCFACTS_FLAGS_PERSONALITY_RAID_DDR:
+ strncpy(personality, "RAID", sizeof(personality));
+ break;
+ default:
+ strncpy(personality, "Unknown", sizeof(personality));
+ break;
+ }
+
+ ioc_info(mrioc, "Running in %s Personality", personality);
+
+ ioc_info(mrioc, "FW version(%d.%d.%d.%d.%d.%d)\n",
+ fwver->gen_major, fwver->gen_minor, fwver->ph_major,
+ fwver->ph_minor, fwver->cust_id, fwver->build_num);
+
+ for (i = 0; i < ARRAY_SIZE(mpi3mr_protocols); i++) {
+ if (mrioc->facts.protocol_flags &
+ mpi3mr_protocols[i].protocol) {
+ if (is_string_nonempty &&
+ (bytes_wrote < sizeof(protocol)))
+ bytes_wrote += snprintf(protocol + bytes_wrote,
+ (sizeof(protocol) - bytes_wrote), ",");
+
+ if (bytes_wrote < sizeof(protocol))
+ bytes_wrote += snprintf(protocol + bytes_wrote,
+ (sizeof(protocol) - bytes_wrote), "%s",
+ mpi3mr_protocols[i].name);
+ is_string_nonempty = true;
+ }
+ }
+
+ bytes_wrote = 0;
+ is_string_nonempty = false;
+ for (i = 0; i < ARRAY_SIZE(mpi3mr_capabilities); i++) {
+ if (mrioc->facts.protocol_flags &
+ mpi3mr_capabilities[i].capability) {
+ if (is_string_nonempty &&
+ (bytes_wrote < sizeof(capabilities)))
+ bytes_wrote += snprintf(capabilities + bytes_wrote,
+ (sizeof(capabilities) - bytes_wrote), ",");
+
+ if (bytes_wrote < sizeof(capabilities))
+ bytes_wrote += snprintf(capabilities + bytes_wrote,
+ (sizeof(capabilities) - bytes_wrote), "%s",
+ mpi3mr_capabilities[i].name);
+ is_string_nonempty = true;
+ }
+ }
+
+ ioc_info(mrioc, "Protocol=(%s), Capabilities=(%s)\n",
+ protocol, capabilities);
+}
+
+/**
+ * mpi3mr_cleanup_resources - Free PCI resources
+ * @mrioc: Adapter instance reference
+ *
+ * Unmap PCI device memory and disable PCI device.
+ *
+ * Return: 0 on success and non-zero on failure.
+ */
+void mpi3mr_cleanup_resources(struct mpi3mr_ioc *mrioc)
+{
+ struct pci_dev *pdev = mrioc->pdev;
+
+ mpi3mr_cleanup_isr(mrioc);
+
+ if (mrioc->sysif_regs) {
+ iounmap((void __iomem *)mrioc->sysif_regs);
+ mrioc->sysif_regs = NULL;
+ }
+
+ if (pci_is_enabled(pdev)) {
+ if (mrioc->bars)
+ pci_release_selected_regions(pdev, mrioc->bars);
+ pci_disable_device(pdev);
+ }
+}
+
+/**
+ * mpi3mr_setup_resources - Enable PCI resources
+ * @mrioc: Adapter instance reference
+ *
+ * Enable PCI device memory, MSI-x registers and set DMA mask.
+ *
+ * Return: 0 on success and non-zero on failure.
+ */
+int mpi3mr_setup_resources(struct mpi3mr_ioc *mrioc)
+{
+ struct pci_dev *pdev = mrioc->pdev;
+ u32 memap_sz = 0;
+ int i, retval = 0, capb = 0;
+ u16 message_control;
+ u64 dma_mask = mrioc->dma_mask ? mrioc->dma_mask :
+ (((dma_get_required_mask(&pdev->dev) > DMA_BIT_MASK(32)) &&
+ (sizeof(dma_addr_t) > 4)) ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
+
+ if (pci_enable_device_mem(pdev)) {
+ ioc_err(mrioc, "pci_enable_device_mem: failed\n");
+ retval = -ENODEV;
+ goto out_failed;
+ }
+
+ capb = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
+ if (!capb) {
+ ioc_err(mrioc, "Unable to find MSI-X Capabilities\n");
+ retval = -ENODEV;
+ goto out_failed;
+ }
+ mrioc->bars = pci_select_bars(pdev, IORESOURCE_MEM);
+
+ if (pci_request_selected_regions(pdev, mrioc->bars,
+ mrioc->driver_name)) {
+ ioc_err(mrioc, "pci_request_selected_regions: failed\n");
+ retval = -ENODEV;
+ goto out_failed;
+ }
+
+ for (i = 0; (i < DEVICE_COUNT_RESOURCE); i++) {
+ if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) {
+ mrioc->sysif_regs_phys = pci_resource_start(pdev, i);
+ memap_sz = pci_resource_len(pdev, i);
+ mrioc->sysif_regs =
+ ioremap(mrioc->sysif_regs_phys, memap_sz);
+ break;
+ }
+ }
+
+ pci_set_master(pdev);
+
+ retval = dma_set_mask_and_coherent(&pdev->dev, dma_mask);
+ if (retval) {
+ if (dma_mask != DMA_BIT_MASK(32)) {
+ ioc_warn(mrioc, "Setting 64 bit DMA mask failed\n");
+ dma_mask = DMA_BIT_MASK(32);
+ retval = dma_set_mask_and_coherent(&pdev->dev,
+ dma_mask);
+ }
+ if (retval) {
+ mrioc->dma_mask = 0;
+ ioc_err(mrioc, "Setting 32 bit DMA mask also failed\n");
+ goto out_failed;
+ }
+ }
+ mrioc->dma_mask = dma_mask;
+
+ if (!mrioc->sysif_regs) {
+ ioc_err(mrioc,
+ "Unable to map adapter memory or resource not found\n");
+ retval = -EINVAL;
+ goto out_failed;
+ }
+
+ pci_read_config_word(pdev, capb + 2, &message_control);
+ mrioc->msix_count = (message_control & 0x3FF) + 1;
+
+ pci_save_state(pdev);
+
+ pci_set_drvdata(pdev, mrioc->shost);
+
+ mpi3mr_ioc_disable_intr(mrioc);
+
+ ioc_info(mrioc, "iomem(0x%016llx), mapped(0x%p), size(%d)\n",
+ (unsigned long long)mrioc->sysif_regs_phys,
+ mrioc->sysif_regs, memap_sz);
+ ioc_info(mrioc, "Number of MSI-X vectors found in capabilities: (%d)\n",
+ mrioc->msix_count);
+ return retval;
+
+out_failed:
+ mpi3mr_cleanup_resources(mrioc);
+ return retval;
+}
+
+/**
+ * mpi3mr_init_ioc - Initialize the controller
+ * @mrioc: Adapter instance reference
+ * @re_init: Flag to indicate is this fresh init or re-init
+ *
+ * This the controller initialization routine, executed either
+ * after soft reset or from pci probe callback.
+ * Setup the required resources, memory map the controller
+ * registers, create admin and operational reply queue pairs,
+ * allocate required memory for reply pool, sense buffer pool,
+ * issue IOC init request to the firmware, unmask the events and
+ * issue port enable to discover SAS/SATA/NVMe devies and RAID
+ * volumes.
+ *
+ * Return: 0 on success and non-zero on failure.
+ */
+int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc, u8 re_init)
+{
+ int retval = 0;
+ enum mpi3mr_iocstate ioc_state;
+ u64 base_info;
+ u32 timeout;
+ u32 ioc_status, ioc_config, i;
+ struct mpi3_ioc_facts_data facts_data;
+
+ mrioc->irqpoll_sleep = MPI3MR_IRQ_POLL_SLEEP;
+ mrioc->change_count = 0;
+ if (!re_init) {
+ mrioc->cpu_count = num_online_cpus();
+ retval = mpi3mr_setup_resources(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Failed to setup resources:error %d\n",
+ retval);
+ goto out_nocleanup;
+ }
+ }
+
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+
+ ioc_info(mrioc, "SOD status %x configuration %x\n",
+ ioc_status, ioc_config);
+
+ base_info = lo_hi_readq(&mrioc->sysif_regs->ioc_information);
+ ioc_info(mrioc, "SOD base_info %llx\n", base_info);
+
+ /*The timeout value is in 2sec unit, changing it to seconds*/
+ mrioc->ready_timeout =
+ ((base_info & MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_MASK) >>
+ MPI3_SYSIF_IOC_INFO_LOW_TIMEOUT_SHIFT) * 2;
+
+ ioc_info(mrioc, "IOC ready timeout %d\n", mrioc->ready_timeout);
+
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ ioc_info(mrioc, "IOC in %s state during detection\n",
+ mpi3mr_iocstate_name(ioc_state));
+
+ if (ioc_state == MRIOC_STATE_BECOMING_READY ||
+ ioc_state == MRIOC_STATE_RESET_REQUESTED) {
+ timeout = mrioc->ready_timeout * 10;
+ do {
+ msleep(100);
+ } while (--timeout);
+
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ ioc_info(mrioc,
+ "IOC in %s state after waiting for reset time\n",
+ mpi3mr_iocstate_name(ioc_state));
+ }
+
+ if (ioc_state == MRIOC_STATE_READY) {
+ retval = mpi3mr_issue_and_process_mur(mrioc,
+ MPI3MR_RESET_FROM_BRINGUP);
+ if (retval) {
+ ioc_err(mrioc, "Failed to MU reset IOC error %d\n",
+ retval);
+ }
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ }
+ if (ioc_state != MRIOC_STATE_RESET) {
+ mpi3mr_print_fault_info(mrioc);
+ retval = mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET,
+ MPI3MR_RESET_FROM_BRINGUP);
+ if (retval) {
+ ioc_err(mrioc,
+ "%s :Failed to soft reset IOC error %d\n",
+ __func__, retval);
+ goto out_failed;
+ }
+ }
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+ if (ioc_state != MRIOC_STATE_RESET) {
+ ioc_err(mrioc, "Cannot bring IOC to reset state\n");
+ goto out_failed;
+ }
+
+ retval = mpi3mr_setup_admin_qpair(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Failed to setup admin Qs: error %d\n",
+ retval);
+ goto out_failed;
+ }
+
+ retval = mpi3mr_bring_ioc_ready(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Failed to bring ioc ready: error %d\n",
+ retval);
+ goto out_failed;
+ }
+
+ if (!re_init) {
+ retval = mpi3mr_setup_isr(mrioc, 1);
+ if (retval) {
+ ioc_err(mrioc, "Failed to setup ISR error %d\n",
+ retval);
+ goto out_failed;
+ }
+ } else
+ mpi3mr_ioc_enable_intr(mrioc);
+
+ retval = mpi3mr_issue_iocfacts(mrioc, &facts_data);
+ if (retval) {
+ ioc_err(mrioc, "Failed to Issue IOC Facts %d\n",
+ retval);
+ goto out_failed;
+ }
+
+ mpi3mr_process_factsdata(mrioc, &facts_data);
+ if (!re_init) {
+ retval = mpi3mr_check_reset_dma_mask(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Resetting dma mask failed %d\n",
+ retval);
+ goto out_failed;
+ }
+ }
+
+ mpi3mr_print_ioc_info(mrioc);
+
+ retval = mpi3mr_alloc_reply_sense_bufs(mrioc);
+ if (retval) {
+ ioc_err(mrioc,
+ "%s :Failed to allocated reply sense buffers %d\n",
+ __func__, retval);
+ goto out_failed;
+ }
+
+ if (!re_init) {
+ retval = mpi3mr_alloc_chain_bufs(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Failed to allocated chain buffers %d\n",
+ retval);
+ goto out_failed;
+ }
+ }
+
+ retval = mpi3mr_issue_iocinit(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Failed to Issue IOC Init %d\n",
+ retval);
+ goto out_failed;
+ }
+ mrioc->reply_free_queue_host_index = mrioc->num_reply_bufs;
+ writel(mrioc->reply_free_queue_host_index,
+ &mrioc->sysif_regs->reply_free_host_index);
+
+ mrioc->sbq_host_index = mrioc->num_sense_bufs;
+ writel(mrioc->sbq_host_index,
+ &mrioc->sysif_regs->sense_buffer_free_host_index);
+
+ if (!re_init) {
+ retval = mpi3mr_setup_isr(mrioc, 0);
+ if (retval) {
+ ioc_err(mrioc, "Failed to re-setup ISR, error %d\n",
+ retval);
+ goto out_failed;
+ }
+ }
+
+ retval = mpi3mr_create_op_queues(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Failed to create OpQueues error %d\n",
+ retval);
+ goto out_failed;
+ }
+
+ if (re_init &&
+ (mrioc->shost->nr_hw_queues > mrioc->num_op_reply_q)) {
+ ioc_err(mrioc,
+ "Cannot create minimum number of OpQueues expected:%d created:%d\n",
+ mrioc->shost->nr_hw_queues, mrioc->num_op_reply_q);
+ goto out_failed;
+ }
+
+ for (i = 0; i < MPI3_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
+ mrioc->event_masks[i] = -1;
+
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_DEVICE_ADDED);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_DEVICE_INFO_CHANGED);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_DEVICE_STATUS_CHANGE);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_DISCOVERY);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_SAS_BROADCAST_PRIMITIVE);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_PCIE_ENUMERATION);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_CABLE_MGMT);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_ENERGY_PACK_CHANGE);
+
+ retval = mpi3mr_issue_event_notification(mrioc);
+ if (retval) {
+ ioc_err(mrioc, "Failed to issue event notification %d\n",
+ retval);
+ goto out_failed;
+ }
+
+ if (re_init) {
+ ioc_info(mrioc, "Issuing Port Enable\n");
+ retval = mpi3mr_issue_port_enable(mrioc, 0);
+ if (retval) {
+ ioc_err(mrioc, "Failed to issue port enable %d\n",
+ retval);
+ goto out_failed;
+ }
+ }
+ return retval;
+
+out_failed:
+ mpi3mr_cleanup_ioc(mrioc, re_init);
+out_nocleanup:
+ return retval;
+}
+
+/**
+ * mpi3mr_memset_op_reply_q_buffers - memset the operational reply queue's
+ * segments
+ * @mrioc: Adapter instance reference
+ * @qidx: Operational reply queue index
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_memset_op_reply_q_buffers(struct mpi3mr_ioc *mrioc, u16 qidx)
+{
+ struct op_reply_qinfo *op_reply_q = mrioc->op_reply_qinfo + qidx;
+ struct segments *segments;
+ int i, size;
+
+ if (!op_reply_q->q_segments)
+ return;
+
+ size = op_reply_q->segment_qd * mrioc->op_reply_desc_sz;
+ segments = op_reply_q->q_segments;
+ for (i = 0; i < op_reply_q->num_segments; i++)
+ memset(segments[i].segment, 0, size);
+}
+
+/**
+ * mpi3mr_memset_op_req_q_buffers - memset the operational request queue's
+ * segments
+ * @mrioc: Adapter instance reference
+ * @qidx: Operational request queue index
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_memset_op_req_q_buffers(struct mpi3mr_ioc *mrioc, u16 qidx)
+{
+ struct op_req_qinfo *op_req_q = mrioc->req_qinfo + qidx;
+ struct segments *segments;
+ int i, size;
+
+ if (!op_req_q->q_segments)
+ return;
+
+ size = op_req_q->segment_qd * mrioc->facts.op_req_sz;
+ segments = op_req_q->q_segments;
+ for (i = 0; i < op_req_q->num_segments; i++)
+ memset(segments[i].segment, 0, size);
+}
+
+/**
+ * mpi3mr_memset_buffers - memset memory for a controller
+ * @mrioc: Adapter instance reference
+ *
+ * clear all the memory allocated for a controller, typically
+ * called post reset to reuse the memory allocated during the
+ * controller init.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_memset_buffers(struct mpi3mr_ioc *mrioc)
+{
+ u16 i;
+
+ memset(mrioc->admin_req_base, 0, mrioc->admin_req_q_sz);
+ memset(mrioc->admin_reply_base, 0, mrioc->admin_reply_q_sz);
+
+ memset(mrioc->init_cmds.reply, 0, sizeof(*mrioc->init_cmds.reply));
+ memset(mrioc->host_tm_cmds.reply, 0,
+ sizeof(*mrioc->host_tm_cmds.reply));
+ for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++)
+ memset(mrioc->dev_rmhs_cmds[i].reply, 0,
+ sizeof(*mrioc->dev_rmhs_cmds[i].reply));
+ memset(mrioc->removepend_bitmap, 0, mrioc->dev_handle_bitmap_sz);
+ memset(mrioc->devrem_bitmap, 0, mrioc->devrem_bitmap_sz);
+
+ for (i = 0; i < mrioc->num_queues; i++) {
+ mrioc->op_reply_qinfo[i].qid = 0;
+ mrioc->op_reply_qinfo[i].ci = 0;
+ mrioc->op_reply_qinfo[i].num_replies = 0;
+ mrioc->op_reply_qinfo[i].ephase = 0;
+ atomic_set(&mrioc->op_reply_qinfo[i].pend_ios, 0);
+ atomic_set(&mrioc->op_reply_qinfo[i].in_use, 0);
+ mpi3mr_memset_op_reply_q_buffers(mrioc, i);
+
+ mrioc->req_qinfo[i].ci = 0;
+ mrioc->req_qinfo[i].pi = 0;
+ mrioc->req_qinfo[i].num_requests = 0;
+ mrioc->req_qinfo[i].qid = 0;
+ mrioc->req_qinfo[i].reply_qid = 0;
+ spin_lock_init(&mrioc->req_qinfo[i].q_lock);
+ mpi3mr_memset_op_req_q_buffers(mrioc, i);
+ }
+}
+
+/**
+ * mpi3mr_free_mem - Free memory allocated for a controller
+ * @mrioc: Adapter instance reference
+ *
+ * Free all the memory allocated for a controller.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_free_mem(struct mpi3mr_ioc *mrioc)
+{
+ u16 i;
+ struct mpi3mr_intr_info *intr_info;
+
+ if (mrioc->sense_buf_pool) {
+ if (mrioc->sense_buf)
+ dma_pool_free(mrioc->sense_buf_pool, mrioc->sense_buf,
+ mrioc->sense_buf_dma);
+ dma_pool_destroy(mrioc->sense_buf_pool);
+ mrioc->sense_buf = NULL;
+ mrioc->sense_buf_pool = NULL;
+ }
+ if (mrioc->sense_buf_q_pool) {
+ if (mrioc->sense_buf_q)
+ dma_pool_free(mrioc->sense_buf_q_pool,
+ mrioc->sense_buf_q, mrioc->sense_buf_q_dma);
+ dma_pool_destroy(mrioc->sense_buf_q_pool);
+ mrioc->sense_buf_q = NULL;
+ mrioc->sense_buf_q_pool = NULL;
+ }
+
+ if (mrioc->reply_buf_pool) {
+ if (mrioc->reply_buf)
+ dma_pool_free(mrioc->reply_buf_pool, mrioc->reply_buf,
+ mrioc->reply_buf_dma);
+ dma_pool_destroy(mrioc->reply_buf_pool);
+ mrioc->reply_buf = NULL;
+ mrioc->reply_buf_pool = NULL;
+ }
+ if (mrioc->reply_free_q_pool) {
+ if (mrioc->reply_free_q)
+ dma_pool_free(mrioc->reply_free_q_pool,
+ mrioc->reply_free_q, mrioc->reply_free_q_dma);
+ dma_pool_destroy(mrioc->reply_free_q_pool);
+ mrioc->reply_free_q = NULL;
+ mrioc->reply_free_q_pool = NULL;
+ }
+
+ for (i = 0; i < mrioc->num_op_req_q; i++)
+ mpi3mr_free_op_req_q_segments(mrioc, i);
+
+ for (i = 0; i < mrioc->num_op_reply_q; i++)
+ mpi3mr_free_op_reply_q_segments(mrioc, i);
+
+ for (i = 0; i < mrioc->intr_info_count; i++) {
+ intr_info = mrioc->intr_info + i;
+ if (intr_info)
+ intr_info->op_reply_q = NULL;
+ }
+
+ kfree(mrioc->req_qinfo);
+ mrioc->req_qinfo = NULL;
+ mrioc->num_op_req_q = 0;
+
+ kfree(mrioc->op_reply_qinfo);
+ mrioc->op_reply_qinfo = NULL;
+ mrioc->num_op_reply_q = 0;
+
+ kfree(mrioc->init_cmds.reply);
+ mrioc->init_cmds.reply = NULL;
+
+ kfree(mrioc->host_tm_cmds.reply);
+ mrioc->host_tm_cmds.reply = NULL;
+
+ kfree(mrioc->removepend_bitmap);
+ mrioc->removepend_bitmap = NULL;
+
+ kfree(mrioc->devrem_bitmap);
+ mrioc->devrem_bitmap = NULL;
+
+ kfree(mrioc->chain_bitmap);
+ mrioc->chain_bitmap = NULL;
+
+ for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) {
+ kfree(mrioc->dev_rmhs_cmds[i].reply);
+ mrioc->dev_rmhs_cmds[i].reply = NULL;
+ }
+
+ if (mrioc->chain_buf_pool) {
+ for (i = 0; i < mrioc->chain_buf_count; i++) {
+ if (mrioc->chain_sgl_list[i].addr) {
+ dma_pool_free(mrioc->chain_buf_pool,
+ mrioc->chain_sgl_list[i].addr,
+ mrioc->chain_sgl_list[i].dma_addr);
+ mrioc->chain_sgl_list[i].addr = NULL;
+ }
+ }
+ dma_pool_destroy(mrioc->chain_buf_pool);
+ mrioc->chain_buf_pool = NULL;
+ }
+
+ kfree(mrioc->chain_sgl_list);
+ mrioc->chain_sgl_list = NULL;
+
+ if (mrioc->admin_reply_base) {
+ dma_free_coherent(&mrioc->pdev->dev, mrioc->admin_reply_q_sz,
+ mrioc->admin_reply_base, mrioc->admin_reply_dma);
+ mrioc->admin_reply_base = NULL;
+ }
+ if (mrioc->admin_req_base) {
+ dma_free_coherent(&mrioc->pdev->dev, mrioc->admin_req_q_sz,
+ mrioc->admin_req_base, mrioc->admin_req_dma);
+ mrioc->admin_req_base = NULL;
+ }
+}
+
+/**
+ * mpi3mr_issue_ioc_shutdown - shutdown controller
+ * @mrioc: Adapter instance reference
+ *
+ * Send shutodwn notification to the controller and wait for the
+ * shutdown_timeout for it to be completed.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_issue_ioc_shutdown(struct mpi3mr_ioc *mrioc)
+{
+ u32 ioc_config, ioc_status;
+ u8 retval = 1;
+ u32 timeout = MPI3MR_DEFAULT_SHUTDOWN_TIME * 10;
+
+ ioc_info(mrioc, "Issuing shutdown Notification\n");
+ if (mrioc->unrecoverable) {
+ ioc_warn(mrioc,
+ "IOC is unrecoverable shutdown is not issued\n");
+ return;
+ }
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if ((ioc_status & MPI3_SYSIF_IOC_STATUS_SHUTDOWN_MASK)
+ == MPI3_SYSIF_IOC_STATUS_SHUTDOWN_IN_PROGRESS) {
+ ioc_info(mrioc, "shutdown already in progress\n");
+ return;
+ }
+
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+ ioc_config |= MPI3_SYSIF_IOC_CONFIG_SHUTDOWN_NORMAL;
+ ioc_config |= MPI3_SYSIF_IOC_CONFIG_DEVICE_SHUTDOWN;
+
+ writel(ioc_config, &mrioc->sysif_regs->ioc_configuration);
+
+ if (mrioc->facts.shutdown_timeout)
+ timeout = mrioc->facts.shutdown_timeout * 10;
+
+ do {
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ if ((ioc_status & MPI3_SYSIF_IOC_STATUS_SHUTDOWN_MASK)
+ == MPI3_SYSIF_IOC_STATUS_SHUTDOWN_COMPLETE) {
+ retval = 0;
+ break;
+ }
+ msleep(100);
+ } while (--timeout);
+
+ ioc_status = readl(&mrioc->sysif_regs->ioc_status);
+ ioc_config = readl(&mrioc->sysif_regs->ioc_configuration);
+
+ if (retval) {
+ if ((ioc_status & MPI3_SYSIF_IOC_STATUS_SHUTDOWN_MASK)
+ == MPI3_SYSIF_IOC_STATUS_SHUTDOWN_IN_PROGRESS)
+ ioc_warn(mrioc,
+ "shutdown still in progress after timeout\n");
+ }
+
+ ioc_info(mrioc,
+ "Base IOC Sts/Config after %s shutdown is (0x%x)/(0x%x)\n",
+ (!retval) ? "successful" : "failed", ioc_status,
+ ioc_config);
+}
+
+/**
+ * mpi3mr_cleanup_ioc - Cleanup controller
+ * @mrioc: Adapter instance reference
+ * @re_init: Cleanup due to a reinit or not
+ *
+ * controller cleanup handler, Message unit reset or soft reset
+ * and shutdown notification is issued to the controller and the
+ * associated memory resources are freed.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_cleanup_ioc(struct mpi3mr_ioc *mrioc, u8 re_init)
+{
+ enum mpi3mr_iocstate ioc_state;
+
+ if (!re_init)
+ mpi3mr_stop_watchdog(mrioc);
+
+ mpi3mr_ioc_disable_intr(mrioc);
+
+ ioc_state = mpi3mr_get_iocstate(mrioc);
+
+ if ((!mrioc->unrecoverable) && (!mrioc->reset_in_progress) &&
+ (ioc_state == MRIOC_STATE_READY)) {
+ if (mpi3mr_issue_and_process_mur(mrioc,
+ MPI3MR_RESET_FROM_CTLR_CLEANUP))
+ mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET,
+ MPI3MR_RESET_FROM_MUR_FAILURE);
+
+ if (!re_init)
+ mpi3mr_issue_ioc_shutdown(mrioc);
+ }
+
+ if (!re_init) {
+ mpi3mr_free_mem(mrioc);
+ mpi3mr_cleanup_resources(mrioc);
+ }
+}
+
+/**
+ * mpi3mr_drv_cmd_comp_reset - Flush a internal driver command
+ * @mrioc: Adapter instance reference
+ * @cmdptr: Internal command tracker
+ *
+ * Complete an internal driver commands with state indicating it
+ * is completed due to reset.
+ *
+ * Return: Nothing.
+ */
+static inline void mpi3mr_drv_cmd_comp_reset(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *cmdptr)
+{
+ if (cmdptr->state & MPI3MR_CMD_PENDING) {
+ cmdptr->state |= MPI3MR_CMD_RESET;
+ cmdptr->state &= ~MPI3MR_CMD_PENDING;
+ if (cmdptr->is_waiting) {
+ complete(&cmdptr->done);
+ cmdptr->is_waiting = 0;
+ } else if (cmdptr->callback)
+ cmdptr->callback(mrioc, cmdptr);
+ }
+}
+
+/**
+ * mpi3mr_flush_drv_cmds - Flush internaldriver commands
+ * @mrioc: Adapter instance reference
+ *
+ * Flush all internal driver commands post reset
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_flush_drv_cmds(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3mr_drv_cmd *cmdptr;
+ u8 i;
+
+ cmdptr = &mrioc->init_cmds;
+ mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+ cmdptr = &mrioc->host_tm_cmds;
+ mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+
+ for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++) {
+ cmdptr = &mrioc->dev_rmhs_cmds[i];
+ mpi3mr_drv_cmd_comp_reset(mrioc, cmdptr);
+ }
+}
+
+/**
+ * mpi3mr_diagfault_reset_handler - Diag fault reset handler
+ * @mrioc: Adapter instance reference
+ * @reset_reason: Reset reason code
+ *
+ * This is an handler for issuing diag fault reset from the
+ * applications through IOCTL path to stop the execution of the
+ * controller
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_diagfault_reset_handler(struct mpi3mr_ioc *mrioc,
+ u32 reset_reason)
+{
+ int retval = 0;
+
+ ioc_info(mrioc, "Entry: reason code: %s\n",
+ mpi3mr_reset_rc_name(reset_reason));
+ mrioc->reset_in_progress = 1;
+
+ mpi3mr_ioc_disable_intr(mrioc);
+
+ retval = mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, reset_reason);
+
+ if (retval) {
+ ioc_err(mrioc, "The diag fault reset failed: reason %d\n",
+ reset_reason);
+ mpi3mr_ioc_enable_intr(mrioc);
+ }
+ ioc_info(mrioc, "%s\n", ((retval == 0) ? "SUCCESS" : "FAILED"));
+ mrioc->reset_in_progress = 0;
+ return retval;
+}
+
+/**
+ * mpi3mr_soft_reset_handler - Reset the controller
+ * @mrioc: Adapter instance reference
+ * @reset_reason: Reset reason code
+ * @snapdump: Flag to generate snapdump in firmware or not
+ *
+ * This is an handler for recovering controller by issuing soft
+ * reset are diag fault reset. This is a blocking function and
+ * when one reset is executed if any other resets they will be
+ * blocked. All IOCTLs/IO will be blocked during the reset. If
+ * controller reset is successful then the controller will be
+ * reinitalized, otherwise the controller will be marked as not
+ * recoverable
+ *
+ * In snapdump bit is set, the controller is issued with diag
+ * fault reset so that the firmware can create a snap dump and
+ * post that the firmware will result in F000 fault and the
+ * driver will issue soft reset to recover from that.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc,
+ u32 reset_reason, u8 snapdump)
+{
+ int retval = 0, i;
+ unsigned long flags;
+ u32 host_diagnostic, timeout = MPI3_SYSIF_DIAG_SAVE_TIMEOUT * 10;
+
+ if (mrioc->fault_dbg) {
+ if (snapdump)
+ mpi3mr_set_diagsave(mrioc);
+ mpi3mr_kill_ioc(mrioc, reset_reason);
+ }
+
+ /*
+ * Block new resets until the currently executing one is finished and
+ * return the status of the existing reset for all blocked resets
+ */
+ if (!mutex_trylock(&mrioc->reset_mutex)) {
+ ioc_info(mrioc, "Another reset in progress\n");
+ return -1;
+ }
+ mrioc->reset_in_progress = 1;
+
+ if ((!snapdump) && (reset_reason != MPI3MR_RESET_FROM_FAULT_WATCH) &&
+ (reset_reason != MPI3MR_RESET_FROM_CIACTIV_FAULT)) {
+ for (i = 0; i < MPI3_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
+ mrioc->event_masks[i] = -1;
+
+ retval = mpi3mr_issue_event_notification(mrioc);
+
+ if (retval) {
+ ioc_err(mrioc,
+ "Failed to turn off events prior to reset %d\n",
+ retval);
+ }
+ }
+
+ mpi3mr_wait_for_host_io(mrioc, MPI3MR_RESET_HOST_IOWAIT_TIMEOUT);
+
+ mpi3mr_ioc_disable_intr(mrioc);
+
+ if (snapdump) {
+ mpi3mr_set_diagsave(mrioc);
+ retval = mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, reset_reason);
+ if (!retval) {
+ do {
+ host_diagnostic =
+ readl(&mrioc->sysif_regs->host_diagnostic);
+ if (!(host_diagnostic &
+ MPI3_SYSIF_HOST_DIAG_SAVE_IN_PROGRESS))
+ break;
+ msleep(100);
+ } while (--timeout);
+ }
+ }
+
+ retval = mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET, reset_reason);
+ if (retval) {
+ ioc_err(mrioc, "Failed to issue soft reset to the ioc\n");
+ goto out;
+ }
+
+ mpi3mr_flush_delayed_rmhs_list(mrioc);
+ mpi3mr_flush_drv_cmds(mrioc);
+ memset(mrioc->devrem_bitmap, 0, mrioc->devrem_bitmap_sz);
+ memset(mrioc->removepend_bitmap, 0, mrioc->dev_handle_bitmap_sz);
+ mpi3mr_cleanup_fwevt_list(mrioc);
+ mpi3mr_flush_host_io(mrioc);
+ mpi3mr_invalidate_devhandles(mrioc);
+ mpi3mr_memset_buffers(mrioc);
+ retval = mpi3mr_init_ioc(mrioc, 1);
+ if (retval) {
+ pr_err(IOCNAME "reinit after soft reset failed: reason %d\n",
+ mrioc->name, reset_reason);
+ goto out;
+ }
+ ssleep(10);
+
+out:
+ if (!retval) {
+ mrioc->reset_in_progress = 0;
+ scsi_unblock_requests(mrioc->shost);
+ mpi3mr_rfresh_tgtdevs(mrioc);
+ mrioc->ts_update_counter = 0;
+ spin_lock_irqsave(&mrioc->watchdog_lock, flags);
+ if (mrioc->watchdog_work_q)
+ queue_delayed_work(mrioc->watchdog_work_q,
+ &mrioc->watchdog_work,
+ msecs_to_jiffies(MPI3MR_WATCHDOG_INTERVAL));
+ spin_unlock_irqrestore(&mrioc->watchdog_lock, flags);
+ } else {
+ mpi3mr_issue_reset(mrioc,
+ MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, reset_reason);
+ mrioc->unrecoverable = 1;
+ mrioc->reset_in_progress = 0;
+ retval = -1;
+ }
+
+ mutex_unlock(&mrioc->reset_mutex);
+ ioc_info(mrioc, "%s\n", ((retval == 0) ? "SUCCESS" : "FAILED"));
+ return retval;
+}
diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c
new file mode 100644
index 000000000000..a54aa009ec5a
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi3mr_os.c
@@ -0,0 +1,4049 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Broadcom MPI3 Storage Controllers
+ *
+ * Copyright (C) 2017-2021 Broadcom Inc.
+ * (mailto: mpi3mr-linuxdrv.pdl@broadcom.com)
+ *
+ */
+
+#include "mpi3mr.h"
+
+/* global driver scop variables */
+LIST_HEAD(mrioc_list);
+DEFINE_SPINLOCK(mrioc_list_lock);
+static int mrioc_ids;
+static int warn_non_secure_ctlr;
+
+MODULE_AUTHOR(MPI3MR_DRIVER_AUTHOR);
+MODULE_DESCRIPTION(MPI3MR_DRIVER_DESC);
+MODULE_LICENSE(MPI3MR_DRIVER_LICENSE);
+MODULE_VERSION(MPI3MR_DRIVER_VERSION);
+
+/* Module parameters*/
+int prot_mask = -1;
+module_param(prot_mask, int, 0);
+MODULE_PARM_DESC(prot_mask, "Host protection capabilities mask, def=0x07");
+
+int prot_guard_mask = 3;
+module_param(prot_guard_mask, int, 0);
+MODULE_PARM_DESC(prot_guard_mask, " Host protection guard mask, def=3");
+int logging_level;
+module_param(logging_level, int, 0);
+MODULE_PARM_DESC(logging_level,
+ " bits for enabling additional logging info (default=0)");
+
+/* Forward declarations*/
+/**
+ * mpi3mr_host_tag_for_scmd - Get host tag for a scmd
+ * @mrioc: Adapter instance reference
+ * @scmd: SCSI command reference
+ *
+ * Calculate the host tag based on block tag for a given scmd.
+ *
+ * Return: Valid host tag or MPI3MR_HOSTTAG_INVALID.
+ */
+static u16 mpi3mr_host_tag_for_scmd(struct mpi3mr_ioc *mrioc,
+ struct scsi_cmnd *scmd)
+{
+ struct scmd_priv *priv = NULL;
+ u32 unique_tag;
+ u16 host_tag, hw_queue;
+
+ unique_tag = blk_mq_unique_tag(scmd->request);
+
+ hw_queue = blk_mq_unique_tag_to_hwq(unique_tag);
+ if (hw_queue >= mrioc->num_op_reply_q)
+ return MPI3MR_HOSTTAG_INVALID;
+ host_tag = blk_mq_unique_tag_to_tag(unique_tag);
+
+ if (WARN_ON(host_tag >= mrioc->max_host_ios))
+ return MPI3MR_HOSTTAG_INVALID;
+
+ priv = scsi_cmd_priv(scmd);
+ /*host_tag 0 is invalid hence incrementing by 1*/
+ priv->host_tag = host_tag + 1;
+ priv->scmd = scmd;
+ priv->in_lld_scope = 1;
+ priv->req_q_idx = hw_queue;
+ priv->meta_chain_idx = -1;
+ priv->chain_idx = -1;
+ priv->meta_sg_valid = 0;
+ return priv->host_tag;
+}
+
+/**
+ * mpi3mr_scmd_from_host_tag - Get SCSI command from host tag
+ * @mrioc: Adapter instance reference
+ * @host_tag: Host tag
+ * @qidx: Operational queue index
+ *
+ * Identify the block tag from the host tag and queue index and
+ * retrieve associated scsi command using scsi_host_find_tag().
+ *
+ * Return: SCSI command reference or NULL.
+ */
+static struct scsi_cmnd *mpi3mr_scmd_from_host_tag(
+ struct mpi3mr_ioc *mrioc, u16 host_tag, u16 qidx)
+{
+ struct scsi_cmnd *scmd = NULL;
+ struct scmd_priv *priv = NULL;
+ u32 unique_tag = host_tag - 1;
+
+ if (WARN_ON(host_tag > mrioc->max_host_ios))
+ goto out;
+
+ unique_tag |= (qidx << BLK_MQ_UNIQUE_TAG_BITS);
+
+ scmd = scsi_host_find_tag(mrioc->shost, unique_tag);
+ if (scmd) {
+ priv = scsi_cmd_priv(scmd);
+ if (!priv->in_lld_scope)
+ scmd = NULL;
+ }
+out:
+ return scmd;
+}
+
+/**
+ * mpi3mr_clear_scmd_priv - Cleanup SCSI command private date
+ * @mrioc: Adapter instance reference
+ * @scmd: SCSI command reference
+ *
+ * Invalidate the SCSI command private data to mark the command
+ * is not in LLD scope anymore.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_clear_scmd_priv(struct mpi3mr_ioc *mrioc,
+ struct scsi_cmnd *scmd)
+{
+ struct scmd_priv *priv = NULL;
+
+ priv = scsi_cmd_priv(scmd);
+
+ if (WARN_ON(priv->in_lld_scope == 0))
+ return;
+ priv->host_tag = MPI3MR_HOSTTAG_INVALID;
+ priv->req_q_idx = 0xFFFF;
+ priv->scmd = NULL;
+ priv->in_lld_scope = 0;
+ priv->meta_sg_valid = 0;
+ if (priv->chain_idx >= 0) {
+ clear_bit(priv->chain_idx, mrioc->chain_bitmap);
+ priv->chain_idx = -1;
+ }
+ if (priv->meta_chain_idx >= 0) {
+ clear_bit(priv->meta_chain_idx, mrioc->chain_bitmap);
+ priv->meta_chain_idx = -1;
+ }
+}
+
+static void mpi3mr_dev_rmhs_send_tm(struct mpi3mr_ioc *mrioc, u16 handle,
+ struct mpi3mr_drv_cmd *cmdparam, u8 iou_rc);
+static void mpi3mr_fwevt_worker(struct work_struct *work);
+
+/**
+ * mpi3mr_fwevt_free - firmware event memory dealloctor
+ * @r: k reference pointer of the firmware event
+ *
+ * Free firmware event memory when no reference.
+ */
+static void mpi3mr_fwevt_free(struct kref *r)
+{
+ kfree(container_of(r, struct mpi3mr_fwevt, ref_count));
+}
+
+/**
+ * mpi3mr_fwevt_get - k reference incrementor
+ * @fwevt: Firmware event reference
+ *
+ * Increment firmware event reference count.
+ */
+static void mpi3mr_fwevt_get(struct mpi3mr_fwevt *fwevt)
+{
+ kref_get(&fwevt->ref_count);
+}
+
+/**
+ * mpi3mr_fwevt_put - k reference decrementor
+ * @fwevt: Firmware event reference
+ *
+ * decrement firmware event reference count.
+ */
+static void mpi3mr_fwevt_put(struct mpi3mr_fwevt *fwevt)
+{
+ kref_put(&fwevt->ref_count, mpi3mr_fwevt_free);
+}
+
+/**
+ * mpi3mr_alloc_fwevt - Allocate firmware event
+ * @len: length of firmware event data to allocate
+ *
+ * Allocate firmware event with required length and initialize
+ * the reference counter.
+ *
+ * Return: firmware event reference.
+ */
+static struct mpi3mr_fwevt *mpi3mr_alloc_fwevt(int len)
+{
+ struct mpi3mr_fwevt *fwevt;
+
+ fwevt = kzalloc(sizeof(*fwevt) + len, GFP_ATOMIC);
+ if (!fwevt)
+ return NULL;
+
+ kref_init(&fwevt->ref_count);
+ return fwevt;
+}
+
+/**
+ * mpi3mr_fwevt_add_to_list - Add firmware event to the list
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event reference
+ *
+ * Add the given firmware event to the firmware event list.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_fwevt_add_to_list(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ unsigned long flags;
+
+ if (!mrioc->fwevt_worker_thread)
+ return;
+
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ /* get fwevt reference count while adding it to fwevt_list */
+ mpi3mr_fwevt_get(fwevt);
+ INIT_LIST_HEAD(&fwevt->list);
+ list_add_tail(&fwevt->list, &mrioc->fwevt_list);
+ INIT_WORK(&fwevt->work, mpi3mr_fwevt_worker);
+ /* get fwevt reference count while enqueueing it to worker queue */
+ mpi3mr_fwevt_get(fwevt);
+ queue_work(mrioc->fwevt_worker_thread, &fwevt->work);
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+}
+
+/**
+ * mpi3mr_fwevt_del_from_list - Delete firmware event from list
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event reference
+ *
+ * Delete the given firmware event from the firmware event list.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_fwevt_del_from_list(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ if (!list_empty(&fwevt->list)) {
+ list_del_init(&fwevt->list);
+ /*
+ * Put fwevt reference count after
+ * removing it from fwevt_list
+ */
+ mpi3mr_fwevt_put(fwevt);
+ }
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+}
+
+/**
+ * mpi3mr_dequeue_fwevt - Dequeue firmware event from the list
+ * @mrioc: Adapter instance reference
+ *
+ * Dequeue a firmware event from the firmware event list.
+ *
+ * Return: firmware event.
+ */
+static struct mpi3mr_fwevt *mpi3mr_dequeue_fwevt(
+ struct mpi3mr_ioc *mrioc)
+{
+ unsigned long flags;
+ struct mpi3mr_fwevt *fwevt = NULL;
+
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ if (!list_empty(&mrioc->fwevt_list)) {
+ fwevt = list_first_entry(&mrioc->fwevt_list,
+ struct mpi3mr_fwevt, list);
+ list_del_init(&fwevt->list);
+ /*
+ * Put fwevt reference count after
+ * removing it from fwevt_list
+ */
+ mpi3mr_fwevt_put(fwevt);
+ }
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+
+ return fwevt;
+}
+
+/**
+ * mpi3mr_cleanup_fwevt_list - Cleanup firmware event list
+ * @mrioc: Adapter instance reference
+ *
+ * Flush all pending firmware events from the firmware event
+ * list.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3mr_fwevt *fwevt = NULL;
+
+ if ((list_empty(&mrioc->fwevt_list) && !mrioc->current_event) ||
+ !mrioc->fwevt_worker_thread)
+ return;
+
+ while ((fwevt = mpi3mr_dequeue_fwevt(mrioc)) ||
+ (fwevt = mrioc->current_event)) {
+ /*
+ * Wait on the fwevt to complete. If this returns 1, then
+ * the event was never executed, and we need a put for the
+ * reference the work had on the fwevt.
+ *
+ * If it did execute, we wait for it to finish, and the put will
+ * happen from mpi3mr_process_fwevt()
+ */
+ if (cancel_work_sync(&fwevt->work)) {
+ /*
+ * Put fwevt reference count after
+ * dequeuing it from worker queue
+ */
+ mpi3mr_fwevt_put(fwevt);
+ /*
+ * Put fwevt reference count to neutralize
+ * kref_init increment
+ */
+ mpi3mr_fwevt_put(fwevt);
+ }
+ }
+}
+
+/**
+ * mpi3mr_invalidate_devhandles -Invalidate device handles
+ * @mrioc: Adapter instance reference
+ *
+ * Invalidate the device handles in the target device structures
+ * . Called post reset prior to reinitializing the controller.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_invalidate_devhandles(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+ struct mpi3mr_stgt_priv_data *tgt_priv;
+
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) {
+ tgtdev->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ if (tgtdev->starget && tgtdev->starget->hostdata) {
+ tgt_priv = tgtdev->starget->hostdata;
+ tgt_priv->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ }
+ }
+}
+
+/**
+ * mpi3mr_print_scmd - print individual SCSI command
+ * @rq: Block request
+ * @data: Adapter instance reference
+ * @reserved: N/A. Currently not used
+ *
+ * Print the SCSI command details if it is in LLD scope.
+ *
+ * Return: true always.
+ */
+static bool mpi3mr_print_scmd(struct request *rq,
+ void *data, bool reserved)
+{
+ struct mpi3mr_ioc *mrioc = (struct mpi3mr_ioc *)data;
+ struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
+ struct scmd_priv *priv = NULL;
+
+ if (scmd) {
+ priv = scsi_cmd_priv(scmd);
+ if (!priv->in_lld_scope)
+ goto out;
+
+ ioc_info(mrioc, "%s :Host Tag = %d, qid = %d\n",
+ __func__, priv->host_tag, priv->req_q_idx + 1);
+ scsi_print_command(scmd);
+ }
+
+out:
+ return(true);
+}
+
+/**
+ * mpi3mr_flush_scmd - Flush individual SCSI command
+ * @rq: Block request
+ * @data: Adapter instance reference
+ * @reserved: N/A. Currently not used
+ *
+ * Return the SCSI command to the upper layers if it is in LLD
+ * scope.
+ *
+ * Return: true always.
+ */
+
+static bool mpi3mr_flush_scmd(struct request *rq,
+ void *data, bool reserved)
+{
+ struct mpi3mr_ioc *mrioc = (struct mpi3mr_ioc *)data;
+ struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
+ struct scmd_priv *priv = NULL;
+
+ if (scmd) {
+ priv = scsi_cmd_priv(scmd);
+ if (!priv->in_lld_scope)
+ goto out;
+
+ if (priv->meta_sg_valid)
+ dma_unmap_sg(&mrioc->pdev->dev, scsi_prot_sglist(scmd),
+ scsi_prot_sg_count(scmd), scmd->sc_data_direction);
+ mpi3mr_clear_scmd_priv(mrioc, scmd);
+ scsi_dma_unmap(scmd);
+ scmd->result = DID_RESET << 16;
+ scsi_print_command(scmd);
+ scmd->scsi_done(scmd);
+ mrioc->flush_io_count++;
+ }
+
+out:
+ return(true);
+}
+
+/**
+ * mpi3mr_flush_host_io - Flush host I/Os
+ * @mrioc: Adapter instance reference
+ *
+ * Flush all of the pending I/Os by calling
+ * blk_mq_tagset_busy_iter() for each possible tag. This is
+ * executed post controller reset
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_flush_host_io(struct mpi3mr_ioc *mrioc)
+{
+ struct Scsi_Host *shost = mrioc->shost;
+
+ mrioc->flush_io_count = 0;
+ ioc_info(mrioc, "%s :Flushing Host I/O cmds post reset\n", __func__);
+ blk_mq_tagset_busy_iter(&shost->tag_set,
+ mpi3mr_flush_scmd, (void *)mrioc);
+ ioc_info(mrioc, "%s :Flushed %d Host I/O cmds\n", __func__,
+ mrioc->flush_io_count);
+}
+
+/**
+ * mpi3mr_alloc_tgtdev - target device allocator
+ *
+ * Allocate target device instance and initialize the reference
+ * count
+ *
+ * Return: target device instance.
+ */
+static struct mpi3mr_tgt_dev *mpi3mr_alloc_tgtdev(void)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ tgtdev = kzalloc(sizeof(*tgtdev), GFP_ATOMIC);
+ if (!tgtdev)
+ return NULL;
+ kref_init(&tgtdev->ref_count);
+ return tgtdev;
+}
+
+/**
+ * mpi3mr_tgtdev_add_to_list -Add tgtdevice to the list
+ * @mrioc: Adapter instance reference
+ * @tgtdev: Target device
+ *
+ * Add the target device to the target device list
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_tgtdev_add_to_list(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ mpi3mr_tgtdev_get(tgtdev);
+ INIT_LIST_HEAD(&tgtdev->list);
+ list_add_tail(&tgtdev->list, &mrioc->tgtdev_list);
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+}
+
+/**
+ * mpi3mr_tgtdev_del_from_list -Delete tgtdevice from the list
+ * @mrioc: Adapter instance reference
+ * @tgtdev: Target device
+ *
+ * Remove the target device from the target device list
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_tgtdev_del_from_list(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ if (!list_empty(&tgtdev->list)) {
+ list_del_init(&tgtdev->list);
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+}
+
+/**
+ * __mpi3mr_get_tgtdev_by_handle -Get tgtdev from device handle
+ * @mrioc: Adapter instance reference
+ * @handle: Device handle
+ *
+ * Accessor to retrieve target device from the device handle.
+ * Non Lock version
+ *
+ * Return: Target device reference.
+ */
+static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_handle(
+ struct mpi3mr_ioc *mrioc, u16 handle)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ assert_spin_locked(&mrioc->tgtdev_lock);
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
+ if (tgtdev->dev_handle == handle)
+ goto found_tgtdev;
+ return NULL;
+
+found_tgtdev:
+ mpi3mr_tgtdev_get(tgtdev);
+ return tgtdev;
+}
+
+/**
+ * mpi3mr_get_tgtdev_by_handle -Get tgtdev from device handle
+ * @mrioc: Adapter instance reference
+ * @handle: Device handle
+ *
+ * Accessor to retrieve target device from the device handle.
+ * Lock version
+ *
+ * Return: Target device reference.
+ */
+static struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_handle(
+ struct mpi3mr_ioc *mrioc, u16 handle)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgtdev = __mpi3mr_get_tgtdev_by_handle(mrioc, handle);
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+ return tgtdev;
+}
+
+/**
+ * __mpi3mr_get_tgtdev_by_perst_id -Get tgtdev from persist ID
+ * @mrioc: Adapter instance reference
+ * @persist_id: Persistent ID
+ *
+ * Accessor to retrieve target device from the Persistent ID.
+ * Non Lock version
+ *
+ * Return: Target device reference.
+ */
+static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_by_perst_id(
+ struct mpi3mr_ioc *mrioc, u16 persist_id)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ assert_spin_locked(&mrioc->tgtdev_lock);
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list)
+ if (tgtdev->perst_id == persist_id)
+ goto found_tgtdev;
+ return NULL;
+
+found_tgtdev:
+ mpi3mr_tgtdev_get(tgtdev);
+ return tgtdev;
+}
+
+/**
+ * mpi3mr_get_tgtdev_by_perst_id -Get tgtdev from persistent ID
+ * @mrioc: Adapter instance reference
+ * @persist_id: Persistent ID
+ *
+ * Accessor to retrieve target device from the Persistent ID.
+ * Lock version
+ *
+ * Return: Target device reference.
+ */
+static struct mpi3mr_tgt_dev *mpi3mr_get_tgtdev_by_perst_id(
+ struct mpi3mr_ioc *mrioc, u16 persist_id)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgtdev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, persist_id);
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+ return tgtdev;
+}
+
+/**
+ * __mpi3mr_get_tgtdev_from_tgtpriv -Get tgtdev from tgt private
+ * @mrioc: Adapter instance reference
+ * @tgt_priv: Target private data
+ *
+ * Accessor to return target device from the target private
+ * data. Non Lock version
+ *
+ * Return: Target device reference.
+ */
+static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_from_tgtpriv(
+ struct mpi3mr_ioc *mrioc, struct mpi3mr_stgt_priv_data *tgt_priv)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ assert_spin_locked(&mrioc->tgtdev_lock);
+ tgtdev = tgt_priv->tgt_dev;
+ if (tgtdev)
+ mpi3mr_tgtdev_get(tgtdev);
+ return tgtdev;
+}
+
+/**
+ * mpi3mr_remove_tgtdev_from_host - Remove dev from upper layers
+ * @mrioc: Adapter instance reference
+ * @tgtdev: Target device structure
+ *
+ * Checks whether the device is exposed to upper layers and if it
+ * is then remove the device from upper layers by calling
+ * scsi_remove_target().
+ *
+ * Return: 0 on success, non zero on failure.
+ */
+static void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev)
+{
+ struct mpi3mr_stgt_priv_data *tgt_priv;
+
+ ioc_info(mrioc, "%s :Removing handle(0x%04x), wwid(0x%016llx)\n",
+ __func__, tgtdev->dev_handle, (unsigned long long)tgtdev->wwid);
+ if (tgtdev->starget && tgtdev->starget->hostdata) {
+ tgt_priv = tgtdev->starget->hostdata;
+ tgt_priv->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ }
+
+ if (tgtdev->starget) {
+ scsi_remove_target(&tgtdev->starget->dev);
+ tgtdev->host_exposed = 0;
+ }
+ ioc_info(mrioc, "%s :Removed handle(0x%04x), wwid(0x%016llx)\n",
+ __func__, tgtdev->dev_handle, (unsigned long long)tgtdev->wwid);
+}
+
+/**
+ * mpi3mr_report_tgtdev_to_host - Expose device to upper layers
+ * @mrioc: Adapter instance reference
+ * @perst_id: Persistent ID of the device
+ *
+ * Checks whether the device can be exposed to upper layers and
+ * if it is not then expose the device to upper layers by
+ * calling scsi_scan_target().
+ *
+ * Return: 0 on success, non zero on failure.
+ */
+static int mpi3mr_report_tgtdev_to_host(struct mpi3mr_ioc *mrioc,
+ u16 perst_id)
+{
+ int retval = 0;
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ tgtdev = mpi3mr_get_tgtdev_by_perst_id(mrioc, perst_id);
+ if (!tgtdev) {
+ retval = -1;
+ goto out;
+ }
+ if (tgtdev->is_hidden) {
+ retval = -1;
+ goto out;
+ }
+ if (!tgtdev->host_exposed && !mrioc->reset_in_progress) {
+ tgtdev->host_exposed = 1;
+ scsi_scan_target(&mrioc->shost->shost_gendev, 0,
+ tgtdev->perst_id,
+ SCAN_WILD_CARD, SCSI_SCAN_INITIAL);
+ if (!tgtdev->starget)
+ tgtdev->host_exposed = 0;
+ }
+out:
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_change_queue_depth- Change QD callback handler
+ * @sdev: SCSI device reference
+ * @q_depth: Queue depth
+ *
+ * Validate and limit QD and call scsi_change_queue_depth.
+ *
+ * Return: return value of scsi_change_queue_depth
+ */
+static int mpi3mr_change_queue_depth(struct scsi_device *sdev,
+ int q_depth)
+{
+ struct scsi_target *starget = scsi_target(sdev);
+ struct Scsi_Host *shost = dev_to_shost(&starget->dev);
+ int retval = 0;
+
+ if (!sdev->tagged_supported)
+ q_depth = 1;
+ if (q_depth > shost->can_queue)
+ q_depth = shost->can_queue;
+ else if (!q_depth)
+ q_depth = MPI3MR_DEFAULT_SDEV_QD;
+ retval = scsi_change_queue_depth(sdev, q_depth);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_update_sdev - Update SCSI device information
+ * @sdev: SCSI device reference
+ * @data: target device reference
+ *
+ * This is an iterator function called for each SCSI device in a
+ * target to update the target specific information into each
+ * SCSI device.
+ *
+ * Return: Nothing.
+ */
+static void
+mpi3mr_update_sdev(struct scsi_device *sdev, void *data)
+{
+ struct mpi3mr_tgt_dev *tgtdev;
+
+ tgtdev = (struct mpi3mr_tgt_dev *)data;
+ if (!tgtdev)
+ return;
+
+ mpi3mr_change_queue_depth(sdev, tgtdev->q_depth);
+ switch (tgtdev->dev_type) {
+ case MPI3_DEVICE_DEVFORM_PCIE:
+ /*The block layer hw sector size = 512*/
+ blk_queue_max_hw_sectors(sdev->request_queue,
+ tgtdev->dev_spec.pcie_inf.mdts / 512);
+ blk_queue_virt_boundary(sdev->request_queue,
+ ((1 << tgtdev->dev_spec.pcie_inf.pgsz) - 1));
+
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * mpi3mr_rfresh_tgtdevs - Refresh target device exposure
+ * @mrioc: Adapter instance reference
+ *
+ * This is executed post controller reset to identify any
+ * missing devices during reset and remove from the upper layers
+ * or expose any newly detected device to the upper layers.
+ *
+ * Return: Nothing.
+ */
+
+void mpi3mr_rfresh_tgtdevs(struct mpi3mr_ioc *mrioc)
+{
+ struct mpi3mr_tgt_dev *tgtdev, *tgtdev_next;
+
+ list_for_each_entry_safe(tgtdev, tgtdev_next, &mrioc->tgtdev_list,
+ list) {
+ if ((tgtdev->dev_handle == MPI3MR_INVALID_DEV_HANDLE) &&
+ tgtdev->host_exposed) {
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+ mpi3mr_tgtdev_del_from_list(mrioc, tgtdev);
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+ }
+
+ tgtdev = NULL;
+ list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) {
+ if ((tgtdev->dev_handle != MPI3MR_INVALID_DEV_HANDLE) &&
+ !tgtdev->is_hidden && !tgtdev->host_exposed)
+ mpi3mr_report_tgtdev_to_host(mrioc, tgtdev->perst_id);
+ }
+}
+
+/**
+ * mpi3mr_update_tgtdev - DevStatusChange evt bottomhalf
+ * @mrioc: Adapter instance reference
+ * @tgtdev: Target device internal structure
+ * @dev_pg0: New device page0
+ *
+ * Update the information from the device page0 into the driver
+ * cached target device structure.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_tgt_dev *tgtdev, struct mpi3_device_page0 *dev_pg0)
+{
+ u16 flags = 0;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data;
+ u8 prot_mask = 0;
+
+ tgtdev->perst_id = le16_to_cpu(dev_pg0->persistent_id);
+ tgtdev->dev_handle = le16_to_cpu(dev_pg0->dev_handle);
+ tgtdev->dev_type = dev_pg0->device_form;
+ tgtdev->encl_handle = le16_to_cpu(dev_pg0->enclosure_handle);
+ tgtdev->parent_handle = le16_to_cpu(dev_pg0->parent_dev_handle);
+ tgtdev->slot = le16_to_cpu(dev_pg0->slot);
+ tgtdev->q_depth = le16_to_cpu(dev_pg0->queue_depth);
+ tgtdev->wwid = le64_to_cpu(dev_pg0->wwid);
+
+ flags = le16_to_cpu(dev_pg0->flags);
+ tgtdev->is_hidden = (flags & MPI3_DEVICE0_FLAGS_HIDDEN);
+
+ if (tgtdev->starget && tgtdev->starget->hostdata) {
+ scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *)
+ tgtdev->starget->hostdata;
+ scsi_tgt_priv_data->perst_id = tgtdev->perst_id;
+ scsi_tgt_priv_data->dev_handle = tgtdev->dev_handle;
+ scsi_tgt_priv_data->dev_type = tgtdev->dev_type;
+ }
+
+ switch (tgtdev->dev_type) {
+ case MPI3_DEVICE_DEVFORM_SAS_SATA:
+ {
+ struct mpi3_device0_sas_sata_format *sasinf =
+ &dev_pg0->device_specific.sas_sata_format;
+ u16 dev_info = le16_to_cpu(sasinf->device_info);
+
+ tgtdev->dev_spec.sas_sata_inf.dev_info = dev_info;
+ tgtdev->dev_spec.sas_sata_inf.sas_address =
+ le64_to_cpu(sasinf->sas_address);
+ if ((dev_info & MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_MASK) !=
+ MPI3_SAS_DEVICE_INFO_DEVICE_TYPE_END_DEVICE)
+ tgtdev->is_hidden = 1;
+ else if (!(dev_info & (MPI3_SAS_DEVICE_INFO_STP_SATA_TARGET |
+ MPI3_SAS_DEVICE_INFO_SSP_TARGET)))
+ tgtdev->is_hidden = 1;
+ break;
+ }
+ case MPI3_DEVICE_DEVFORM_PCIE:
+ {
+ struct mpi3_device0_pcie_format *pcieinf =
+ &dev_pg0->device_specific.pcie_format;
+ u16 dev_info = le16_to_cpu(pcieinf->device_info);
+
+ tgtdev->dev_spec.pcie_inf.capb =
+ le32_to_cpu(pcieinf->capabilities);
+ tgtdev->dev_spec.pcie_inf.mdts = MPI3MR_DEFAULT_MDTS;
+ /* 2^12 = 4096 */
+ tgtdev->dev_spec.pcie_inf.pgsz = 12;
+ if (dev_pg0->access_status == MPI3_DEVICE0_ASTATUS_NO_ERRORS) {
+ tgtdev->dev_spec.pcie_inf.mdts =
+ le32_to_cpu(pcieinf->maximum_data_transfer_size);
+ tgtdev->dev_spec.pcie_inf.pgsz = pcieinf->page_size;
+ tgtdev->dev_spec.pcie_inf.reset_to =
+ pcieinf->controller_reset_to;
+ tgtdev->dev_spec.pcie_inf.abort_to =
+ pcieinf->nv_me_abort_to;
+ }
+ if (tgtdev->dev_spec.pcie_inf.mdts > (1024 * 1024))
+ tgtdev->dev_spec.pcie_inf.mdts = (1024 * 1024);
+ if ((dev_info & MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK) !=
+ MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NVME_DEVICE)
+ tgtdev->is_hidden = 1;
+ if (mrioc->shost)
+ prot_mask = scsi_host_get_prot(mrioc->shost);
+ if (prot_mask & SHOST_DIX_TYPE0_PROTECTION) {
+ scsi_host_set_prot(mrioc->shost, prot_mask & 0x77);
+ ioc_info(mrioc,
+ "%s : Disabling DIX0 prot capability\n", __func__);
+ ioc_info(mrioc,
+ "because HBA does not support DIX0 operation on NVME drives\n");
+ }
+ break;
+ }
+ case MPI3_DEVICE_DEVFORM_VD:
+ {
+ struct mpi3_device0_vd_format *vdinf =
+ &dev_pg0->device_specific.vd_format;
+
+ tgtdev->dev_spec.vol_inf.state = vdinf->vd_state;
+ if (vdinf->vd_state == MPI3_DEVICE0_VD_STATE_OFFLINE)
+ tgtdev->is_hidden = 1;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/**
+ * mpi3mr_devstatuschg_evt_bh - DevStatusChange evt bottomhalf
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event information.
+ *
+ * Process Device status Change event and based on device's new
+ * information, either expose the device to the upper layers, or
+ * remove the device from upper layers.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_devstatuschg_evt_bh(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ u16 dev_handle = 0;
+ u8 uhide = 0, delete = 0, cleanup = 0;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ struct mpi3_event_data_device_status_change *evtdata =
+ (struct mpi3_event_data_device_status_change *)fwevt->event_data;
+
+ dev_handle = le16_to_cpu(evtdata->dev_handle);
+ ioc_info(mrioc,
+ "%s :device status change: handle(0x%04x): reason code(0x%x)\n",
+ __func__, dev_handle, evtdata->reason_code);
+ switch (evtdata->reason_code) {
+ case MPI3_EVENT_DEV_STAT_RC_HIDDEN:
+ delete = 1;
+ break;
+ case MPI3_EVENT_DEV_STAT_RC_NOT_HIDDEN:
+ uhide = 1;
+ break;
+ case MPI3_EVENT_DEV_STAT_RC_VD_NOT_RESPONDING:
+ delete = 1;
+ cleanup = 1;
+ break;
+ default:
+ ioc_info(mrioc, "%s :Unhandled reason code(0x%x)\n", __func__,
+ evtdata->reason_code);
+ break;
+ }
+
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle);
+ if (!tgtdev)
+ goto out;
+ if (uhide) {
+ tgtdev->is_hidden = 0;
+ if (!tgtdev->host_exposed)
+ mpi3mr_report_tgtdev_to_host(mrioc, tgtdev->perst_id);
+ }
+ if (tgtdev->starget && tgtdev->starget->hostdata) {
+ if (delete)
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+ }
+ if (cleanup) {
+ mpi3mr_tgtdev_del_from_list(mrioc, tgtdev);
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+
+out:
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+}
+
+/**
+ * mpi3mr_devinfochg_evt_bh - DeviceInfoChange evt bottomhalf
+ * @mrioc: Adapter instance reference
+ * @dev_pg0: New device page0
+ *
+ * Process Device Info Change event and based on device's new
+ * information, either expose the device to the upper layers, or
+ * remove the device from upper layers or update the details of
+ * the device.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_devinfochg_evt_bh(struct mpi3mr_ioc *mrioc,
+ struct mpi3_device_page0 *dev_pg0)
+{
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ u16 dev_handle = 0, perst_id = 0;
+
+ perst_id = le16_to_cpu(dev_pg0->persistent_id);
+ dev_handle = le16_to_cpu(dev_pg0->dev_handle);
+ ioc_info(mrioc,
+ "%s :Device info change: handle(0x%04x): persist_id(0x%x)\n",
+ __func__, dev_handle, perst_id);
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle);
+ if (!tgtdev)
+ goto out;
+ mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0);
+ if (!tgtdev->is_hidden && !tgtdev->host_exposed)
+ mpi3mr_report_tgtdev_to_host(mrioc, perst_id);
+ if (tgtdev->is_hidden && tgtdev->host_exposed)
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+ if (!tgtdev->is_hidden && tgtdev->host_exposed && tgtdev->starget)
+ starget_for_each_device(tgtdev->starget, (void *)tgtdev,
+ mpi3mr_update_sdev);
+out:
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+}
+
+/**
+ * mpi3mr_sastopochg_evt_debug - SASTopoChange details
+ * @mrioc: Adapter instance reference
+ * @event_data: SAS topology change list event data
+ *
+ * Prints information about the SAS topology change event.
+ *
+ * Return: Nothing.
+ */
+static void
+mpi3mr_sastopochg_evt_debug(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_data_sas_topology_change_list *event_data)
+{
+ int i;
+ u16 handle;
+ u8 reason_code, phy_number;
+ char *status_str = NULL;
+ u8 link_rate, prev_link_rate;
+
+ switch (event_data->exp_status) {
+ case MPI3_EVENT_SAS_TOPO_ES_NOT_RESPONDING:
+ status_str = "remove";
+ break;
+ case MPI3_EVENT_SAS_TOPO_ES_RESPONDING:
+ status_str = "responding";
+ break;
+ case MPI3_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING:
+ status_str = "remove delay";
+ break;
+ case MPI3_EVENT_SAS_TOPO_ES_NO_EXPANDER:
+ status_str = "direct attached";
+ break;
+ default:
+ status_str = "unknown status";
+ break;
+ }
+ ioc_info(mrioc, "%s :sas topology change: (%s)\n",
+ __func__, status_str);
+ ioc_info(mrioc,
+ "%s :\texpander_handle(0x%04x), enclosure_handle(0x%04x) start_phy(%02d), num_entries(%d)\n",
+ __func__, le16_to_cpu(event_data->expander_dev_handle),
+ le16_to_cpu(event_data->enclosure_handle),
+ event_data->start_phy_num, event_data->num_entries);
+ for (i = 0; i < event_data->num_entries; i++) {
+ handle = le16_to_cpu(event_data->phy_entry[i].attached_dev_handle);
+ if (!handle)
+ continue;
+ phy_number = event_data->start_phy_num + i;
+ reason_code = event_data->phy_entry[i].status &
+ MPI3_EVENT_SAS_TOPO_PHY_RC_MASK;
+ switch (reason_code) {
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING:
+ status_str = "target remove";
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_DELAY_NOT_RESPONDING:
+ status_str = "delay target remove";
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_PHY_CHANGED:
+ status_str = "link status change";
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_NO_CHANGE:
+ status_str = "link status no change";
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_RESPONDING:
+ status_str = "target responding";
+ break;
+ default:
+ status_str = "unknown";
+ break;
+ }
+ link_rate = event_data->phy_entry[i].link_rate >> 4;
+ prev_link_rate = event_data->phy_entry[i].link_rate & 0xF;
+ ioc_info(mrioc,
+ "%s :\tphy(%02d), attached_handle(0x%04x): %s: link rate: new(0x%02x), old(0x%02x)\n",
+ __func__, phy_number, handle, status_str, link_rate,
+ prev_link_rate);
+ }
+}
+
+/**
+ * mpi3mr_sastopochg_evt_bh - SASTopologyChange evt bottomhalf
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event reference
+ *
+ * Prints information about the SAS topology change event and
+ * for "not responding" event code, removes the device from the
+ * upper layers.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_sastopochg_evt_bh(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ struct mpi3_event_data_sas_topology_change_list *event_data =
+ (struct mpi3_event_data_sas_topology_change_list *)fwevt->event_data;
+ int i;
+ u16 handle;
+ u8 reason_code;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+
+ mpi3mr_sastopochg_evt_debug(mrioc, event_data);
+
+ for (i = 0; i < event_data->num_entries; i++) {
+ handle = le16_to_cpu(event_data->phy_entry[i].attached_dev_handle);
+ if (!handle)
+ continue;
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle);
+ if (!tgtdev)
+ continue;
+
+ reason_code = event_data->phy_entry[i].status &
+ MPI3_EVENT_SAS_TOPO_PHY_RC_MASK;
+
+ switch (reason_code) {
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING:
+ if (tgtdev->host_exposed)
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+ mpi3mr_tgtdev_del_from_list(mrioc, tgtdev);
+ mpi3mr_tgtdev_put(tgtdev);
+ break;
+ default:
+ break;
+ }
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+}
+
+/**
+ * mpi3mr_pcietopochg_evt_debug - PCIeTopoChange details
+ * @mrioc: Adapter instance reference
+ * @event_data: PCIe topology change list event data
+ *
+ * Prints information about the PCIe topology change event.
+ *
+ * Return: Nothing.
+ */
+static void
+mpi3mr_pcietopochg_evt_debug(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_data_pcie_topology_change_list *event_data)
+{
+ int i;
+ u16 handle;
+ u16 reason_code;
+ u8 port_number;
+ char *status_str = NULL;
+ u8 link_rate, prev_link_rate;
+
+ switch (event_data->switch_status) {
+ case MPI3_EVENT_PCIE_TOPO_SS_NOT_RESPONDING:
+ status_str = "remove";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_SS_RESPONDING:
+ status_str = "responding";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_SS_DELAY_NOT_RESPONDING:
+ status_str = "remove delay";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_SS_NO_PCIE_SWITCH:
+ status_str = "direct attached";
+ break;
+ default:
+ status_str = "unknown status";
+ break;
+ }
+ ioc_info(mrioc, "%s :pcie topology change: (%s)\n",
+ __func__, status_str);
+ ioc_info(mrioc,
+ "%s :\tswitch_handle(0x%04x), enclosure_handle(0x%04x) start_port(%02d), num_entries(%d)\n",
+ __func__, le16_to_cpu(event_data->switch_dev_handle),
+ le16_to_cpu(event_data->enclosure_handle),
+ event_data->start_port_num, event_data->num_entries);
+ for (i = 0; i < event_data->num_entries; i++) {
+ handle =
+ le16_to_cpu(event_data->port_entry[i].attached_dev_handle);
+ if (!handle)
+ continue;
+ port_number = event_data->start_port_num + i;
+ reason_code = event_data->port_entry[i].port_status;
+ switch (reason_code) {
+ case MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING:
+ status_str = "target remove";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING:
+ status_str = "delay target remove";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_PORT_CHANGED:
+ status_str = "link status change";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_NO_CHANGE:
+ status_str = "link status no change";
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_RESPONDING:
+ status_str = "target responding";
+ break;
+ default:
+ status_str = "unknown";
+ break;
+ }
+ link_rate = event_data->port_entry[i].current_port_info &
+ MPI3_EVENT_PCIE_TOPO_PI_RATE_MASK;
+ prev_link_rate = event_data->port_entry[i].previous_port_info &
+ MPI3_EVENT_PCIE_TOPO_PI_RATE_MASK;
+ ioc_info(mrioc,
+ "%s :\tport(%02d), attached_handle(0x%04x): %s: link rate: new(0x%02x), old(0x%02x)\n",
+ __func__, port_number, handle, status_str, link_rate,
+ prev_link_rate);
+ }
+}
+
+/**
+ * mpi3mr_pcietopochg_evt_bh - PCIeTopologyChange evt bottomhalf
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event reference
+ *
+ * Prints information about the PCIe topology change event and
+ * for "not responding" event code, removes the device from the
+ * upper layers.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_pcietopochg_evt_bh(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ struct mpi3_event_data_pcie_topology_change_list *event_data =
+ (struct mpi3_event_data_pcie_topology_change_list *)fwevt->event_data;
+ int i;
+ u16 handle;
+ u8 reason_code;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+
+ mpi3mr_pcietopochg_evt_debug(mrioc, event_data);
+
+ for (i = 0; i < event_data->num_entries; i++) {
+ handle =
+ le16_to_cpu(event_data->port_entry[i].attached_dev_handle);
+ if (!handle)
+ continue;
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle);
+ if (!tgtdev)
+ continue;
+
+ reason_code = event_data->port_entry[i].port_status;
+
+ switch (reason_code) {
+ case MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING:
+ if (tgtdev->host_exposed)
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+ mpi3mr_tgtdev_del_from_list(mrioc, tgtdev);
+ mpi3mr_tgtdev_put(tgtdev);
+ break;
+ default:
+ break;
+ }
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+}
+
+/**
+ * mpi3mr_fwevt_bh - Firmware event bottomhalf handler
+ * @mrioc: Adapter instance reference
+ * @fwevt: Firmware event reference
+ *
+ * Identifies the firmware event and calls corresponding bottomg
+ * half handler and sends event acknowledgment if required.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_fwevt *fwevt)
+{
+ mrioc->current_event = fwevt;
+ mpi3mr_fwevt_del_from_list(mrioc, fwevt);
+
+ if (mrioc->stop_drv_processing)
+ goto out;
+
+ if (!fwevt->process_evt)
+ goto evt_ack;
+
+ switch (fwevt->event_id) {
+ case MPI3_EVENT_DEVICE_ADDED:
+ {
+ struct mpi3_device_page0 *dev_pg0 =
+ (struct mpi3_device_page0 *)fwevt->event_data;
+ mpi3mr_report_tgtdev_to_host(mrioc,
+ le16_to_cpu(dev_pg0->persistent_id));
+ break;
+ }
+ case MPI3_EVENT_DEVICE_INFO_CHANGED:
+ {
+ mpi3mr_devinfochg_evt_bh(mrioc,
+ (struct mpi3_device_page0 *)fwevt->event_data);
+ break;
+ }
+ case MPI3_EVENT_DEVICE_STATUS_CHANGE:
+ {
+ mpi3mr_devstatuschg_evt_bh(mrioc, fwevt);
+ break;
+ }
+ case MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ {
+ mpi3mr_sastopochg_evt_bh(mrioc, fwevt);
+ break;
+ }
+ case MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
+ {
+ mpi3mr_pcietopochg_evt_bh(mrioc, fwevt);
+ break;
+ }
+ default:
+ break;
+ }
+
+evt_ack:
+ if (fwevt->send_ack)
+ mpi3mr_send_event_ack(mrioc, fwevt->event_id,
+ fwevt->evt_ctx);
+out:
+ /* Put fwevt reference count to neutralize kref_init increment */
+ mpi3mr_fwevt_put(fwevt);
+ mrioc->current_event = NULL;
+}
+
+/**
+ * mpi3mr_fwevt_worker - Firmware event worker
+ * @work: Work struct containing firmware event
+ *
+ * Extracts the firmware event and calls mpi3mr_fwevt_bh.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_fwevt_worker(struct work_struct *work)
+{
+ struct mpi3mr_fwevt *fwevt = container_of(work, struct mpi3mr_fwevt,
+ work);
+ mpi3mr_fwevt_bh(fwevt->mrioc, fwevt);
+ /*
+ * Put fwevt reference count after
+ * dequeuing it from worker queue
+ */
+ mpi3mr_fwevt_put(fwevt);
+}
+
+/**
+ * mpi3mr_create_tgtdev - Create and add a target device
+ * @mrioc: Adapter instance reference
+ * @dev_pg0: Device Page 0 data
+ *
+ * If the device specified by the device page 0 data is not
+ * present in the driver's internal list, allocate the memory
+ * for the device, populate the data and add to the list, else
+ * update the device data. The key is persistent ID.
+ *
+ * Return: 0 on success, -ENOMEM on memory allocation failure
+ */
+static int mpi3mr_create_tgtdev(struct mpi3mr_ioc *mrioc,
+ struct mpi3_device_page0 *dev_pg0)
+{
+ int retval = 0;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ u16 perst_id = 0;
+
+ perst_id = le16_to_cpu(dev_pg0->persistent_id);
+ tgtdev = mpi3mr_get_tgtdev_by_perst_id(mrioc, perst_id);
+ if (tgtdev) {
+ mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0);
+ mpi3mr_tgtdev_put(tgtdev);
+ } else {
+ tgtdev = mpi3mr_alloc_tgtdev();
+ if (!tgtdev)
+ return -ENOMEM;
+ mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0);
+ mpi3mr_tgtdev_add_to_list(mrioc, tgtdev);
+ }
+
+ return retval;
+}
+
+/**
+ * mpi3mr_flush_delayed_rmhs_list - Flush pending commands
+ * @mrioc: Adapter instance reference
+ *
+ * Flush pending commands in the delayed removal handshake list
+ * due to a controller reset or driver removal as a cleanup.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_flush_delayed_rmhs_list(struct mpi3mr_ioc *mrioc)
+{
+ struct delayed_dev_rmhs_node *_rmhs_node;
+
+ while (!list_empty(&mrioc->delayed_rmhs_list)) {
+ _rmhs_node = list_entry(mrioc->delayed_rmhs_list.next,
+ struct delayed_dev_rmhs_node, list);
+ list_del(&_rmhs_node->list);
+ kfree(_rmhs_node);
+ }
+}
+
+/**
+ * mpi3mr_dev_rmhs_complete_iou - Device removal IOUC completion
+ * @mrioc: Adapter instance reference
+ * @drv_cmd: Internal command tracker
+ *
+ * Issues a target reset TM to the firmware from the device
+ * removal TM pend list or retry the removal handshake sequence
+ * based on the IOU control request IOC status.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_dev_rmhs_complete_iou(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd)
+{
+ u16 cmd_idx = drv_cmd->host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN;
+ struct delayed_dev_rmhs_node *delayed_dev_rmhs = NULL;
+
+ ioc_info(mrioc,
+ "%s :dev_rmhs_iouctrl_complete:handle(0x%04x), ioc_status(0x%04x), loginfo(0x%08x)\n",
+ __func__, drv_cmd->dev_handle, drv_cmd->ioc_status,
+ drv_cmd->ioc_loginfo);
+ if (drv_cmd->ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ if (drv_cmd->retry_count < MPI3MR_DEV_RMHS_RETRY_COUNT) {
+ drv_cmd->retry_count++;
+ ioc_info(mrioc,
+ "%s :dev_rmhs_iouctrl_complete: handle(0x%04x)retrying handshake retry=%d\n",
+ __func__, drv_cmd->dev_handle,
+ drv_cmd->retry_count);
+ mpi3mr_dev_rmhs_send_tm(mrioc, drv_cmd->dev_handle,
+ drv_cmd, drv_cmd->iou_rc);
+ return;
+ }
+ ioc_err(mrioc,
+ "%s :dev removal handshake failed after all retries: handle(0x%04x)\n",
+ __func__, drv_cmd->dev_handle);
+ } else {
+ ioc_info(mrioc,
+ "%s :dev removal handshake completed successfully: handle(0x%04x)\n",
+ __func__, drv_cmd->dev_handle);
+ clear_bit(drv_cmd->dev_handle, mrioc->removepend_bitmap);
+ }
+
+ if (!list_empty(&mrioc->delayed_rmhs_list)) {
+ delayed_dev_rmhs = list_entry(mrioc->delayed_rmhs_list.next,
+ struct delayed_dev_rmhs_node, list);
+ drv_cmd->dev_handle = delayed_dev_rmhs->handle;
+ drv_cmd->retry_count = 0;
+ drv_cmd->iou_rc = delayed_dev_rmhs->iou_rc;
+ ioc_info(mrioc,
+ "%s :dev_rmhs_iouctrl_complete: processing delayed TM: handle(0x%04x)\n",
+ __func__, drv_cmd->dev_handle);
+ mpi3mr_dev_rmhs_send_tm(mrioc, drv_cmd->dev_handle, drv_cmd,
+ drv_cmd->iou_rc);
+ list_del(&delayed_dev_rmhs->list);
+ kfree(delayed_dev_rmhs);
+ return;
+ }
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ drv_cmd->callback = NULL;
+ drv_cmd->retry_count = 0;
+ drv_cmd->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ clear_bit(cmd_idx, mrioc->devrem_bitmap);
+}
+
+/**
+ * mpi3mr_dev_rmhs_complete_tm - Device removal TM completion
+ * @mrioc: Adapter instance reference
+ * @drv_cmd: Internal command tracker
+ *
+ * Issues a target reset TM to the firmware from the device
+ * removal TM pend list or issue IO unit control request as
+ * part of device removal or hidden acknowledgment handshake.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_dev_rmhs_complete_tm(struct mpi3mr_ioc *mrioc,
+ struct mpi3mr_drv_cmd *drv_cmd)
+{
+ struct mpi3_iounit_control_request iou_ctrl;
+ u16 cmd_idx = drv_cmd->host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN;
+ struct mpi3_scsi_task_mgmt_reply *tm_reply = NULL;
+ int retval;
+
+ if (drv_cmd->state & MPI3MR_CMD_REPLY_VALID)
+ tm_reply = (struct mpi3_scsi_task_mgmt_reply *)drv_cmd->reply;
+
+ if (tm_reply)
+ pr_info(IOCNAME
+ "dev_rmhs_tr_complete:handle(0x%04x), ioc_status(0x%04x), loginfo(0x%08x), term_count(%d)\n",
+ mrioc->name, drv_cmd->dev_handle, drv_cmd->ioc_status,
+ drv_cmd->ioc_loginfo,
+ le32_to_cpu(tm_reply->termination_count));
+
+ pr_info(IOCNAME "Issuing IOU CTL: handle(0x%04x) dev_rmhs idx(%d)\n",
+ mrioc->name, drv_cmd->dev_handle, cmd_idx);
+
+ memset(&iou_ctrl, 0, sizeof(iou_ctrl));
+
+ drv_cmd->state = MPI3MR_CMD_PENDING;
+ drv_cmd->is_waiting = 0;
+ drv_cmd->callback = mpi3mr_dev_rmhs_complete_iou;
+ iou_ctrl.operation = drv_cmd->iou_rc;
+ iou_ctrl.param16[0] = cpu_to_le16(drv_cmd->dev_handle);
+ iou_ctrl.host_tag = cpu_to_le16(drv_cmd->host_tag);
+ iou_ctrl.function = MPI3_FUNCTION_IO_UNIT_CONTROL;
+
+ retval = mpi3mr_admin_request_post(mrioc, &iou_ctrl, sizeof(iou_ctrl),
+ 1);
+ if (retval) {
+ pr_err(IOCNAME "Issue DevRmHsTMIOUCTL: Admin post failed\n",
+ mrioc->name);
+ goto out_failed;
+ }
+
+ return;
+out_failed:
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ drv_cmd->callback = NULL;
+ drv_cmd->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ drv_cmd->retry_count = 0;
+ clear_bit(cmd_idx, mrioc->devrem_bitmap);
+}
+
+/**
+ * mpi3mr_dev_rmhs_send_tm - Issue TM for device removal
+ * @mrioc: Adapter instance reference
+ * @handle: Device handle
+ * @cmdparam: Internal command tracker
+ * @iou_rc: IO unit reason code
+ *
+ * Issues a target reset TM to the firmware or add it to a pend
+ * list as part of device removal or hidden acknowledgment
+ * handshake.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_dev_rmhs_send_tm(struct mpi3mr_ioc *mrioc, u16 handle,
+ struct mpi3mr_drv_cmd *cmdparam, u8 iou_rc)
+{
+ struct mpi3_scsi_task_mgmt_request tm_req;
+ int retval = 0;
+ u16 cmd_idx = MPI3MR_NUM_DEVRMCMD;
+ u8 retrycount = 5;
+ struct mpi3mr_drv_cmd *drv_cmd = cmdparam;
+ struct delayed_dev_rmhs_node *delayed_dev_rmhs = NULL;
+
+ if (drv_cmd)
+ goto issue_cmd;
+ do {
+ cmd_idx = find_first_zero_bit(mrioc->devrem_bitmap,
+ MPI3MR_NUM_DEVRMCMD);
+ if (cmd_idx < MPI3MR_NUM_DEVRMCMD) {
+ if (!test_and_set_bit(cmd_idx, mrioc->devrem_bitmap))
+ break;
+ cmd_idx = MPI3MR_NUM_DEVRMCMD;
+ }
+ } while (retrycount--);
+
+ if (cmd_idx >= MPI3MR_NUM_DEVRMCMD) {
+ delayed_dev_rmhs = kzalloc(sizeof(*delayed_dev_rmhs),
+ GFP_ATOMIC);
+ if (!delayed_dev_rmhs)
+ return;
+ INIT_LIST_HEAD(&delayed_dev_rmhs->list);
+ delayed_dev_rmhs->handle = handle;
+ delayed_dev_rmhs->iou_rc = iou_rc;
+ list_add_tail(&delayed_dev_rmhs->list,
+ &mrioc->delayed_rmhs_list);
+ ioc_info(mrioc, "%s :DevRmHs: tr:handle(0x%04x) is postponed\n",
+ __func__, handle);
+ return;
+ }
+ drv_cmd = &mrioc->dev_rmhs_cmds[cmd_idx];
+
+issue_cmd:
+ cmd_idx = drv_cmd->host_tag - MPI3MR_HOSTTAG_DEVRMCMD_MIN;
+ ioc_info(mrioc,
+ "%s :Issuing TR TM: for devhandle 0x%04x with dev_rmhs %d\n",
+ __func__, handle, cmd_idx);
+
+ memset(&tm_req, 0, sizeof(tm_req));
+ if (drv_cmd->state & MPI3MR_CMD_PENDING) {
+ ioc_err(mrioc, "%s :Issue TM: Command is in use\n", __func__);
+ goto out;
+ }
+ drv_cmd->state = MPI3MR_CMD_PENDING;
+ drv_cmd->is_waiting = 0;
+ drv_cmd->callback = mpi3mr_dev_rmhs_complete_tm;
+ drv_cmd->dev_handle = handle;
+ drv_cmd->iou_rc = iou_rc;
+ tm_req.dev_handle = cpu_to_le16(handle);
+ tm_req.task_type = MPI3_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
+ tm_req.host_tag = cpu_to_le16(drv_cmd->host_tag);
+ tm_req.task_host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INVALID);
+ tm_req.function = MPI3_FUNCTION_SCSI_TASK_MGMT;
+
+ set_bit(handle, mrioc->removepend_bitmap);
+ retval = mpi3mr_admin_request_post(mrioc, &tm_req, sizeof(tm_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "%s :Issue DevRmHsTM: Admin Post failed\n",
+ __func__);
+ goto out_failed;
+ }
+out:
+ return;
+out_failed:
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ drv_cmd->callback = NULL;
+ drv_cmd->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ drv_cmd->retry_count = 0;
+ clear_bit(cmd_idx, mrioc->devrem_bitmap);
+}
+
+/**
+ * mpi3mr_pcietopochg_evt_th - PCIETopologyChange evt tophalf
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Checks for the reason code and based on that either block I/O
+ * to device, or unblock I/O to the device, or start the device
+ * removal handshake with reason as remove with the firmware for
+ * PCIe devices.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_pcietopochg_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ struct mpi3_event_data_pcie_topology_change_list *topo_evt =
+ (struct mpi3_event_data_pcie_topology_change_list *)event_reply->event_data;
+ int i;
+ u16 handle;
+ u8 reason_code;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL;
+
+ for (i = 0; i < topo_evt->num_entries; i++) {
+ handle = le16_to_cpu(topo_evt->port_entry[i].attached_dev_handle);
+ if (!handle)
+ continue;
+ reason_code = topo_evt->port_entry[i].port_status;
+ scsi_tgt_priv_data = NULL;
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle);
+ if (tgtdev && tgtdev->starget && tgtdev->starget->hostdata)
+ scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *)
+ tgtdev->starget->hostdata;
+ switch (reason_code) {
+ case MPI3_EVENT_PCIE_TOPO_PS_NOT_RESPONDING:
+ if (scsi_tgt_priv_data) {
+ scsi_tgt_priv_data->dev_removed = 1;
+ scsi_tgt_priv_data->dev_removedelay = 0;
+ atomic_set(&scsi_tgt_priv_data->block_io, 0);
+ }
+ mpi3mr_dev_rmhs_send_tm(mrioc, handle, NULL,
+ MPI3_CTRL_OP_REMOVE_DEVICE);
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_DELAY_NOT_RESPONDING:
+ if (scsi_tgt_priv_data) {
+ scsi_tgt_priv_data->dev_removedelay = 1;
+ atomic_inc(&scsi_tgt_priv_data->block_io);
+ }
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_RESPONDING:
+ if (scsi_tgt_priv_data &&
+ scsi_tgt_priv_data->dev_removedelay) {
+ scsi_tgt_priv_data->dev_removedelay = 0;
+ atomic_dec_if_positive
+ (&scsi_tgt_priv_data->block_io);
+ }
+ break;
+ case MPI3_EVENT_PCIE_TOPO_PS_PORT_CHANGED:
+ default:
+ break;
+ }
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+}
+
+/**
+ * mpi3mr_sastopochg_evt_th - SASTopologyChange evt tophalf
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Checks for the reason code and based on that either block I/O
+ * to device, or unblock I/O to the device, or start the device
+ * removal handshake with reason as remove with the firmware for
+ * SAS/SATA devices.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_sastopochg_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ struct mpi3_event_data_sas_topology_change_list *topo_evt =
+ (struct mpi3_event_data_sas_topology_change_list *)event_reply->event_data;
+ int i;
+ u16 handle;
+ u8 reason_code;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL;
+
+ for (i = 0; i < topo_evt->num_entries; i++) {
+ handle = le16_to_cpu(topo_evt->phy_entry[i].attached_dev_handle);
+ if (!handle)
+ continue;
+ reason_code = topo_evt->phy_entry[i].status &
+ MPI3_EVENT_SAS_TOPO_PHY_RC_MASK;
+ scsi_tgt_priv_data = NULL;
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle);
+ if (tgtdev && tgtdev->starget && tgtdev->starget->hostdata)
+ scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *)
+ tgtdev->starget->hostdata;
+ switch (reason_code) {
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_TARG_NOT_RESPONDING:
+ if (scsi_tgt_priv_data) {
+ scsi_tgt_priv_data->dev_removed = 1;
+ scsi_tgt_priv_data->dev_removedelay = 0;
+ atomic_set(&scsi_tgt_priv_data->block_io, 0);
+ }
+ mpi3mr_dev_rmhs_send_tm(mrioc, handle, NULL,
+ MPI3_CTRL_OP_REMOVE_DEVICE);
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_DELAY_NOT_RESPONDING:
+ if (scsi_tgt_priv_data) {
+ scsi_tgt_priv_data->dev_removedelay = 1;
+ atomic_inc(&scsi_tgt_priv_data->block_io);
+ }
+ break;
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_RESPONDING:
+ if (scsi_tgt_priv_data &&
+ scsi_tgt_priv_data->dev_removedelay) {
+ scsi_tgt_priv_data->dev_removedelay = 0;
+ atomic_dec_if_positive
+ (&scsi_tgt_priv_data->block_io);
+ }
+ case MPI3_EVENT_SAS_TOPO_PHY_RC_PHY_CHANGED:
+ default:
+ break;
+ }
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+}
+
+/**
+ * mpi3mr_devstatuschg_evt_th - DeviceStatusChange evt tophalf
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Checks for the reason code and based on that either block I/O
+ * to device, or unblock I/O to the device, or start the device
+ * removal handshake with reason as remove/hide acknowledgment
+ * with the firmware.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_devstatuschg_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ u16 dev_handle = 0;
+ u8 ublock = 0, block = 0, hide = 0, delete = 0, remove = 0;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL;
+ struct mpi3_event_data_device_status_change *evtdata =
+ (struct mpi3_event_data_device_status_change *)event_reply->event_data;
+
+ if (mrioc->stop_drv_processing)
+ goto out;
+
+ dev_handle = le16_to_cpu(evtdata->dev_handle);
+
+ switch (evtdata->reason_code) {
+ case MPI3_EVENT_DEV_STAT_RC_INT_DEVICE_RESET_STRT:
+ case MPI3_EVENT_DEV_STAT_RC_INT_IT_NEXUS_RESET_STRT:
+ block = 1;
+ break;
+ case MPI3_EVENT_DEV_STAT_RC_HIDDEN:
+ delete = 1;
+ hide = 1;
+ break;
+ case MPI3_EVENT_DEV_STAT_RC_VD_NOT_RESPONDING:
+ delete = 1;
+ remove = 1;
+ break;
+ case MPI3_EVENT_DEV_STAT_RC_INT_DEVICE_RESET_CMP:
+ case MPI3_EVENT_DEV_STAT_RC_INT_IT_NEXUS_RESET_CMP:
+ ublock = 1;
+ break;
+ default:
+ break;
+ }
+
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle);
+ if (!tgtdev)
+ goto out;
+ if (hide)
+ tgtdev->is_hidden = hide;
+ if (tgtdev->starget && tgtdev->starget->hostdata) {
+ scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *)
+ tgtdev->starget->hostdata;
+ if (block)
+ atomic_inc(&scsi_tgt_priv_data->block_io);
+ if (delete)
+ scsi_tgt_priv_data->dev_removed = 1;
+ if (ublock)
+ atomic_dec_if_positive(&scsi_tgt_priv_data->block_io);
+ }
+ if (remove)
+ mpi3mr_dev_rmhs_send_tm(mrioc, dev_handle, NULL,
+ MPI3_CTRL_OP_REMOVE_DEVICE);
+ if (hide)
+ mpi3mr_dev_rmhs_send_tm(mrioc, dev_handle, NULL,
+ MPI3_CTRL_OP_HIDDEN_ACK);
+
+out:
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+}
+
+/**
+ * mpi3mr_energypackchg_evt_th - Energy pack change evt tophalf
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Identifies the new shutdown timeout value and update.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_energypackchg_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ struct mpi3_event_data_energy_pack_change *evtdata =
+ (struct mpi3_event_data_energy_pack_change *)event_reply->event_data;
+ u16 shutdown_timeout = le16_to_cpu(evtdata->shutdown_timeout);
+
+ if (shutdown_timeout <= 0) {
+ ioc_warn(mrioc,
+ "%s :Invalid Shutdown Timeout received = %d\n",
+ __func__, shutdown_timeout);
+ return;
+ }
+
+ ioc_info(mrioc,
+ "%s :Previous Shutdown Timeout Value = %d New Shutdown Timeout Value = %d\n",
+ __func__, mrioc->facts.shutdown_timeout, shutdown_timeout);
+ mrioc->facts.shutdown_timeout = shutdown_timeout;
+}
+
+/**
+ * mpi3mr_os_handle_events - Firmware event handler
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Identify whteher the event has to handled and acknowledged
+ * and either process the event in the tophalf and/or schedule a
+ * bottom half through mpi3mr_fwevt_worker.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_os_handle_events(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ u16 evt_type, sz;
+ struct mpi3mr_fwevt *fwevt = NULL;
+ bool ack_req = 0, process_evt_bh = 0;
+
+ if (mrioc->stop_drv_processing)
+ return;
+
+ if ((event_reply->msg_flags & MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_MASK)
+ == MPI3_EVENT_NOTIFY_MSGFLAGS_ACK_REQUIRED)
+ ack_req = 1;
+
+ evt_type = event_reply->event;
+
+ switch (evt_type) {
+ case MPI3_EVENT_DEVICE_ADDED:
+ {
+ struct mpi3_device_page0 *dev_pg0 =
+ (struct mpi3_device_page0 *)event_reply->event_data;
+ if (mpi3mr_create_tgtdev(mrioc, dev_pg0))
+ ioc_err(mrioc,
+ "%s :Failed to add device in the device add event\n",
+ __func__);
+ else
+ process_evt_bh = 1;
+ break;
+ }
+ case MPI3_EVENT_DEVICE_STATUS_CHANGE:
+ {
+ process_evt_bh = 1;
+ mpi3mr_devstatuschg_evt_th(mrioc, event_reply);
+ break;
+ }
+ case MPI3_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
+ {
+ process_evt_bh = 1;
+ mpi3mr_sastopochg_evt_th(mrioc, event_reply);
+ break;
+ }
+ case MPI3_EVENT_PCIE_TOPOLOGY_CHANGE_LIST:
+ {
+ process_evt_bh = 1;
+ mpi3mr_pcietopochg_evt_th(mrioc, event_reply);
+ break;
+ }
+ case MPI3_EVENT_DEVICE_INFO_CHANGED:
+ {
+ process_evt_bh = 1;
+ break;
+ }
+ case MPI3_EVENT_ENERGY_PACK_CHANGE:
+ {
+ mpi3mr_energypackchg_evt_th(mrioc, event_reply);
+ break;
+ }
+ case MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE:
+ case MPI3_EVENT_SAS_DISCOVERY:
+ case MPI3_EVENT_CABLE_MGMT:
+ case MPI3_EVENT_SAS_DEVICE_DISCOVERY_ERROR:
+ case MPI3_EVENT_SAS_BROADCAST_PRIMITIVE:
+ case MPI3_EVENT_PCIE_ENUMERATION:
+ break;
+ default:
+ ioc_info(mrioc, "%s :event 0x%02x is not handled\n",
+ __func__, evt_type);
+ break;
+ }
+ if (process_evt_bh || ack_req) {
+ sz = event_reply->event_data_length * 4;
+ fwevt = mpi3mr_alloc_fwevt(sz);
+ if (!fwevt) {
+ ioc_info(mrioc, "%s :failure at %s:%d/%s()!\n",
+ __func__, __FILE__, __LINE__, __func__);
+ return;
+ }
+
+ memcpy(fwevt->event_data, event_reply->event_data, sz);
+ fwevt->mrioc = mrioc;
+ fwevt->event_id = evt_type;
+ fwevt->send_ack = ack_req;
+ fwevt->process_evt = process_evt_bh;
+ fwevt->evt_ctx = le32_to_cpu(event_reply->event_context);
+ mpi3mr_fwevt_add_to_list(mrioc, fwevt);
+ }
+}
+
+/**
+ * mpi3mr_setup_eedp - Setup EEDP information in MPI3 SCSI IO
+ * @mrioc: Adapter instance reference
+ * @scmd: SCSI command reference
+ * @scsiio_req: MPI3 SCSI IO request
+ *
+ * Identifies the protection information flags from the SCSI
+ * command and set appropriate flags in the MPI3 SCSI IO
+ * request.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_setup_eedp(struct mpi3mr_ioc *mrioc,
+ struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req)
+{
+ u16 eedp_flags = 0;
+ unsigned char prot_op = scsi_get_prot_op(scmd);
+ unsigned char prot_type = scsi_get_prot_type(scmd);
+
+ switch (prot_op) {
+ case SCSI_PROT_NORMAL:
+ return;
+ case SCSI_PROT_READ_STRIP:
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK_REMOVE;
+ break;
+ case SCSI_PROT_WRITE_INSERT:
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_INSERT;
+ break;
+ case SCSI_PROT_READ_INSERT:
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_INSERT;
+ scsiio_req->msg_flags |= MPI3_SCSIIO_MSGFLAGS_METASGL_VALID;
+ break;
+ case SCSI_PROT_WRITE_STRIP:
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK_REMOVE;
+ scsiio_req->msg_flags |= MPI3_SCSIIO_MSGFLAGS_METASGL_VALID;
+ break;
+ case SCSI_PROT_READ_PASS:
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK |
+ MPI3_EEDPFLAGS_CHK_REF_TAG | MPI3_EEDPFLAGS_CHK_APP_TAG |
+ MPI3_EEDPFLAGS_CHK_GUARD;
+ scsiio_req->msg_flags |= MPI3_SCSIIO_MSGFLAGS_METASGL_VALID;
+ break;
+ case SCSI_PROT_WRITE_PASS:
+ if (scsi_host_get_guard(scmd->device->host)
+ & SHOST_DIX_GUARD_IP) {
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK_REGEN |
+ MPI3_EEDPFLAGS_CHK_APP_TAG |
+ MPI3_EEDPFLAGS_CHK_GUARD |
+ MPI3_EEDPFLAGS_INCR_PRI_REF_TAG;
+ scsiio_req->sgl[0].eedp.application_tag_translation_mask =
+ 0xffff;
+ } else {
+ eedp_flags = MPI3_EEDPFLAGS_EEDP_OP_CHECK |
+ MPI3_EEDPFLAGS_CHK_REF_TAG |
+ MPI3_EEDPFLAGS_CHK_APP_TAG |
+ MPI3_EEDPFLAGS_CHK_GUARD;
+ }
+ scsiio_req->msg_flags |= MPI3_SCSIIO_MSGFLAGS_METASGL_VALID;
+ break;
+ default:
+ return;
+ }
+
+ if (scsi_host_get_guard(scmd->device->host) & SHOST_DIX_GUARD_IP)
+ eedp_flags |= MPI3_EEDPFLAGS_HOST_GUARD_IP_CHKSUM;
+
+ switch (prot_type) {
+ case SCSI_PROT_DIF_TYPE0:
+ eedp_flags |= MPI3_EEDPFLAGS_INCR_PRI_REF_TAG;
+ scsiio_req->cdb.eedp32.primary_reference_tag =
+ cpu_to_be32(t10_pi_ref_tag(scmd->request));
+ break;
+ case SCSI_PROT_DIF_TYPE1:
+ case SCSI_PROT_DIF_TYPE2:
+ eedp_flags |= MPI3_EEDPFLAGS_INCR_PRI_REF_TAG |
+ MPI3_EEDPFLAGS_ESC_MODE_APPTAG_DISABLE |
+ MPI3_EEDPFLAGS_CHK_GUARD;
+ scsiio_req->cdb.eedp32.primary_reference_tag =
+ cpu_to_be32(t10_pi_ref_tag(scmd->request));
+ break;
+ case SCSI_PROT_DIF_TYPE3:
+ eedp_flags |= MPI3_EEDPFLAGS_CHK_GUARD |
+ MPI3_EEDPFLAGS_ESC_MODE_APPTAG_DISABLE;
+ break;
+
+ default:
+ scsiio_req->msg_flags &= ~(MPI3_SCSIIO_MSGFLAGS_METASGL_VALID);
+ return;
+ }
+
+ switch (scmd->device->sector_size) {
+ case 512:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_512;
+ break;
+ case 520:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_520;
+ break;
+ case 4080:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4080;
+ break;
+ case 4088:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4088;
+ break;
+ case 4096:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4096;
+ break;
+ case 4104:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4104;
+ break;
+ case 4160:
+ scsiio_req->sgl[0].eedp.user_data_size = MPI3_EEDP_UDS_4160;
+ break;
+ default:
+ break;
+ }
+
+ scsiio_req->sgl[0].eedp.eedp_flags = cpu_to_le16(eedp_flags);
+ scsiio_req->sgl[0].eedp.flags = MPI3_SGE_FLAGS_ELEMENT_TYPE_EXTENDED;
+}
+
+/**
+ * mpi3mr_build_sense_buffer - Map sense information
+ * @desc: Sense type
+ * @buf: Sense buffer to populate
+ * @key: Sense key
+ * @asc: Additional sense code
+ * @ascq: Additional sense code qualifier
+ *
+ * Maps the given sense information into either descriptor or
+ * fixed format sense data.
+ *
+ * Return: Nothing
+ */
+static inline void mpi3mr_build_sense_buffer(int desc, u8 *buf, u8 key,
+ u8 asc, u8 ascq)
+{
+ if (desc) {
+ buf[0] = 0x72; /* descriptor, current */
+ buf[1] = key;
+ buf[2] = asc;
+ buf[3] = ascq;
+ buf[7] = 0;
+ } else {
+ buf[0] = 0x70; /* fixed, current */
+ buf[2] = key;
+ buf[7] = 0xa;
+ buf[12] = asc;
+ buf[13] = ascq;
+ }
+}
+
+/**
+ * mpi3mr_map_eedp_error - Map EEDP errors from IOC status
+ * @scmd: SCSI command reference
+ * @ioc_status: status of MPI3 request
+ *
+ * Maps the EEDP error status of the SCSI IO request to sense
+ * data.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_map_eedp_error(struct scsi_cmnd *scmd,
+ u16 ioc_status)
+{
+ u8 ascq = 0;
+
+ switch (ioc_status) {
+ case MPI3_IOCSTATUS_EEDP_GUARD_ERROR:
+ ascq = 0x01;
+ break;
+ case MPI3_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ ascq = 0x02;
+ break;
+ case MPI3_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ ascq = 0x03;
+ break;
+ default:
+ ascq = 0x00;
+ break;
+ }
+
+ mpi3mr_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, ascq);
+ scmd->result = (DID_ABORT << 16) | SAM_STAT_CHECK_CONDITION;
+}
+
+/**
+ * mpi3mr_process_op_reply_desc - reply descriptor handler
+ * @mrioc: Adapter instance reference
+ * @reply_desc: Operational reply descriptor
+ * @reply_dma: place holder for reply DMA address
+ * @qidx: Operational queue index
+ *
+ * Process the operational reply descriptor and identifies the
+ * descriptor type. Based on the descriptor map the MPI3 request
+ * status to a SCSI command status and calls scsi_done call
+ * back.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc,
+ struct mpi3_default_reply_descriptor *reply_desc, u64 *reply_dma, u16 qidx)
+{
+ u16 reply_desc_type, host_tag = 0;
+ u16 ioc_status = MPI3_IOCSTATUS_SUCCESS;
+ u32 ioc_loginfo = 0;
+ struct mpi3_status_reply_descriptor *status_desc = NULL;
+ struct mpi3_address_reply_descriptor *addr_desc = NULL;
+ struct mpi3_success_reply_descriptor *success_desc = NULL;
+ struct mpi3_scsi_io_reply *scsi_reply = NULL;
+ struct scsi_cmnd *scmd = NULL;
+ struct scmd_priv *priv = NULL;
+ u8 *sense_buf = NULL;
+ u8 scsi_state = 0, scsi_status = 0, sense_state = 0;
+ u32 xfer_count = 0, sense_count = 0, resp_data = 0;
+ u16 dev_handle = 0xFFFF;
+ struct scsi_sense_hdr sshdr;
+
+ *reply_dma = 0;
+ reply_desc_type = le16_to_cpu(reply_desc->reply_flags) &
+ MPI3_REPLY_DESCRIPT_FLAGS_TYPE_MASK;
+ switch (reply_desc_type) {
+ case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_STATUS:
+ status_desc = (struct mpi3_status_reply_descriptor *)reply_desc;
+ host_tag = le16_to_cpu(status_desc->host_tag);
+ ioc_status = le16_to_cpu(status_desc->ioc_status);
+ if (ioc_status &
+ MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL)
+ ioc_loginfo = le32_to_cpu(status_desc->ioc_log_info);
+ ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK;
+ break;
+ case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_ADDRESS_REPLY:
+ addr_desc = (struct mpi3_address_reply_descriptor *)reply_desc;
+ *reply_dma = le64_to_cpu(addr_desc->reply_frame_address);
+ scsi_reply = mpi3mr_get_reply_virt_addr(mrioc,
+ *reply_dma);
+ if (!scsi_reply) {
+ panic("%s: scsi_reply is NULL, this shouldn't happen\n",
+ mrioc->name);
+ goto out;
+ }
+ host_tag = le16_to_cpu(scsi_reply->host_tag);
+ ioc_status = le16_to_cpu(scsi_reply->ioc_status);
+ scsi_status = scsi_reply->scsi_status;
+ scsi_state = scsi_reply->scsi_state;
+ dev_handle = le16_to_cpu(scsi_reply->dev_handle);
+ sense_state = (scsi_state & MPI3_SCSI_STATE_SENSE_MASK);
+ xfer_count = le32_to_cpu(scsi_reply->transfer_count);
+ sense_count = le32_to_cpu(scsi_reply->sense_count);
+ resp_data = le32_to_cpu(scsi_reply->response_data);
+ sense_buf = mpi3mr_get_sensebuf_virt_addr(mrioc,
+ le64_to_cpu(scsi_reply->sense_data_buffer_address));
+ if (ioc_status &
+ MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL)
+ ioc_loginfo = le32_to_cpu(scsi_reply->ioc_log_info);
+ ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK;
+ if (sense_state == MPI3_SCSI_STATE_SENSE_BUFF_Q_EMPTY)
+ panic("%s: Ran out of sense buffers\n", mrioc->name);
+ break;
+ case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SUCCESS:
+ success_desc = (struct mpi3_success_reply_descriptor *)reply_desc;
+ host_tag = le16_to_cpu(success_desc->host_tag);
+ break;
+ default:
+ break;
+ }
+ scmd = mpi3mr_scmd_from_host_tag(mrioc, host_tag, qidx);
+ if (!scmd) {
+ panic("%s: Cannot Identify scmd for host_tag 0x%x\n",
+ mrioc->name, host_tag);
+ goto out;
+ }
+ priv = scsi_cmd_priv(scmd);
+ if (success_desc) {
+ scmd->result = DID_OK << 16;
+ goto out_success;
+ }
+ if (ioc_status == MPI3_IOCSTATUS_SCSI_DATA_UNDERRUN &&
+ xfer_count == 0 && (scsi_status == MPI3_SCSI_STATUS_BUSY ||
+ scsi_status == MPI3_SCSI_STATUS_RESERVATION_CONFLICT ||
+ scsi_status == MPI3_SCSI_STATUS_TASK_SET_FULL))
+ ioc_status = MPI3_IOCSTATUS_SUCCESS;
+
+ if ((sense_state == MPI3_SCSI_STATE_SENSE_VALID) && sense_count &&
+ sense_buf) {
+ u32 sz = min_t(u32, SCSI_SENSE_BUFFERSIZE, sense_count);
+
+ memcpy(scmd->sense_buffer, sense_buf, sz);
+ }
+
+ switch (ioc_status) {
+ case MPI3_IOCSTATUS_BUSY:
+ case MPI3_IOCSTATUS_INSUFFICIENT_RESOURCES:
+ scmd->result = SAM_STAT_BUSY;
+ break;
+ case MPI3_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
+ scmd->result = DID_NO_CONNECT << 16;
+ break;
+ case MPI3_IOCSTATUS_SCSI_IOC_TERMINATED:
+ scmd->result = DID_SOFT_ERROR << 16;
+ break;
+ case MPI3_IOCSTATUS_SCSI_TASK_TERMINATED:
+ case MPI3_IOCSTATUS_SCSI_EXT_TERMINATED:
+ scmd->result = DID_RESET << 16;
+ break;
+ case MPI3_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
+ if ((xfer_count == 0) || (scmd->underflow > xfer_count))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else
+ scmd->result = (DID_OK << 16) | scsi_status;
+ break;
+ case MPI3_IOCSTATUS_SCSI_DATA_UNDERRUN:
+ scmd->result = (DID_OK << 16) | scsi_status;
+ if (sense_state == MPI3_SCSI_STATE_SENSE_VALID)
+ break;
+ if (xfer_count < scmd->underflow) {
+ if (scsi_status == SAM_STAT_BUSY)
+ scmd->result = SAM_STAT_BUSY;
+ else
+ scmd->result = DID_SOFT_ERROR << 16;
+ } else if ((scsi_state & (MPI3_SCSI_STATE_NO_SCSI_STATUS)) ||
+ (sense_state != MPI3_SCSI_STATE_SENSE_NOT_AVAILABLE))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else if (scsi_state & MPI3_SCSI_STATE_TERMINATED)
+ scmd->result = DID_RESET << 16;
+ break;
+ case MPI3_IOCSTATUS_SCSI_DATA_OVERRUN:
+ scsi_set_resid(scmd, 0);
+ fallthrough;
+ case MPI3_IOCSTATUS_SCSI_RECOVERED_ERROR:
+ case MPI3_IOCSTATUS_SUCCESS:
+ scmd->result = (DID_OK << 16) | scsi_status;
+ if ((scsi_state & (MPI3_SCSI_STATE_NO_SCSI_STATUS)) ||
+ (sense_state == MPI3_SCSI_STATE_SENSE_FAILED) ||
+ (sense_state == MPI3_SCSI_STATE_SENSE_BUFF_Q_EMPTY))
+ scmd->result = DID_SOFT_ERROR << 16;
+ else if (scsi_state & MPI3_SCSI_STATE_TERMINATED)
+ scmd->result = DID_RESET << 16;
+ break;
+ case MPI3_IOCSTATUS_EEDP_GUARD_ERROR:
+ case MPI3_IOCSTATUS_EEDP_REF_TAG_ERROR:
+ case MPI3_IOCSTATUS_EEDP_APP_TAG_ERROR:
+ mpi3mr_map_eedp_error(scmd, ioc_status);
+ break;
+ case MPI3_IOCSTATUS_SCSI_PROTOCOL_ERROR:
+ case MPI3_IOCSTATUS_INVALID_FUNCTION:
+ case MPI3_IOCSTATUS_INVALID_SGL:
+ case MPI3_IOCSTATUS_INTERNAL_ERROR:
+ case MPI3_IOCSTATUS_INVALID_FIELD:
+ case MPI3_IOCSTATUS_INVALID_STATE:
+ case MPI3_IOCSTATUS_SCSI_IO_DATA_ERROR:
+ case MPI3_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
+ case MPI3_IOCSTATUS_INSUFFICIENT_POWER:
+ default:
+ scmd->result = DID_SOFT_ERROR << 16;
+ break;
+ }
+
+ if (scmd->result != (DID_OK << 16) && (scmd->cmnd[0] != ATA_12) &&
+ (scmd->cmnd[0] != ATA_16)) {
+ ioc_info(mrioc, "%s :scmd->result 0x%x\n", __func__,
+ scmd->result);
+ scsi_print_command(scmd);
+ ioc_info(mrioc,
+ "%s :Command issued to handle 0x%02x returned with error 0x%04x loginfo 0x%08x, qid %d\n",
+ __func__, dev_handle, ioc_status, ioc_loginfo,
+ priv->req_q_idx + 1);
+ ioc_info(mrioc,
+ " host_tag %d scsi_state 0x%02x scsi_status 0x%02x, xfer_cnt %d resp_data 0x%x\n",
+ host_tag, scsi_state, scsi_status, xfer_count, resp_data);
+ if (sense_buf) {
+ scsi_normalize_sense(sense_buf, sense_count, &sshdr);
+ ioc_info(mrioc,
+ "%s :sense_count 0x%x, sense_key 0x%x ASC 0x%x, ASCQ 0x%x\n",
+ __func__, sense_count, sshdr.sense_key,
+ sshdr.asc, sshdr.ascq);
+ }
+ }
+out_success:
+ if (priv->meta_sg_valid) {
+ dma_unmap_sg(&mrioc->pdev->dev, scsi_prot_sglist(scmd),
+ scsi_prot_sg_count(scmd), scmd->sc_data_direction);
+ }
+ mpi3mr_clear_scmd_priv(mrioc, scmd);
+ scsi_dma_unmap(scmd);
+ scmd->scsi_done(scmd);
+out:
+ if (sense_buf)
+ mpi3mr_repost_sense_buf(mrioc,
+ le64_to_cpu(scsi_reply->sense_data_buffer_address));
+}
+
+/**
+ * mpi3mr_get_chain_idx - get free chain buffer index
+ * @mrioc: Adapter instance reference
+ *
+ * Try to get a free chain buffer index from the free pool.
+ *
+ * Return: -1 on failure or the free chain buffer index
+ */
+static int mpi3mr_get_chain_idx(struct mpi3mr_ioc *mrioc)
+{
+ u8 retry_count = 5;
+ int cmd_idx = -1;
+
+ do {
+ spin_lock(&mrioc->chain_buf_lock);
+ cmd_idx = find_first_zero_bit(mrioc->chain_bitmap,
+ mrioc->chain_buf_count);
+ if (cmd_idx < mrioc->chain_buf_count) {
+ set_bit(cmd_idx, mrioc->chain_bitmap);
+ spin_unlock(&mrioc->chain_buf_lock);
+ break;
+ }
+ spin_unlock(&mrioc->chain_buf_lock);
+ cmd_idx = -1;
+ } while (retry_count--);
+ return cmd_idx;
+}
+
+/**
+ * mpi3mr_prepare_sg_scmd - build scatter gather list
+ * @mrioc: Adapter instance reference
+ * @scmd: SCSI command reference
+ * @scsiio_req: MPI3 SCSI IO request
+ *
+ * This function maps SCSI command's data and protection SGEs to
+ * MPI request SGEs. If required additional 4K chain buffer is
+ * used to send the SGEs.
+ *
+ * Return: 0 on success, -ENOMEM on dma_map_sg failure
+ */
+static int mpi3mr_prepare_sg_scmd(struct mpi3mr_ioc *mrioc,
+ struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req)
+{
+ dma_addr_t chain_dma;
+ struct scatterlist *sg_scmd;
+ void *sg_local, *chain;
+ u32 chain_length;
+ int sges_left, chain_idx;
+ u32 sges_in_segment;
+ u8 simple_sgl_flags;
+ u8 simple_sgl_flags_last;
+ u8 last_chain_sgl_flags;
+ struct chain_element *chain_req;
+ struct scmd_priv *priv = NULL;
+ u32 meta_sg = le32_to_cpu(scsiio_req->flags) &
+ MPI3_SCSIIO_FLAGS_DMAOPERATION_HOST_PI;
+
+ priv = scsi_cmd_priv(scmd);
+
+ simple_sgl_flags = MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE |
+ MPI3_SGE_FLAGS_DLAS_SYSTEM;
+ simple_sgl_flags_last = simple_sgl_flags |
+ MPI3_SGE_FLAGS_END_OF_LIST;
+ last_chain_sgl_flags = MPI3_SGE_FLAGS_ELEMENT_TYPE_LAST_CHAIN |
+ MPI3_SGE_FLAGS_DLAS_SYSTEM;
+
+ if (meta_sg)
+ sg_local = &scsiio_req->sgl[MPI3_SCSIIO_METASGL_INDEX];
+ else
+ sg_local = &scsiio_req->sgl;
+
+ if (!scsiio_req->data_length && !meta_sg) {
+ mpi3mr_build_zero_len_sge(sg_local);
+ return 0;
+ }
+
+ if (meta_sg) {
+ sg_scmd = scsi_prot_sglist(scmd);
+ sges_left = dma_map_sg(&mrioc->pdev->dev,
+ scsi_prot_sglist(scmd),
+ scsi_prot_sg_count(scmd),
+ scmd->sc_data_direction);
+ priv->meta_sg_valid = 1; /* To unmap meta sg DMA */
+ } else {
+ sg_scmd = scsi_sglist(scmd);
+ sges_left = scsi_dma_map(scmd);
+ }
+
+ if (sges_left < 0) {
+ sdev_printk(KERN_ERR, scmd->device,
+ "scsi_dma_map failed: request for %d bytes!\n",
+ scsi_bufflen(scmd));
+ return -ENOMEM;
+ }
+ if (sges_left > MPI3MR_SG_DEPTH) {
+ sdev_printk(KERN_ERR, scmd->device,
+ "scsi_dma_map returned unsupported sge count %d!\n",
+ sges_left);
+ return -ENOMEM;
+ }
+
+ sges_in_segment = (mrioc->facts.op_req_sz -
+ offsetof(struct mpi3_scsi_io_request, sgl)) / sizeof(struct mpi3_sge_common);
+
+ if (scsiio_req->sgl[0].eedp.flags ==
+ MPI3_SGE_FLAGS_ELEMENT_TYPE_EXTENDED && !meta_sg) {
+ sg_local += sizeof(struct mpi3_sge_common);
+ sges_in_segment--;
+ /* Reserve 1st segment (scsiio_req->sgl[0]) for eedp */
+ }
+
+ if (scsiio_req->msg_flags ==
+ MPI3_SCSIIO_MSGFLAGS_METASGL_VALID && !meta_sg) {
+ sges_in_segment--;
+ /* Reserve last segment (scsiio_req->sgl[3]) for meta sg */
+ }
+
+ if (meta_sg)
+ sges_in_segment = 1;
+
+ if (sges_left <= sges_in_segment)
+ goto fill_in_last_segment;
+
+ /* fill in main message segment when there is a chain following */
+ while (sges_in_segment > 1) {
+ mpi3mr_add_sg_single(sg_local, simple_sgl_flags,
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += sizeof(struct mpi3_sge_common);
+ sges_left--;
+ sges_in_segment--;
+ }
+
+ chain_idx = mpi3mr_get_chain_idx(mrioc);
+ if (chain_idx < 0)
+ return -1;
+ chain_req = &mrioc->chain_sgl_list[chain_idx];
+ if (meta_sg)
+ priv->meta_chain_idx = chain_idx;
+ else
+ priv->chain_idx = chain_idx;
+
+ chain = chain_req->addr;
+ chain_dma = chain_req->dma_addr;
+ sges_in_segment = sges_left;
+ chain_length = sges_in_segment * sizeof(struct mpi3_sge_common);
+
+ mpi3mr_add_sg_single(sg_local, last_chain_sgl_flags,
+ chain_length, chain_dma);
+
+ sg_local = chain;
+
+fill_in_last_segment:
+ while (sges_left > 0) {
+ if (sges_left == 1)
+ mpi3mr_add_sg_single(sg_local,
+ simple_sgl_flags_last, sg_dma_len(sg_scmd),
+ sg_dma_address(sg_scmd));
+ else
+ mpi3mr_add_sg_single(sg_local, simple_sgl_flags,
+ sg_dma_len(sg_scmd), sg_dma_address(sg_scmd));
+ sg_scmd = sg_next(sg_scmd);
+ sg_local += sizeof(struct mpi3_sge_common);
+ sges_left--;
+ }
+
+ return 0;
+}
+
+/**
+ * mpi3mr_build_sg_scmd - build scatter gather list for SCSI IO
+ * @mrioc: Adapter instance reference
+ * @scmd: SCSI command reference
+ * @scsiio_req: MPI3 SCSI IO request
+ *
+ * This function calls mpi3mr_prepare_sg_scmd for constructing
+ * both data SGEs and protection information SGEs in the MPI
+ * format from the SCSI Command as appropriate .
+ *
+ * Return: return value of mpi3mr_prepare_sg_scmd.
+ */
+static int mpi3mr_build_sg_scmd(struct mpi3mr_ioc *mrioc,
+ struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req)
+{
+ int ret;
+
+ ret = mpi3mr_prepare_sg_scmd(mrioc, scmd, scsiio_req);
+ if (ret)
+ return ret;
+
+ if (scsiio_req->msg_flags == MPI3_SCSIIO_MSGFLAGS_METASGL_VALID) {
+ /* There is a valid meta sg */
+ scsiio_req->flags |=
+ cpu_to_le32(MPI3_SCSIIO_FLAGS_DMAOPERATION_HOST_PI);
+ ret = mpi3mr_prepare_sg_scmd(mrioc, scmd, scsiio_req);
+ }
+
+ return ret;
+}
+
+/**
+ * mpi3mr_print_response_code - print TM response as a string
+ * @mrioc: Adapter instance reference
+ * @resp_code: TM response code
+ *
+ * Print TM response code as a readable string.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_print_response_code(struct mpi3mr_ioc *mrioc, u8 resp_code)
+{
+ char *desc;
+
+ switch (resp_code) {
+ case MPI3MR_RSP_TM_COMPLETE:
+ desc = "task management request completed";
+ break;
+ case MPI3MR_RSP_INVALID_FRAME:
+ desc = "invalid frame";
+ break;
+ case MPI3MR_RSP_TM_NOT_SUPPORTED:
+ desc = "task management request not supported";
+ break;
+ case MPI3MR_RSP_TM_FAILED:
+ desc = "task management request failed";
+ break;
+ case MPI3MR_RSP_TM_SUCCEEDED:
+ desc = "task management request succeeded";
+ break;
+ case MPI3MR_RSP_TM_INVALID_LUN:
+ desc = "invalid lun";
+ break;
+ case MPI3MR_RSP_TM_OVERLAPPED_TAG:
+ desc = "overlapped tag attempted";
+ break;
+ case MPI3MR_RSP_IO_QUEUED_ON_IOC:
+ desc = "task queued, however not sent to target";
+ break;
+ default:
+ desc = "unknown";
+ break;
+ }
+ ioc_info(mrioc, "%s :response_code(0x%01x): %s\n", __func__,
+ resp_code, desc);
+}
+
+/**
+ * mpi3mr_issue_tm - Issue Task Management request
+ * @mrioc: Adapter instance reference
+ * @tm_type: Task Management type
+ * @handle: Device handle
+ * @lun: lun ID
+ * @htag: Host tag of the TM request
+ * @drv_cmd: Internal command tracker
+ * @resp_code: Response code place holder
+ * @cmd_priv: SCSI command private data
+ *
+ * Issues a Task Management Request to the controller for a
+ * specified target, lun and command and wait for its completion
+ * and check TM response. Recover the TM if it timed out by
+ * issuing controller reset.
+ *
+ * Return: 0 on success, non-zero on errors
+ */
+static int mpi3mr_issue_tm(struct mpi3mr_ioc *mrioc, u8 tm_type,
+ u16 handle, uint lun, u16 htag, ulong timeout,
+ struct mpi3mr_drv_cmd *drv_cmd,
+ u8 *resp_code, struct scmd_priv *cmd_priv)
+{
+ struct mpi3_scsi_task_mgmt_request tm_req;
+ struct mpi3_scsi_task_mgmt_reply *tm_reply = NULL;
+ int retval = 0;
+ struct mpi3mr_tgt_dev *tgtdev = NULL;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL;
+ struct op_req_qinfo *op_req_q = NULL;
+
+ ioc_info(mrioc, "%s :Issue TM: TM type (0x%x) for devhandle 0x%04x\n",
+ __func__, tm_type, handle);
+ if (mrioc->unrecoverable) {
+ retval = -1;
+ ioc_err(mrioc, "%s :Issue TM: Unrecoverable controller\n",
+ __func__);
+ goto out;
+ }
+
+ memset(&tm_req, 0, sizeof(tm_req));
+ mutex_lock(&drv_cmd->mutex);
+ if (drv_cmd->state & MPI3MR_CMD_PENDING) {
+ retval = -1;
+ ioc_err(mrioc, "%s :Issue TM: Command is in use\n", __func__);
+ mutex_unlock(&drv_cmd->mutex);
+ goto out;
+ }
+ if (mrioc->reset_in_progress) {
+ retval = -1;
+ ioc_err(mrioc, "%s :Issue TM: Reset in progress\n", __func__);
+ mutex_unlock(&drv_cmd->mutex);
+ goto out;
+ }
+
+ drv_cmd->state = MPI3MR_CMD_PENDING;
+ drv_cmd->is_waiting = 1;
+ drv_cmd->callback = NULL;
+ tm_req.dev_handle = cpu_to_le16(handle);
+ tm_req.task_type = tm_type;
+ tm_req.host_tag = cpu_to_le16(htag);
+
+ int_to_scsilun(lun, (struct scsi_lun *)tm_req.lun);
+ tm_req.function = MPI3_FUNCTION_SCSI_TASK_MGMT;
+
+ tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, handle);
+ if (tgtdev && tgtdev->starget && tgtdev->starget->hostdata) {
+ scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *)
+ tgtdev->starget->hostdata;
+ atomic_inc(&scsi_tgt_priv_data->block_io);
+ }
+ if (cmd_priv) {
+ op_req_q = &mrioc->req_qinfo[cmd_priv->req_q_idx];
+ tm_req.task_host_tag = cpu_to_le16(cmd_priv->host_tag);
+ tm_req.task_request_queue_id = cpu_to_le16(op_req_q->qid);
+ }
+ if (tgtdev && (tgtdev->dev_type == MPI3_DEVICE_DEVFORM_PCIE)) {
+ if (cmd_priv && tgtdev->dev_spec.pcie_inf.abort_to)
+ timeout = tgtdev->dev_spec.pcie_inf.abort_to;
+ else if (!cmd_priv && tgtdev->dev_spec.pcie_inf.reset_to)
+ timeout = tgtdev->dev_spec.pcie_inf.reset_to;
+ }
+
+ init_completion(&drv_cmd->done);
+ retval = mpi3mr_admin_request_post(mrioc, &tm_req, sizeof(tm_req), 1);
+ if (retval) {
+ ioc_err(mrioc, "%s :Issue TM: Admin Post failed\n", __func__);
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&drv_cmd->done, (timeout * HZ));
+
+ if (!(drv_cmd->state & MPI3MR_CMD_COMPLETE)) {
+ ioc_err(mrioc, "%s :Issue TM: command timed out\n", __func__);
+ drv_cmd->is_waiting = 0;
+ retval = -1;
+ mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_TM_TIMEOUT, 1);
+ goto out_unlock;
+ }
+
+ if (drv_cmd->state & MPI3MR_CMD_REPLY_VALID)
+ tm_reply = (struct mpi3_scsi_task_mgmt_reply *)drv_cmd->reply;
+
+ if (drv_cmd->ioc_status != MPI3_IOCSTATUS_SUCCESS) {
+ ioc_err(mrioc,
+ "%s :Issue TM: handle(0x%04x) Failed ioc_status(0x%04x) Loginfo(0x%08x)\n",
+ __func__, handle, drv_cmd->ioc_status,
+ drv_cmd->ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+
+ if (!tm_reply) {
+ ioc_err(mrioc, "%s :Issue TM: No TM Reply message\n", __func__);
+ retval = -1;
+ goto out_unlock;
+ }
+
+ *resp_code = le32_to_cpu(tm_reply->response_data) &
+ MPI3MR_RI_MASK_RESPCODE;
+ switch (*resp_code) {
+ case MPI3MR_RSP_TM_SUCCEEDED:
+ case MPI3MR_RSP_TM_COMPLETE:
+ break;
+ case MPI3MR_RSP_IO_QUEUED_ON_IOC:
+ if (tm_type != MPI3_SCSITASKMGMT_TASKTYPE_QUERY_TASK)
+ retval = -1;
+ break;
+ default:
+ retval = -1;
+ break;
+ }
+
+ ioc_info(mrioc,
+ "%s :Issue TM: Completed TM type (0x%x) handle(0x%04x) ",
+ __func__, tm_type, handle);
+ ioc_info(mrioc,
+ "with ioc_status(0x%04x), loginfo(0x%08x), term_count(0x%08x)\n",
+ drv_cmd->ioc_status, drv_cmd->ioc_loginfo,
+ le32_to_cpu(tm_reply->termination_count));
+ mpi3mr_print_response_code(mrioc, *resp_code);
+
+out_unlock:
+ drv_cmd->state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&drv_cmd->mutex);
+ if (scsi_tgt_priv_data)
+ atomic_dec_if_positive(&scsi_tgt_priv_data->block_io);
+ if (tgtdev)
+ mpi3mr_tgtdev_put(tgtdev);
+ if (!retval) {
+ /*
+ * Flush all IRQ handlers by calling synchronize_irq().
+ * mpi3mr_ioc_disable_intr() takes care of it.
+ */
+ mpi3mr_ioc_disable_intr(mrioc);
+ mpi3mr_ioc_enable_intr(mrioc);
+ }
+out:
+ return retval;
+}
+
+/**
+ * mpi3mr_bios_param - BIOS param callback
+ * @sdev: SCSI device reference
+ * @bdev: Block device reference
+ * @capacity: Capacity in logical sectors
+ * @params: Parameter array
+ *
+ * Just the parameters with heads/secots/cylinders.
+ *
+ * Return: 0 always
+ */
+static int mpi3mr_bios_param(struct scsi_device *sdev,
+ struct block_device *bdev, sector_t capacity, int params[])
+{
+ int heads;
+ int sectors;
+ sector_t cylinders;
+ ulong dummy;
+
+ heads = 64;
+ sectors = 32;
+
+ dummy = heads * sectors;
+ cylinders = capacity;
+ sector_div(cylinders, dummy);
+
+ if ((ulong)capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ dummy = heads * sectors;
+ cylinders = capacity;
+ sector_div(cylinders, dummy);
+ }
+
+ params[0] = heads;
+ params[1] = sectors;
+ params[2] = cylinders;
+ return 0;
+}
+
+/**
+ * mpi3mr_map_queues - Map queues callback handler
+ * @shost: SCSI host reference
+ *
+ * Call the blk_mq_pci_map_queues with from which operational
+ * queue the mapping has to be done
+ *
+ * Return: return of blk_mq_pci_map_queues
+ */
+static int mpi3mr_map_queues(struct Scsi_Host *shost)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+
+ return blk_mq_pci_map_queues(&shost->tag_set.map[HCTX_TYPE_DEFAULT],
+ mrioc->pdev, mrioc->op_reply_q_offset);
+}
+
+/**
+ * mpi3mr_get_fw_pending_ios - Calculate pending I/O count
+ * @mrioc: Adapter instance reference
+ *
+ * Calculate the pending I/Os for the controller and return.
+ *
+ * Return: Number of pending I/Os
+ */
+static inline int mpi3mr_get_fw_pending_ios(struct mpi3mr_ioc *mrioc)
+{
+ u16 i;
+ uint pend_ios = 0;
+
+ for (i = 0; i < mrioc->num_op_reply_q; i++)
+ pend_ios += atomic_read(&mrioc->op_reply_qinfo[i].pend_ios);
+ return pend_ios;
+}
+
+/**
+ * mpi3mr_print_pending_host_io - print pending I/Os
+ * @mrioc: Adapter instance reference
+ *
+ * Print number of pending I/Os and each I/O details prior to
+ * reset for debug purpose.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_print_pending_host_io(struct mpi3mr_ioc *mrioc)
+{
+ struct Scsi_Host *shost = mrioc->shost;
+
+ ioc_info(mrioc, "%s :Pending commands prior to reset: %d\n",
+ __func__, mpi3mr_get_fw_pending_ios(mrioc));
+ blk_mq_tagset_busy_iter(&shost->tag_set,
+ mpi3mr_print_scmd, (void *)mrioc);
+}
+
+/**
+ * mpi3mr_wait_for_host_io - block for I/Os to complete
+ * @mrioc: Adapter instance reference
+ * @timeout: time out in seconds
+ * Waits for pending I/Os for the given adapter to complete or
+ * to hit the timeout.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_wait_for_host_io(struct mpi3mr_ioc *mrioc, u32 timeout)
+{
+ enum mpi3mr_iocstate iocstate;
+ int i = 0;
+
+ iocstate = mpi3mr_get_iocstate(mrioc);
+ if (iocstate != MRIOC_STATE_READY)
+ return;
+
+ if (!mpi3mr_get_fw_pending_ios(mrioc))
+ return;
+ ioc_info(mrioc,
+ "%s :Waiting for %d seconds prior to reset for %d I/O\n",
+ __func__, timeout, mpi3mr_get_fw_pending_ios(mrioc));
+
+ for (i = 0; i < timeout; i++) {
+ if (!mpi3mr_get_fw_pending_ios(mrioc))
+ break;
+ iocstate = mpi3mr_get_iocstate(mrioc);
+ if (iocstate != MRIOC_STATE_READY)
+ break;
+ msleep(1000);
+ }
+
+ ioc_info(mrioc, "%s :Pending I/Os after wait is: %d\n", __func__,
+ mpi3mr_get_fw_pending_ios(mrioc));
+}
+
+/**
+ * mpi3mr_eh_host_reset - Host reset error handling callback
+ * @scmd: SCSI command reference
+ *
+ * Issue controller reset if the scmd is for a Physical Device,
+ * if the scmd is for RAID volume, then wait for
+ * MPI3MR_RAID_ERRREC_RESET_TIMEOUT and checke whether any
+ * pending I/Os prior to issuing reset to the controller.
+ *
+ * Return: SUCCESS of successful reset else FAILED
+ */
+static int mpi3mr_eh_host_reset(struct scsi_cmnd *scmd)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(scmd->device->host);
+ struct mpi3mr_stgt_priv_data *stgt_priv_data;
+ struct mpi3mr_sdev_priv_data *sdev_priv_data;
+ u8 dev_type = MPI3_DEVICE_DEVFORM_VD;
+ int retval = FAILED, ret;
+
+ sdev_priv_data = scmd->device->hostdata;
+ if (sdev_priv_data && sdev_priv_data->tgt_priv_data) {
+ stgt_priv_data = sdev_priv_data->tgt_priv_data;
+ dev_type = stgt_priv_data->dev_type;
+ }
+
+ if (dev_type == MPI3_DEVICE_DEVFORM_VD) {
+ mpi3mr_wait_for_host_io(mrioc,
+ MPI3MR_RAID_ERRREC_RESET_TIMEOUT);
+ if (!mpi3mr_get_fw_pending_ios(mrioc)) {
+ retval = SUCCESS;
+ goto out;
+ }
+ }
+
+ mpi3mr_print_pending_host_io(mrioc);
+ ret = mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_EH_HOS, 1);
+ if (ret)
+ goto out;
+
+ retval = SUCCESS;
+out:
+ sdev_printk(KERN_INFO, scmd->device,
+ "Host reset is %s for scmd(%p)\n",
+ ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_eh_target_reset - Target reset error handling callback
+ * @scmd: SCSI command reference
+ *
+ * Issue Target reset Task Management and verify the scmd is
+ * terminated successfully and return status accordingly.
+ *
+ * Return: SUCCESS of successful termination of the scmd else
+ * FAILED
+ */
+static int mpi3mr_eh_target_reset(struct scsi_cmnd *scmd)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(scmd->device->host);
+ struct mpi3mr_stgt_priv_data *stgt_priv_data;
+ struct mpi3mr_sdev_priv_data *sdev_priv_data;
+ u16 dev_handle;
+ u8 resp_code = 0;
+ int retval = FAILED, ret = 0;
+
+ sdev_printk(KERN_INFO, scmd->device,
+ "Attempting Target Reset! scmd(%p)\n", scmd);
+ scsi_print_command(scmd);
+
+ sdev_priv_data = scmd->device->hostdata;
+ if (!sdev_priv_data || !sdev_priv_data->tgt_priv_data) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "SCSI device is not available\n");
+ retval = SUCCESS;
+ goto out;
+ }
+
+ stgt_priv_data = sdev_priv_data->tgt_priv_data;
+ dev_handle = stgt_priv_data->dev_handle;
+ sdev_printk(KERN_INFO, scmd->device,
+ "Target Reset is issued to handle(0x%04x)\n",
+ dev_handle);
+
+ ret = mpi3mr_issue_tm(mrioc,
+ MPI3_SCSITASKMGMT_TASKTYPE_TARGET_RESET, dev_handle,
+ sdev_priv_data->lun_id, MPI3MR_HOSTTAG_BLK_TMS,
+ MPI3MR_RESETTM_TIMEOUT, &mrioc->host_tm_cmds, &resp_code, NULL);
+
+ if (ret)
+ goto out;
+
+ retval = SUCCESS;
+out:
+ sdev_printk(KERN_INFO, scmd->device,
+ "Target reset is %s for scmd(%p)\n",
+ ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_eh_dev_reset- Device reset error handling callback
+ * @scmd: SCSI command reference
+ *
+ * Issue lun reset Task Management and verify the scmd is
+ * terminated successfully and return status accordingly.
+ *
+ * Return: SUCCESS of successful termination of the scmd else
+ * FAILED
+ */
+static int mpi3mr_eh_dev_reset(struct scsi_cmnd *scmd)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(scmd->device->host);
+ struct mpi3mr_stgt_priv_data *stgt_priv_data;
+ struct mpi3mr_sdev_priv_data *sdev_priv_data;
+ u16 dev_handle;
+ u8 resp_code = 0;
+ int retval = FAILED, ret = 0;
+
+ sdev_printk(KERN_INFO, scmd->device,
+ "Attempting Device(lun) Reset! scmd(%p)\n", scmd);
+ scsi_print_command(scmd);
+
+ sdev_priv_data = scmd->device->hostdata;
+ if (!sdev_priv_data || !sdev_priv_data->tgt_priv_data) {
+ sdev_printk(KERN_INFO, scmd->device,
+ "SCSI device is not available\n");
+ retval = SUCCESS;
+ goto out;
+ }
+
+ stgt_priv_data = sdev_priv_data->tgt_priv_data;
+ dev_handle = stgt_priv_data->dev_handle;
+ sdev_printk(KERN_INFO, scmd->device,
+ "Device(lun) Reset is issued to handle(0x%04x)\n", dev_handle);
+
+ ret = mpi3mr_issue_tm(mrioc,
+ MPI3_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, dev_handle,
+ sdev_priv_data->lun_id, MPI3MR_HOSTTAG_BLK_TMS,
+ MPI3MR_RESETTM_TIMEOUT, &mrioc->host_tm_cmds, &resp_code, NULL);
+
+ if (ret)
+ goto out;
+
+ retval = SUCCESS;
+out:
+ sdev_printk(KERN_INFO, scmd->device,
+ "Device(lun) reset is %s for scmd(%p)\n",
+ ((retval == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_scan_start - Scan start callback handler
+ * @shost: SCSI host reference
+ *
+ * Issue port enable request asynchronously.
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_scan_start(struct Scsi_Host *shost)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+
+ mrioc->scan_started = 1;
+ ioc_info(mrioc, "%s :Issuing Port Enable\n", __func__);
+ if (mpi3mr_issue_port_enable(mrioc, 1)) {
+ ioc_err(mrioc, "%s :Issuing port enable failed\n", __func__);
+ mrioc->scan_started = 0;
+ mrioc->scan_failed = MPI3_IOCSTATUS_INTERNAL_ERROR;
+ }
+}
+
+/**
+ * mpi3mr_scan_finished - Scan finished callback handler
+ * @shost: SCSI host reference
+ * @time: Jiffies from the scan start
+ *
+ * Checks whether the port enable is completed or timedout or
+ * failed and set the scan status accordingly after taking any
+ * recovery if required.
+ *
+ * Return: 1 on scan finished or timed out, 0 for in progress
+ */
+static int mpi3mr_scan_finished(struct Scsi_Host *shost,
+ unsigned long time)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+ u32 pe_timeout = MPI3MR_PORTENABLE_TIMEOUT;
+
+ if (time >= (pe_timeout * HZ)) {
+ mrioc->init_cmds.is_waiting = 0;
+ mrioc->init_cmds.callback = NULL;
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ ioc_err(mrioc, "%s :port enable request timed out\n", __func__);
+ mrioc->is_driver_loading = 0;
+ mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_PE_TIMEOUT, 1);
+ }
+
+ if (mrioc->scan_failed) {
+ ioc_err(mrioc,
+ "%s :port enable failed with (ioc_status=0x%08x)\n",
+ __func__, mrioc->scan_failed);
+ mrioc->is_driver_loading = 0;
+ mrioc->stop_drv_processing = 1;
+ return 1;
+ }
+
+ if (mrioc->scan_started)
+ return 0;
+ ioc_info(mrioc, "%s :port enable: SUCCESS\n", __func__);
+ mpi3mr_start_watchdog(mrioc);
+ mrioc->is_driver_loading = 0;
+
+ return 1;
+}
+
+/**
+ * mpi3mr_slave_destroy - Slave destroy callback handler
+ * @sdev: SCSI device reference
+ *
+ * Cleanup and free per device(lun) private data.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_slave_destroy(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost;
+ struct mpi3mr_ioc *mrioc;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data;
+ struct mpi3mr_tgt_dev *tgt_dev;
+ unsigned long flags;
+ struct scsi_target *starget;
+
+ if (!sdev->hostdata)
+ return;
+
+ starget = scsi_target(sdev);
+ shost = dev_to_shost(&starget->dev);
+ mrioc = shost_priv(shost);
+ scsi_tgt_priv_data = starget->hostdata;
+
+ scsi_tgt_priv_data->num_luns--;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id);
+ if (tgt_dev && (!scsi_tgt_priv_data->num_luns))
+ tgt_dev->starget = NULL;
+ if (tgt_dev)
+ mpi3mr_tgtdev_put(tgt_dev);
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ kfree(sdev->hostdata);
+ sdev->hostdata = NULL;
+}
+
+/**
+ * mpi3mr_target_destroy - Target destroy callback handler
+ * @starget: SCSI target reference
+ *
+ * Cleanup and free per target private data.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_target_destroy(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost;
+ struct mpi3mr_ioc *mrioc;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data;
+ struct mpi3mr_tgt_dev *tgt_dev;
+ unsigned long flags;
+
+ if (!starget->hostdata)
+ return;
+
+ shost = dev_to_shost(&starget->dev);
+ mrioc = shost_priv(shost);
+ scsi_tgt_priv_data = starget->hostdata;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgt_dev = __mpi3mr_get_tgtdev_from_tgtpriv(mrioc, scsi_tgt_priv_data);
+ if (tgt_dev && (tgt_dev->starget == starget) &&
+ (tgt_dev->perst_id == starget->id))
+ tgt_dev->starget = NULL;
+ if (tgt_dev) {
+ scsi_tgt_priv_data->tgt_dev = NULL;
+ scsi_tgt_priv_data->perst_id = 0;
+ mpi3mr_tgtdev_put(tgt_dev);
+ mpi3mr_tgtdev_put(tgt_dev);
+ }
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ kfree(starget->hostdata);
+ starget->hostdata = NULL;
+}
+
+/**
+ * mpi3mr_slave_configure - Slave configure callback handler
+ * @sdev: SCSI device reference
+ *
+ * Configure queue depth, max hardware sectors and virt boundary
+ * as required
+ *
+ * Return: 0 always.
+ */
+static int mpi3mr_slave_configure(struct scsi_device *sdev)
+{
+ struct scsi_target *starget;
+ struct Scsi_Host *shost;
+ struct mpi3mr_ioc *mrioc;
+ struct mpi3mr_tgt_dev *tgt_dev;
+ unsigned long flags;
+ int retval = 0;
+
+ starget = scsi_target(sdev);
+ shost = dev_to_shost(&starget->dev);
+ mrioc = shost_priv(shost);
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id);
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+ if (!tgt_dev)
+ return -ENXIO;
+
+ mpi3mr_change_queue_depth(sdev, tgt_dev->q_depth);
+ switch (tgt_dev->dev_type) {
+ case MPI3_DEVICE_DEVFORM_PCIE:
+ /*The block layer hw sector size = 512*/
+ blk_queue_max_hw_sectors(sdev->request_queue,
+ tgt_dev->dev_spec.pcie_inf.mdts / 512);
+ blk_queue_virt_boundary(sdev->request_queue,
+ ((1 << tgt_dev->dev_spec.pcie_inf.pgsz) - 1));
+ break;
+ default:
+ break;
+ }
+
+ mpi3mr_tgtdev_put(tgt_dev);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_slave_alloc -Slave alloc callback handler
+ * @sdev: SCSI device reference
+ *
+ * Allocate per device(lun) private data and initialize it.
+ *
+ * Return: 0 on success -ENOMEM on memory allocation failure.
+ */
+static int mpi3mr_slave_alloc(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost;
+ struct mpi3mr_ioc *mrioc;
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data;
+ struct mpi3mr_tgt_dev *tgt_dev;
+ struct mpi3mr_sdev_priv_data *scsi_dev_priv_data;
+ unsigned long flags;
+ struct scsi_target *starget;
+ int retval = 0;
+
+ starget = scsi_target(sdev);
+ shost = dev_to_shost(&starget->dev);
+ mrioc = shost_priv(shost);
+ scsi_tgt_priv_data = starget->hostdata;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id);
+
+ if (tgt_dev) {
+ if (tgt_dev->starget == NULL)
+ tgt_dev->starget = starget;
+ mpi3mr_tgtdev_put(tgt_dev);
+ retval = 0;
+ } else {
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+ return -ENXIO;
+ }
+
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ scsi_dev_priv_data = kzalloc(sizeof(*scsi_dev_priv_data), GFP_KERNEL);
+ if (!scsi_dev_priv_data)
+ return -ENOMEM;
+
+ scsi_dev_priv_data->lun_id = sdev->lun;
+ scsi_dev_priv_data->tgt_priv_data = scsi_tgt_priv_data;
+ sdev->hostdata = scsi_dev_priv_data;
+
+ scsi_tgt_priv_data->num_luns++;
+
+ return retval;
+}
+
+/**
+ * mpi3mr_target_alloc - Target alloc callback handler
+ * @starget: SCSI target reference
+ *
+ * Allocate per target private data and initialize it.
+ *
+ * Return: 0 on success -ENOMEM on memory allocation failure.
+ */
+static int mpi3mr_target_alloc(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(&starget->dev);
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+ struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data;
+ struct mpi3mr_tgt_dev *tgt_dev;
+ unsigned long flags;
+ int retval = 0;
+
+ scsi_tgt_priv_data = kzalloc(sizeof(*scsi_tgt_priv_data), GFP_KERNEL);
+ if (!scsi_tgt_priv_data)
+ return -ENOMEM;
+
+ starget->hostdata = scsi_tgt_priv_data;
+ scsi_tgt_priv_data->starget = starget;
+ scsi_tgt_priv_data->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+
+ spin_lock_irqsave(&mrioc->tgtdev_lock, flags);
+ tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id);
+ if (tgt_dev && !tgt_dev->is_hidden) {
+ starget->hostdata = scsi_tgt_priv_data;
+ scsi_tgt_priv_data->starget = starget;
+ scsi_tgt_priv_data->dev_handle = tgt_dev->dev_handle;
+ scsi_tgt_priv_data->perst_id = tgt_dev->perst_id;
+ scsi_tgt_priv_data->dev_type = tgt_dev->dev_type;
+ scsi_tgt_priv_data->tgt_dev = tgt_dev;
+ tgt_dev->starget = starget;
+ atomic_set(&scsi_tgt_priv_data->block_io, 0);
+ retval = 0;
+ } else {
+ kfree(scsi_tgt_priv_data);
+ retval = -ENXIO;
+ }
+ spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags);
+
+ return retval;
+}
+
+/**
+ * mpi3mr_check_return_unmap - Whether an unmap is allowed
+ * @mrioc: Adapter instance reference
+ * @scmd: SCSI Command reference
+ *
+ * The controller hardware cannot handle certain unmap commands
+ * for NVMe drives, this routine checks those and return true
+ * and completes the SCSI command with proper status and sense
+ * data.
+ *
+ * Return: TRUE for not allowed unmap, FALSE otherwise.
+ */
+static bool mpi3mr_check_return_unmap(struct mpi3mr_ioc *mrioc,
+ struct scsi_cmnd *scmd)
+{
+ unsigned char *buf;
+ u16 param_len, desc_len;
+
+ param_len = get_unaligned_be16(scmd->cmnd + 7);
+
+ if (!param_len) {
+ ioc_warn(mrioc,
+ "%s: cdb received with zero parameter length\n",
+ __func__);
+ scsi_print_command(scmd);
+ scmd->result = DID_OK << 16;
+ scmd->scsi_done(scmd);
+ return true;
+ }
+
+ if (param_len < 24) {
+ ioc_warn(mrioc,
+ "%s: cdb received with invalid param_len: %d\n",
+ __func__, param_len);
+ scsi_print_command(scmd);
+ scmd->result = SAM_STAT_CHECK_CONDITION;
+ scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x1A, 0);
+ scmd->scsi_done(scmd);
+ return true;
+ }
+ if (param_len != scsi_bufflen(scmd)) {
+ ioc_warn(mrioc,
+ "%s: cdb received with param_len: %d bufflen: %d\n",
+ __func__, param_len, scsi_bufflen(scmd));
+ scsi_print_command(scmd);
+ scmd->result = SAM_STAT_CHECK_CONDITION;
+ scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x1A, 0);
+ scmd->scsi_done(scmd);
+ return true;
+ }
+ buf = kzalloc(scsi_bufflen(scmd), GFP_ATOMIC);
+ if (!buf) {
+ scsi_print_command(scmd);
+ scmd->result = SAM_STAT_CHECK_CONDITION;
+ scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x55, 0x03);
+ scmd->scsi_done(scmd);
+ return true;
+ }
+ scsi_sg_copy_to_buffer(scmd, buf, scsi_bufflen(scmd));
+ desc_len = get_unaligned_be16(&buf[2]);
+
+ if (desc_len < 16) {
+ ioc_warn(mrioc,
+ "%s: Invalid descriptor length in param list: %d\n",
+ __func__, desc_len);
+ scsi_print_command(scmd);
+ scmd->result = SAM_STAT_CHECK_CONDITION;
+ scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x26, 0);
+ scmd->scsi_done(scmd);
+ kfree(buf);
+ return true;
+ }
+
+ if (param_len > (desc_len + 8)) {
+ scsi_print_command(scmd);
+ ioc_warn(mrioc,
+ "%s: Truncating param_len(%d) to desc_len+8(%d)\n",
+ __func__, param_len, (desc_len + 8));
+ param_len = desc_len + 8;
+ put_unaligned_be16(param_len, scmd->cmnd + 7);
+ scsi_print_command(scmd);
+ }
+
+ kfree(buf);
+ return false;
+}
+
+/**
+ * mpi3mr_allow_scmd_to_fw - Command is allowed during shutdown
+ * @scmd: SCSI Command reference
+ *
+ * Checks whether a cdb is allowed during shutdown or not.
+ *
+ * Return: TRUE for allowed commands, FALSE otherwise.
+ */
+
+inline bool mpi3mr_allow_scmd_to_fw(struct scsi_cmnd *scmd)
+{
+ switch (scmd->cmnd[0]) {
+ case SYNCHRONIZE_CACHE:
+ case START_STOP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * mpi3mr_qcmd - I/O request despatcher
+ * @shost: SCSI Host reference
+ * @scmd: SCSI Command reference
+ *
+ * Issues the SCSI Command as an MPI3 request.
+ *
+ * Return: 0 on successful queueing of the request or if the
+ * request is completed with failure.
+ * SCSI_MLQUEUE_DEVICE_BUSY when the device is busy.
+ * SCSI_MLQUEUE_HOST_BUSY when the host queue is full.
+ */
+static int mpi3mr_qcmd(struct Scsi_Host *shost,
+ struct scsi_cmnd *scmd)
+{
+ struct mpi3mr_ioc *mrioc = shost_priv(shost);
+ struct mpi3mr_stgt_priv_data *stgt_priv_data;
+ struct mpi3mr_sdev_priv_data *sdev_priv_data;
+ struct scmd_priv *scmd_priv_data = NULL;
+ struct mpi3_scsi_io_request *scsiio_req = NULL;
+ struct op_req_qinfo *op_req_q = NULL;
+ int retval = 0;
+ u16 dev_handle;
+ u16 host_tag;
+ u32 scsiio_flags = 0;
+ struct request *rq = scmd->request;
+ int iprio_class;
+
+ sdev_priv_data = scmd->device->hostdata;
+ if (!sdev_priv_data || !sdev_priv_data->tgt_priv_data) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ goto out;
+ }
+
+ if (mrioc->stop_drv_processing &&
+ !(mpi3mr_allow_scmd_to_fw(scmd))) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ goto out;
+ }
+
+ if (mrioc->reset_in_progress) {
+ retval = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+
+ stgt_priv_data = sdev_priv_data->tgt_priv_data;
+
+ dev_handle = stgt_priv_data->dev_handle;
+ if (dev_handle == MPI3MR_INVALID_DEV_HANDLE) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ goto out;
+ }
+ if (stgt_priv_data->dev_removed) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ goto out;
+ }
+
+ if (atomic_read(&stgt_priv_data->block_io)) {
+ if (mrioc->stop_drv_processing) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ goto out;
+ }
+ retval = SCSI_MLQUEUE_DEVICE_BUSY;
+ goto out;
+ }
+
+ if ((scmd->cmnd[0] == UNMAP) &&
+ (stgt_priv_data->dev_type == MPI3_DEVICE_DEVFORM_PCIE) &&
+ mpi3mr_check_return_unmap(mrioc, scmd))
+ goto out;
+
+ host_tag = mpi3mr_host_tag_for_scmd(mrioc, scmd);
+ if (host_tag == MPI3MR_HOSTTAG_INVALID) {
+ scmd->result = DID_ERROR << 16;
+ scmd->scsi_done(scmd);
+ goto out;
+ }
+
+ if (scmd->sc_data_direction == DMA_FROM_DEVICE)
+ scsiio_flags = MPI3_SCSIIO_FLAGS_DATADIRECTION_READ;
+ else if (scmd->sc_data_direction == DMA_TO_DEVICE)
+ scsiio_flags = MPI3_SCSIIO_FLAGS_DATADIRECTION_WRITE;
+ else
+ scsiio_flags = MPI3_SCSIIO_FLAGS_DATADIRECTION_NO_DATA_TRANSFER;
+
+ scsiio_flags |= MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_SIMPLEQ;
+
+ if (sdev_priv_data->ncq_prio_enable) {
+ iprio_class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq));
+ if (iprio_class == IOPRIO_CLASS_RT)
+ scsiio_flags |= 1 << MPI3_SCSIIO_FLAGS_CMDPRI_SHIFT;
+ }
+
+ if (scmd->cmd_len > 16)
+ scsiio_flags |= MPI3_SCSIIO_FLAGS_CDB_GREATER_THAN_16;
+
+ scmd_priv_data = scsi_cmd_priv(scmd);
+ memset(scmd_priv_data->mpi3mr_scsiio_req, 0, MPI3MR_ADMIN_REQ_FRAME_SZ);
+ scsiio_req = (struct mpi3_scsi_io_request *)scmd_priv_data->mpi3mr_scsiio_req;
+ scsiio_req->function = MPI3_FUNCTION_SCSI_IO;
+ scsiio_req->host_tag = cpu_to_le16(host_tag);
+
+ mpi3mr_setup_eedp(mrioc, scmd, scsiio_req);
+
+ memcpy(scsiio_req->cdb.cdb32, scmd->cmnd, scmd->cmd_len);
+ scsiio_req->data_length = cpu_to_le32(scsi_bufflen(scmd));
+ scsiio_req->dev_handle = cpu_to_le16(dev_handle);
+ scsiio_req->flags = cpu_to_le32(scsiio_flags);
+ int_to_scsilun(sdev_priv_data->lun_id,
+ (struct scsi_lun *)scsiio_req->lun);
+
+ if (mpi3mr_build_sg_scmd(mrioc, scmd, scsiio_req)) {
+ mpi3mr_clear_scmd_priv(mrioc, scmd);
+ retval = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+ op_req_q = &mrioc->req_qinfo[scmd_priv_data->req_q_idx];
+
+ if (mpi3mr_op_request_post(mrioc, op_req_q,
+ scmd_priv_data->mpi3mr_scsiio_req)) {
+ mpi3mr_clear_scmd_priv(mrioc, scmd);
+ retval = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+
+out:
+ return retval;
+}
+
+static struct scsi_host_template mpi3mr_driver_template = {
+ .module = THIS_MODULE,
+ .name = "MPI3 Storage Controller",
+ .proc_name = MPI3MR_DRIVER_NAME,
+ .queuecommand = mpi3mr_qcmd,
+ .target_alloc = mpi3mr_target_alloc,
+ .slave_alloc = mpi3mr_slave_alloc,
+ .slave_configure = mpi3mr_slave_configure,
+ .target_destroy = mpi3mr_target_destroy,
+ .slave_destroy = mpi3mr_slave_destroy,
+ .scan_finished = mpi3mr_scan_finished,
+ .scan_start = mpi3mr_scan_start,
+ .change_queue_depth = mpi3mr_change_queue_depth,
+ .eh_device_reset_handler = mpi3mr_eh_dev_reset,
+ .eh_target_reset_handler = mpi3mr_eh_target_reset,
+ .eh_host_reset_handler = mpi3mr_eh_host_reset,
+ .bios_param = mpi3mr_bios_param,
+ .map_queues = mpi3mr_map_queues,
+ .no_write_same = 1,
+ .can_queue = 1,
+ .this_id = -1,
+ .sg_tablesize = MPI3MR_SG_DEPTH,
+ /* max xfer supported is 1M (2K in 512 byte sized sectors)
+ */
+ .max_sectors = 2048,
+ .cmd_per_lun = MPI3MR_MAX_CMDS_LUN,
+ .track_queue_depth = 1,
+ .cmd_size = sizeof(struct scmd_priv),
+};
+
+/**
+ * mpi3mr_init_drv_cmd - Initialize internal command tracker
+ * @cmdptr: Internal command tracker
+ * @host_tag: Host tag used for the specific command
+ *
+ * Initialize the internal command tracker structure with
+ * specified host tag.
+ *
+ * Return: Nothing.
+ */
+static inline void mpi3mr_init_drv_cmd(struct mpi3mr_drv_cmd *cmdptr,
+ u16 host_tag)
+{
+ mutex_init(&cmdptr->mutex);
+ cmdptr->reply = NULL;
+ cmdptr->state = MPI3MR_CMD_NOTUSED;
+ cmdptr->dev_handle = MPI3MR_INVALID_DEV_HANDLE;
+ cmdptr->host_tag = host_tag;
+}
+
+/**
+ * osintfc_mrioc_security_status -Check controller secure status
+ * @pdev: PCI device instance
+ *
+ * Read the Device Serial Number capability from PCI config
+ * space and decide whether the controller is secure or not.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static int
+osintfc_mrioc_security_status(struct pci_dev *pdev)
+{
+ u32 cap_data;
+ int base;
+ u32 ctlr_status;
+ u32 debug_status;
+ int retval = 0;
+
+ base = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DSN);
+ if (!base) {
+ dev_err(&pdev->dev,
+ "%s: PCI_EXT_CAP_ID_DSN is not supported\n", __func__);
+ return -1;
+ }
+
+ pci_read_config_dword(pdev, base + 4, &cap_data);
+
+ debug_status = cap_data & MPI3MR_CTLR_SECURE_DBG_STATUS_MASK;
+ ctlr_status = cap_data & MPI3MR_CTLR_SECURITY_STATUS_MASK;
+
+ switch (ctlr_status) {
+ case MPI3MR_INVALID_DEVICE:
+ dev_err(&pdev->dev,
+ "%s: Non secure ctlr (Invalid) is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n",
+ __func__, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
+ retval = -1;
+ break;
+ case MPI3MR_CONFIG_SECURE_DEVICE:
+ if (!debug_status)
+ dev_info(&pdev->dev,
+ "%s: Config secure ctlr is detected\n",
+ __func__);
+ break;
+ case MPI3MR_HARD_SECURE_DEVICE:
+ break;
+ case MPI3MR_TAMPERED_DEVICE:
+ dev_err(&pdev->dev,
+ "%s: Non secure ctlr (Tampered) is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n",
+ __func__, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
+ retval = -1;
+ break;
+ default:
+ retval = -1;
+ break;
+ }
+
+ if (!retval && debug_status) {
+ dev_err(&pdev->dev,
+ "%s: Non secure ctlr (Secure Dbg) is detected: DID: 0x%x: SVID: 0x%x: SDID: 0x%x\n",
+ __func__, pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
+ retval = -1;
+ }
+
+ return retval;
+}
+
+/**
+ * mpi3mr_probe - PCI probe callback
+ * @pdev: PCI device instance
+ * @id: PCI device ID details
+ *
+ * controller initialization routine. Checks the security status
+ * of the controller and if it is invalid or tampered return the
+ * probe without initializing the controller. Otherwise,
+ * allocate per adapter instance through shost_priv and
+ * initialize controller specific data structures, initializae
+ * the controller hardware, add shost to the SCSI subsystem.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+
+static int
+mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct mpi3mr_ioc *mrioc = NULL;
+ struct Scsi_Host *shost = NULL;
+ int retval = 0, i;
+
+ if (osintfc_mrioc_security_status(pdev)) {
+ warn_non_secure_ctlr = 1;
+ return 1; /* For Invalid and Tampered device */
+ }
+
+ shost = scsi_host_alloc(&mpi3mr_driver_template,
+ sizeof(struct mpi3mr_ioc));
+ if (!shost) {
+ retval = -ENODEV;
+ goto shost_failed;
+ }
+
+ mrioc = shost_priv(shost);
+ mrioc->id = mrioc_ids++;
+ sprintf(mrioc->driver_name, "%s", MPI3MR_DRIVER_NAME);
+ sprintf(mrioc->name, "%s%d", mrioc->driver_name, mrioc->id);
+ INIT_LIST_HEAD(&mrioc->list);
+ spin_lock(&mrioc_list_lock);
+ list_add_tail(&mrioc->list, &mrioc_list);
+ spin_unlock(&mrioc_list_lock);
+
+ spin_lock_init(&mrioc->admin_req_lock);
+ spin_lock_init(&mrioc->reply_free_queue_lock);
+ spin_lock_init(&mrioc->sbq_lock);
+ spin_lock_init(&mrioc->fwevt_lock);
+ spin_lock_init(&mrioc->tgtdev_lock);
+ spin_lock_init(&mrioc->watchdog_lock);
+ spin_lock_init(&mrioc->chain_buf_lock);
+
+ INIT_LIST_HEAD(&mrioc->fwevt_list);
+ INIT_LIST_HEAD(&mrioc->tgtdev_list);
+ INIT_LIST_HEAD(&mrioc->delayed_rmhs_list);
+
+ mutex_init(&mrioc->reset_mutex);
+ mpi3mr_init_drv_cmd(&mrioc->init_cmds, MPI3MR_HOSTTAG_INITCMDS);
+ mpi3mr_init_drv_cmd(&mrioc->host_tm_cmds, MPI3MR_HOSTTAG_BLK_TMS);
+
+ for (i = 0; i < MPI3MR_NUM_DEVRMCMD; i++)
+ mpi3mr_init_drv_cmd(&mrioc->dev_rmhs_cmds[i],
+ MPI3MR_HOSTTAG_DEVRMCMD_MIN + i);
+
+ if (pdev->revision)
+ mrioc->enable_segqueue = true;
+
+ init_waitqueue_head(&mrioc->reset_waitq);
+ mrioc->logging_level = logging_level;
+ mrioc->shost = shost;
+ mrioc->pdev = pdev;
+
+ /* init shost parameters */
+ shost->max_cmd_len = MPI3MR_MAX_CDB_LENGTH;
+ shost->max_lun = -1;
+ shost->unique_id = mrioc->id;
+
+ shost->max_channel = 1;
+ shost->max_id = 0xFFFFFFFF;
+
+ if (prot_mask >= 0)
+ scsi_host_set_prot(shost, prot_mask);
+ else {
+ prot_mask = SHOST_DIF_TYPE1_PROTECTION
+ | SHOST_DIF_TYPE2_PROTECTION
+ | SHOST_DIF_TYPE3_PROTECTION;
+ scsi_host_set_prot(shost, prot_mask);
+ }
+
+ ioc_info(mrioc,
+ "%s :host protection capabilities enabled %s%s%s%s%s%s%s\n",
+ __func__,
+ (prot_mask & SHOST_DIF_TYPE1_PROTECTION) ? " DIF1" : "",
+ (prot_mask & SHOST_DIF_TYPE2_PROTECTION) ? " DIF2" : "",
+ (prot_mask & SHOST_DIF_TYPE3_PROTECTION) ? " DIF3" : "",
+ (prot_mask & SHOST_DIX_TYPE0_PROTECTION) ? " DIX0" : "",
+ (prot_mask & SHOST_DIX_TYPE1_PROTECTION) ? " DIX1" : "",
+ (prot_mask & SHOST_DIX_TYPE2_PROTECTION) ? " DIX2" : "",
+ (prot_mask & SHOST_DIX_TYPE3_PROTECTION) ? " DIX3" : "");
+
+ if (prot_guard_mask)
+ scsi_host_set_guard(shost, (prot_guard_mask & 3));
+ else
+ scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
+
+ snprintf(mrioc->fwevt_worker_name, sizeof(mrioc->fwevt_worker_name),
+ "%s%d_fwevt_wrkr", mrioc->driver_name, mrioc->id);
+ mrioc->fwevt_worker_thread = alloc_ordered_workqueue(
+ mrioc->fwevt_worker_name, WQ_MEM_RECLAIM);
+ if (!mrioc->fwevt_worker_thread) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ retval = -ENODEV;
+ goto out_fwevtthread_failed;
+ }
+
+ mrioc->is_driver_loading = 1;
+ if (mpi3mr_init_ioc(mrioc, 0)) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ retval = -ENODEV;
+ goto out_iocinit_failed;
+ }
+
+ shost->nr_hw_queues = mrioc->num_op_reply_q;
+ shost->can_queue = mrioc->max_host_ios;
+ shost->sg_tablesize = MPI3MR_SG_DEPTH;
+ shost->max_id = mrioc->facts.max_perids;
+
+ retval = scsi_add_host(shost, &pdev->dev);
+ if (retval) {
+ ioc_err(mrioc, "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ goto addhost_failed;
+ }
+
+ scsi_scan_host(shost);
+ return retval;
+
+addhost_failed:
+ mpi3mr_cleanup_ioc(mrioc, 0);
+out_iocinit_failed:
+ destroy_workqueue(mrioc->fwevt_worker_thread);
+out_fwevtthread_failed:
+ spin_lock(&mrioc_list_lock);
+ list_del(&mrioc->list);
+ spin_unlock(&mrioc_list_lock);
+ scsi_host_put(shost);
+shost_failed:
+ return retval;
+}
+
+/**
+ * mpi3mr_remove - PCI remove callback
+ * @pdev: PCI device instance
+ *
+ * Free up all memory and resources associated with the
+ * controllerand target devices, unregister the shost.
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct mpi3mr_ioc *mrioc;
+ struct workqueue_struct *wq;
+ unsigned long flags;
+ struct mpi3mr_tgt_dev *tgtdev, *tgtdev_next;
+
+ if (!shost)
+ return;
+
+ mrioc = shost_priv(shost);
+ while (mrioc->reset_in_progress || mrioc->is_driver_loading)
+ ssleep(1);
+
+ mrioc->stop_drv_processing = 1;
+ mpi3mr_cleanup_fwevt_list(mrioc);
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ wq = mrioc->fwevt_worker_thread;
+ mrioc->fwevt_worker_thread = NULL;
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+ if (wq)
+ destroy_workqueue(wq);
+ scsi_remove_host(shost);
+
+ list_for_each_entry_safe(tgtdev, tgtdev_next, &mrioc->tgtdev_list,
+ list) {
+ mpi3mr_remove_tgtdev_from_host(mrioc, tgtdev);
+ mpi3mr_tgtdev_del_from_list(mrioc, tgtdev);
+ mpi3mr_tgtdev_put(tgtdev);
+ }
+ mpi3mr_cleanup_ioc(mrioc, 0);
+
+ spin_lock(&mrioc_list_lock);
+ list_del(&mrioc->list);
+ spin_unlock(&mrioc_list_lock);
+
+ scsi_host_put(shost);
+}
+
+/**
+ * mpi3mr_shutdown - PCI shutdown callback
+ * @pdev: PCI device instance
+ *
+ * Free up all memory and resources associated with the
+ * controller
+ *
+ * Return: Nothing.
+ */
+static void mpi3mr_shutdown(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct mpi3mr_ioc *mrioc;
+ struct workqueue_struct *wq;
+ unsigned long flags;
+
+ if (!shost)
+ return;
+
+ mrioc = shost_priv(shost);
+ while (mrioc->reset_in_progress || mrioc->is_driver_loading)
+ ssleep(1);
+
+ mrioc->stop_drv_processing = 1;
+ mpi3mr_cleanup_fwevt_list(mrioc);
+ spin_lock_irqsave(&mrioc->fwevt_lock, flags);
+ wq = mrioc->fwevt_worker_thread;
+ mrioc->fwevt_worker_thread = NULL;
+ spin_unlock_irqrestore(&mrioc->fwevt_lock, flags);
+ if (wq)
+ destroy_workqueue(wq);
+ mpi3mr_cleanup_ioc(mrioc, 0);
+}
+
+#ifdef CONFIG_PM
+/**
+ * mpi3mr_suspend - PCI power management suspend callback
+ * @pdev: PCI device instance
+ * @state: New power state
+ *
+ * Change the power state to the given value and cleanup the IOC
+ * by issuing MUR and shutdown notification
+ *
+ * Return: 0 always.
+ */
+static int mpi3mr_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct mpi3mr_ioc *mrioc;
+ pci_power_t device_state;
+
+ if (!shost)
+ return 0;
+
+ mrioc = shost_priv(shost);
+ while (mrioc->reset_in_progress || mrioc->is_driver_loading)
+ ssleep(1);
+ mrioc->stop_drv_processing = 1;
+ mpi3mr_cleanup_fwevt_list(mrioc);
+ scsi_block_requests(shost);
+ mpi3mr_stop_watchdog(mrioc);
+ mpi3mr_cleanup_ioc(mrioc, 1);
+
+ device_state = pci_choose_state(pdev, state);
+ ioc_info(mrioc, "pdev=0x%p, slot=%s, entering operating state [D%d]\n",
+ pdev, pci_name(pdev), device_state);
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, device_state);
+ mpi3mr_cleanup_resources(mrioc);
+
+ return 0;
+}
+
+/**
+ * mpi3mr_resume - PCI power management resume callback
+ * @pdev: PCI device instance
+ *
+ * Restore the power state to D0 and reinitialize the controller
+ * and resume I/O operations to the target devices
+ *
+ * Return: 0 on success, non-zero on failure
+ */
+static int mpi3mr_resume(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct mpi3mr_ioc *mrioc;
+ pci_power_t device_state = pdev->current_state;
+ int r;
+
+ if (!shost)
+ return 0;
+
+ mrioc = shost_priv(shost);
+
+ ioc_info(mrioc, "pdev=0x%p, slot=%s, previous operating state [D%d]\n",
+ pdev, pci_name(pdev), device_state);
+ pci_set_power_state(pdev, PCI_D0);
+ pci_enable_wake(pdev, PCI_D0, 0);
+ pci_restore_state(pdev);
+ mrioc->pdev = pdev;
+ mrioc->cpu_count = num_online_cpus();
+ r = mpi3mr_setup_resources(mrioc);
+ if (r) {
+ ioc_info(mrioc, "%s: Setup resources failed[%d]\n",
+ __func__, r);
+ return r;
+ }
+
+ mrioc->stop_drv_processing = 0;
+ mpi3mr_init_ioc(mrioc, 1);
+ scsi_unblock_requests(shost);
+ mpi3mr_start_watchdog(mrioc);
+
+ return 0;
+}
+#endif
+
+static const struct pci_device_id mpi3mr_pci_id_table[] = {
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_LSI_LOGIC, 0x00A5,
+ PCI_ANY_ID, PCI_ANY_ID)
+ },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, mpi3mr_pci_id_table);
+
+static struct pci_driver mpi3mr_pci_driver = {
+ .name = MPI3MR_DRIVER_NAME,
+ .id_table = mpi3mr_pci_id_table,
+ .probe = mpi3mr_probe,
+ .remove = mpi3mr_remove,
+ .shutdown = mpi3mr_shutdown,
+#ifdef CONFIG_PM
+ .suspend = mpi3mr_suspend,
+ .resume = mpi3mr_resume,
+#endif
+};
+
+static int __init mpi3mr_init(void)
+{
+ int ret_val;
+
+ pr_info("Loading %s version %s\n", MPI3MR_DRIVER_NAME,
+ MPI3MR_DRIVER_VERSION);
+
+ ret_val = pci_register_driver(&mpi3mr_pci_driver);
+
+ return ret_val;
+}
+
+static void __exit mpi3mr_exit(void)
+{
+ if (warn_non_secure_ctlr)
+ pr_warn(
+ "Unloading %s version %s while managing a non secure controller\n",
+ MPI3MR_DRIVER_NAME, MPI3MR_DRIVER_VERSION);
+ else
+ pr_info("Unloading %s version %s\n", MPI3MR_DRIVER_NAME,
+ MPI3MR_DRIVER_VERSION);
+
+ pci_unregister_driver(&mpi3mr_pci_driver);
+}
+
+module_init(mpi3mr_init);
+module_exit(mpi3mr_exit);
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
index bc4ed3ed4b9a..4df8448cfbf2 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -4437,6 +4437,7 @@ _base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc)
ioc->pdev->subsystem_device);
break;
}
+ break;
default:
break;
}
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index 45a8bc67f3ae..dc2aaaf645d3 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -12076,6 +12076,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ioc->multipath_on_hba = 1;
else
ioc->multipath_on_hba = 0;
+ break;
default:
break;
}
diff --git a/drivers/scsi/qedi/qedi.h b/drivers/scsi/qedi/qedi.h
index c342defc3f52..ce199a7a16b8 100644
--- a/drivers/scsi/qedi/qedi.h
+++ b/drivers/scsi/qedi/qedi.h
@@ -284,6 +284,7 @@ struct qedi_ctx {
#define QEDI_IN_RECOVERY 5
#define QEDI_IN_OFFLINE 6
#define QEDI_IN_SHUTDOWN 7
+#define QEDI_BLOCK_IO 8
u8 mac[ETH_ALEN];
u32 src_ip[4];
diff --git a/drivers/scsi/qedi/qedi_fw.c b/drivers/scsi/qedi/qedi_fw.c
index 440ddd2309f1..71333d3c5c86 100644
--- a/drivers/scsi/qedi/qedi_fw.c
+++ b/drivers/scsi/qedi/qedi_fw.c
@@ -14,8 +14,8 @@
#include "qedi_fw_iscsi.h"
#include "qedi_fw_scsi.h"
-static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn,
- struct iscsi_task *mtask);
+static int send_iscsi_tmf(struct qedi_conn *qedi_conn,
+ struct iscsi_task *mtask, struct iscsi_task *ctask);
void qedi_iscsi_unmap_sg_list(struct qedi_cmd *cmd)
{
@@ -73,7 +73,6 @@ static void qedi_process_logout_resp(struct qedi_ctx *qedi,
spin_unlock(&qedi_conn->list_lock);
cmd->state = RESPONSE_RECEIVED;
- qedi_clear_task_idx(qedi, cmd->task_id);
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr, NULL, 0);
spin_unlock(&session->back_lock);
@@ -138,7 +137,6 @@ static void qedi_process_text_resp(struct qedi_ctx *qedi,
spin_unlock(&qedi_conn->list_lock);
cmd->state = RESPONSE_RECEIVED;
- qedi_clear_task_idx(qedi, cmd->task_id);
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr_ptr,
qedi_conn->gen_pdu.resp_buf,
@@ -158,19 +156,11 @@ static void qedi_tmf_resp_work(struct work_struct *work)
struct iscsi_tm_rsp *resp_hdr_ptr;
int rval = 0;
- set_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags);
resp_hdr_ptr = (struct iscsi_tm_rsp *)qedi_cmd->tmf_resp_buf;
- iscsi_block_session(session->cls_session);
rval = qedi_cleanup_all_io(qedi, qedi_conn, qedi_cmd->task, true);
- if (rval) {
- qedi_clear_task_idx(qedi, qedi_cmd->task_id);
- iscsi_unblock_session(session->cls_session);
+ if (rval)
goto exit_tmf_resp;
- }
-
- iscsi_unblock_session(session->cls_session);
- qedi_clear_task_idx(qedi, qedi_cmd->task_id);
spin_lock(&session->back_lock);
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr_ptr, NULL, 0);
@@ -178,7 +168,10 @@ static void qedi_tmf_resp_work(struct work_struct *work)
exit_tmf_resp:
kfree(resp_hdr_ptr);
- clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags);
+
+ spin_lock(&qedi_conn->tmf_work_lock);
+ qedi_conn->fw_cleanup_works--;
+ spin_unlock(&qedi_conn->tmf_work_lock);
}
static void qedi_process_tmf_resp(struct qedi_ctx *qedi,
@@ -234,18 +227,25 @@ static void qedi_process_tmf_resp(struct qedi_ctx *qedi,
}
spin_unlock(&qedi_conn->list_lock);
- if (((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
- ISCSI_TM_FUNC_LOGICAL_UNIT_RESET) ||
- ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
- ISCSI_TM_FUNC_TARGET_WARM_RESET) ||
- ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
- ISCSI_TM_FUNC_TARGET_COLD_RESET)) {
+ spin_lock(&qedi_conn->tmf_work_lock);
+ switch (tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) {
+ case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
+ case ISCSI_TM_FUNC_TARGET_WARM_RESET:
+ case ISCSI_TM_FUNC_TARGET_COLD_RESET:
+ if (qedi_conn->ep_disconnect_starting) {
+ /* Session is down so ep_disconnect will clean up */
+ spin_unlock(&qedi_conn->tmf_work_lock);
+ goto unblock_sess;
+ }
+
+ qedi_conn->fw_cleanup_works++;
+ spin_unlock(&qedi_conn->tmf_work_lock);
+
INIT_WORK(&qedi_cmd->tmf_work, qedi_tmf_resp_work);
queue_work(qedi->tmf_thread, &qedi_cmd->tmf_work);
goto unblock_sess;
}
-
- qedi_clear_task_idx(qedi, qedi_cmd->task_id);
+ spin_unlock(&qedi_conn->tmf_work_lock);
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr_ptr, NULL, 0);
kfree(resp_hdr_ptr);
@@ -314,7 +314,6 @@ static void qedi_process_login_resp(struct qedi_ctx *qedi,
"Freeing tid=0x%x for cid=0x%x\n",
cmd->task_id, qedi_conn->iscsi_conn_id);
cmd->state = RESPONSE_RECEIVED;
- qedi_clear_task_idx(qedi, cmd->task_id);
}
static void qedi_get_rq_bdq_buf(struct qedi_ctx *qedi,
@@ -468,7 +467,6 @@ static int qedi_process_nopin_mesg(struct qedi_ctx *qedi,
}
spin_unlock(&qedi_conn->list_lock);
- qedi_clear_task_idx(qedi, cmd->task_id);
}
done:
@@ -673,7 +671,6 @@ static void qedi_scsi_completion(struct qedi_ctx *qedi,
if (qedi_io_tracing)
qedi_trace_io(qedi, task, cmd->task_id, QEDI_IO_TRACE_RSP);
- qedi_clear_task_idx(qedi, cmd->task_id);
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr,
conn->data, datalen);
error:
@@ -730,7 +727,6 @@ static void qedi_process_nopin_local_cmpl(struct qedi_ctx *qedi,
cqe->itid, cmd->task_id);
cmd->state = RESPONSE_RECEIVED;
- qedi_clear_task_idx(qedi, cmd->task_id);
spin_lock_bh(&session->back_lock);
__iscsi_put_task(task);
@@ -739,20 +735,17 @@ static void qedi_process_nopin_local_cmpl(struct qedi_ctx *qedi,
static void qedi_process_cmd_cleanup_resp(struct qedi_ctx *qedi,
struct iscsi_cqe_solicited *cqe,
- struct iscsi_task *task,
struct iscsi_conn *conn)
{
struct qedi_work_map *work, *work_tmp;
u32 proto_itt = cqe->itid;
- u32 ptmp_itt = 0;
itt_t protoitt = 0;
int found = 0;
struct qedi_cmd *qedi_cmd = NULL;
- u32 rtid = 0;
u32 iscsi_cid;
struct qedi_conn *qedi_conn;
struct qedi_cmd *dbg_cmd;
- struct iscsi_task *mtask;
+ struct iscsi_task *mtask, *task;
struct iscsi_tm *tmf_hdr = NULL;
iscsi_cid = cqe->conn_id;
@@ -778,93 +771,64 @@ static void qedi_process_cmd_cleanup_resp(struct qedi_ctx *qedi,
}
found = 1;
mtask = qedi_cmd->task;
+ task = work->ctask;
tmf_hdr = (struct iscsi_tm *)mtask->hdr;
- rtid = work->rtid;
list_del_init(&work->list);
kfree(work);
qedi_cmd->list_tmf_work = NULL;
}
}
- spin_unlock_bh(&qedi_conn->tmf_work_lock);
-
- if (found) {
- QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
- "TMF work, cqe->tid=0x%x, tmf flags=0x%x, cid=0x%x\n",
- proto_itt, tmf_hdr->flags, qedi_conn->iscsi_conn_id);
-
- if ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
- ISCSI_TM_FUNC_ABORT_TASK) {
- spin_lock_bh(&conn->session->back_lock);
-
- protoitt = build_itt(get_itt(tmf_hdr->rtt),
- conn->session->age);
- task = iscsi_itt_to_task(conn, protoitt);
-
- spin_unlock_bh(&conn->session->back_lock);
-
- if (!task) {
- QEDI_NOTICE(&qedi->dbg_ctx,
- "IO task completed, tmf rtt=0x%x, cid=0x%x\n",
- get_itt(tmf_hdr->rtt),
- qedi_conn->iscsi_conn_id);
- return;
- }
-
- dbg_cmd = task->dd_data;
- QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
- "Abort tmf rtt=0x%x, i/o itt=0x%x, i/o tid=0x%x, cid=0x%x\n",
- get_itt(tmf_hdr->rtt), get_itt(task->itt),
- dbg_cmd->task_id, qedi_conn->iscsi_conn_id);
+ if (!found) {
+ spin_unlock_bh(&qedi_conn->tmf_work_lock);
+ goto check_cleanup_reqs;
+ }
- if (qedi_cmd->state == CLEANUP_WAIT_FAILED)
- qedi_cmd->state = CLEANUP_RECV;
+ QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
+ "TMF work, cqe->tid=0x%x, tmf flags=0x%x, cid=0x%x\n",
+ proto_itt, tmf_hdr->flags, qedi_conn->iscsi_conn_id);
+
+ spin_lock_bh(&conn->session->back_lock);
+ if (iscsi_task_is_completed(task)) {
+ QEDI_NOTICE(&qedi->dbg_ctx,
+ "IO task completed, tmf rtt=0x%x, cid=0x%x\n",
+ get_itt(tmf_hdr->rtt), qedi_conn->iscsi_conn_id);
+ goto unlock;
+ }
- qedi_clear_task_idx(qedi_conn->qedi, rtid);
+ dbg_cmd = task->dd_data;
- spin_lock(&qedi_conn->list_lock);
- if (likely(dbg_cmd->io_cmd_in_list)) {
- dbg_cmd->io_cmd_in_list = false;
- list_del_init(&dbg_cmd->io_cmd);
- qedi_conn->active_cmd_count--;
- }
- spin_unlock(&qedi_conn->list_lock);
- qedi_cmd->state = CLEANUP_RECV;
- wake_up_interruptible(&qedi_conn->wait_queue);
- }
- } else if (qedi_conn->cmd_cleanup_req > 0) {
- spin_lock_bh(&conn->session->back_lock);
- qedi_get_proto_itt(qedi, cqe->itid, &ptmp_itt);
- protoitt = build_itt(ptmp_itt, conn->session->age);
- task = iscsi_itt_to_task(conn, protoitt);
- QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
- "cleanup io itid=0x%x, protoitt=0x%x, cmd_cleanup_cmpl=%d, cid=0x%x\n",
- cqe->itid, protoitt, qedi_conn->cmd_cleanup_cmpl,
- qedi_conn->iscsi_conn_id);
+ QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
+ "Abort tmf rtt=0x%x, i/o itt=0x%x, i/o tid=0x%x, cid=0x%x\n",
+ get_itt(tmf_hdr->rtt), get_itt(task->itt), dbg_cmd->task_id,
+ qedi_conn->iscsi_conn_id);
- spin_unlock_bh(&conn->session->back_lock);
- if (!task) {
- QEDI_NOTICE(&qedi->dbg_ctx,
- "task is null, itid=0x%x, cid=0x%x\n",
- cqe->itid, qedi_conn->iscsi_conn_id);
- return;
- }
- qedi_conn->cmd_cleanup_cmpl++;
- wake_up(&qedi_conn->wait_queue);
+ spin_lock(&qedi_conn->list_lock);
+ if (likely(dbg_cmd->io_cmd_in_list)) {
+ dbg_cmd->io_cmd_in_list = false;
+ list_del_init(&dbg_cmd->io_cmd);
+ qedi_conn->active_cmd_count--;
+ }
+ spin_unlock(&qedi_conn->list_lock);
+ qedi_cmd->state = CLEANUP_RECV;
+unlock:
+ spin_unlock_bh(&conn->session->back_lock);
+ spin_unlock_bh(&qedi_conn->tmf_work_lock);
+ wake_up_interruptible(&qedi_conn->wait_queue);
+ return;
+check_cleanup_reqs:
+ if (qedi_conn->cmd_cleanup_req > 0) {
QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_TID,
"Freeing tid=0x%x for cid=0x%x\n",
cqe->itid, qedi_conn->iscsi_conn_id);
- qedi_clear_task_idx(qedi_conn->qedi, cqe->itid);
-
+ qedi_conn->cmd_cleanup_cmpl++;
+ wake_up(&qedi_conn->wait_queue);
} else {
- qedi_get_proto_itt(qedi, cqe->itid, &ptmp_itt);
- protoitt = build_itt(ptmp_itt, conn->session->age);
- task = iscsi_itt_to_task(conn, protoitt);
QEDI_ERR(&qedi->dbg_ctx,
- "Delayed or untracked cleanup response, itt=0x%x, tid=0x%x, cid=0x%x, task=%p\n",
- protoitt, cqe->itid, qedi_conn->iscsi_conn_id, task);
+ "Delayed or untracked cleanup response, itt=0x%x, tid=0x%x, cid=0x%x\n",
+ protoitt, cqe->itid, qedi_conn->iscsi_conn_id);
}
}
@@ -959,8 +923,7 @@ void qedi_fp_process_cqes(struct qedi_work *work)
goto exit_fp_process;
case ISCSI_CQE_TYPE_TASK_CLEANUP:
QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM, "CleanUp CqE\n");
- qedi_process_cmd_cleanup_resp(qedi, &cqe->cqe_solicited, task,
- conn);
+ qedi_process_cmd_cleanup_resp(qedi, &cqe->cqe_solicited, conn);
goto exit_fp_process;
default:
QEDI_ERR(&qedi->dbg_ctx, "Error cqe.\n");
@@ -1368,7 +1331,7 @@ static int qedi_wait_for_cleanup_request(struct qedi_ctx *qedi,
return 0;
}
-static void qedi_tmf_work(struct work_struct *work)
+static void qedi_abort_work(struct work_struct *work)
{
struct qedi_cmd *qedi_cmd =
container_of(work, struct qedi_cmd, tmf_work);
@@ -1381,17 +1344,29 @@ static void qedi_tmf_work(struct work_struct *work)
struct iscsi_task *ctask;
struct iscsi_tm *tmf_hdr;
s16 rval = 0;
- s16 tid = 0;
mtask = qedi_cmd->task;
tmf_hdr = (struct iscsi_tm *)mtask->hdr;
- set_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags);
- ctask = iscsi_itt_to_task(conn, tmf_hdr->rtt);
- if (!ctask || !ctask->sc) {
- QEDI_ERR(&qedi->dbg_ctx, "Task already completed\n");
- goto abort_ret;
+ spin_lock_bh(&conn->session->back_lock);
+ ctask = iscsi_itt_to_ctask(conn, tmf_hdr->rtt);
+ if (!ctask) {
+ spin_unlock_bh(&conn->session->back_lock);
+ QEDI_ERR(&qedi->dbg_ctx, "Invalid RTT. Letting abort timeout.\n");
+ goto clear_cleanup;
+ }
+
+ if (iscsi_task_is_completed(ctask)) {
+ spin_unlock_bh(&conn->session->back_lock);
+ QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO,
+ "Task already completed\n");
+ /*
+ * We have to still send the TMF because libiscsi needs the
+ * response to avoid a timeout.
+ */
+ goto send_tmf;
}
+ spin_unlock_bh(&conn->session->back_lock);
cmd = (struct qedi_cmd *)ctask->dd_data;
QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO,
@@ -1402,19 +1377,21 @@ static void qedi_tmf_work(struct work_struct *work)
if (qedi_do_not_recover) {
QEDI_ERR(&qedi->dbg_ctx, "DONT SEND CLEANUP/ABORT %d\n",
qedi_do_not_recover);
- goto abort_ret;
+ goto clear_cleanup;
}
- list_work = kzalloc(sizeof(*list_work), GFP_ATOMIC);
+ list_work = kzalloc(sizeof(*list_work), GFP_NOIO);
if (!list_work) {
QEDI_ERR(&qedi->dbg_ctx, "Memory allocation failed\n");
- goto abort_ret;
+ goto clear_cleanup;
}
qedi_cmd->type = TYPEIO;
+ qedi_cmd->state = CLEANUP_WAIT;
list_work->qedi_cmd = qedi_cmd;
list_work->rtid = cmd->task_id;
list_work->state = QEDI_WORK_SCHEDULED;
+ list_work->ctask = ctask;
qedi_cmd->list_tmf_work = list_work;
QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM,
@@ -1437,23 +1414,13 @@ static void qedi_tmf_work(struct work_struct *work)
goto ldel_exit;
}
- tid = qedi_get_task_idx(qedi);
- if (tid == -1) {
- QEDI_ERR(&qedi->dbg_ctx, "Invalid tid, cid=0x%x\n",
- qedi_conn->iscsi_conn_id);
- goto ldel_exit;
- }
-
- qedi_cmd->task_id = tid;
- qedi_send_iscsi_tmf(qedi_conn, qedi_cmd->task);
-
-abort_ret:
- clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags);
- return;
+send_tmf:
+ send_iscsi_tmf(qedi_conn, qedi_cmd->task, ctask);
+ goto clear_cleanup;
ldel_exit:
spin_lock_bh(&qedi_conn->tmf_work_lock);
- if (!qedi_cmd->list_tmf_work) {
+ if (qedi_cmd->list_tmf_work) {
list_del_init(&list_work->list);
qedi_cmd->list_tmf_work = NULL;
kfree(list_work);
@@ -1468,18 +1435,19 @@ ldel_exit:
}
spin_unlock(&qedi_conn->list_lock);
- clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags);
+clear_cleanup:
+ spin_lock(&qedi_conn->tmf_work_lock);
+ qedi_conn->fw_cleanup_works--;
+ spin_unlock(&qedi_conn->tmf_work_lock);
}
-static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn,
- struct iscsi_task *mtask)
+static int send_iscsi_tmf(struct qedi_conn *qedi_conn, struct iscsi_task *mtask,
+ struct iscsi_task *ctask)
{
struct iscsi_tmf_request_hdr tmf_pdu_header;
struct iscsi_task_params task_params;
struct qedi_ctx *qedi = qedi_conn->qedi;
struct e4_iscsi_task_context *fw_task_ctx;
- struct iscsi_conn *conn = qedi_conn->cls_conn->dd_data;
- struct iscsi_task *ctask;
struct iscsi_tm *tmf_hdr;
struct qedi_cmd *qedi_cmd;
struct qedi_cmd *cmd;
@@ -1487,7 +1455,6 @@ static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn,
u32 scsi_lun[2];
s16 tid = 0;
u16 sq_idx = 0;
- int rval = 0;
tmf_hdr = (struct iscsi_tm *)mtask->hdr;
qedi_cmd = (struct qedi_cmd *)mtask->dd_data;
@@ -1520,12 +1487,6 @@ static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn,
if ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
ISCSI_TM_FUNC_ABORT_TASK) {
- ctask = iscsi_itt_to_task(conn, tmf_hdr->rtt);
- if (!ctask || !ctask->sc) {
- QEDI_ERR(&qedi->dbg_ctx,
- "Could not get reference task\n");
- return 0;
- }
cmd = (struct qedi_cmd *)ctask->dd_data;
tmf_pdu_header.rtt =
qedi_set_itt(cmd->task_id,
@@ -1551,10 +1512,7 @@ static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn,
task_params.sqe = &ep->sq[sq_idx];
memset(task_params.sqe, 0, sizeof(struct iscsi_wqe));
- rval = init_initiator_tmf_request_task(&task_params,
- &tmf_pdu_header);
- if (rval)
- return -1;
+ init_initiator_tmf_request_task(&task_params, &tmf_pdu_header);
spin_lock(&qedi_conn->list_lock);
list_add_tail(&qedi_cmd->io_cmd, &qedi_conn->active_cmd_list);
@@ -1566,47 +1524,34 @@ static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn,
return 0;
}
-int qedi_iscsi_abort_work(struct qedi_conn *qedi_conn,
- struct iscsi_task *mtask)
+int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, struct iscsi_task *mtask)
{
+ struct iscsi_tm *tmf_hdr = (struct iscsi_tm *)mtask->hdr;
+ struct qedi_cmd *qedi_cmd = mtask->dd_data;
struct qedi_ctx *qedi = qedi_conn->qedi;
- struct iscsi_tm *tmf_hdr;
- struct qedi_cmd *qedi_cmd = (struct qedi_cmd *)mtask->dd_data;
- s16 tid = 0;
+ int rc = 0;
- tmf_hdr = (struct iscsi_tm *)mtask->hdr;
- qedi_cmd->task = mtask;
+ switch (tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) {
+ case ISCSI_TM_FUNC_ABORT_TASK:
+ spin_lock(&qedi_conn->tmf_work_lock);
+ qedi_conn->fw_cleanup_works++;
+ spin_unlock(&qedi_conn->tmf_work_lock);
- /* If abort task then schedule the work and return */
- if ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
- ISCSI_TM_FUNC_ABORT_TASK) {
- qedi_cmd->state = CLEANUP_WAIT;
- INIT_WORK(&qedi_cmd->tmf_work, qedi_tmf_work);
+ INIT_WORK(&qedi_cmd->tmf_work, qedi_abort_work);
queue_work(qedi->tmf_thread, &qedi_cmd->tmf_work);
-
- } else if (((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
- ISCSI_TM_FUNC_LOGICAL_UNIT_RESET) ||
- ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
- ISCSI_TM_FUNC_TARGET_WARM_RESET) ||
- ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) ==
- ISCSI_TM_FUNC_TARGET_COLD_RESET)) {
- tid = qedi_get_task_idx(qedi);
- if (tid == -1) {
- QEDI_ERR(&qedi->dbg_ctx, "Invalid tid, cid=0x%x\n",
- qedi_conn->iscsi_conn_id);
- return -1;
- }
- qedi_cmd->task_id = tid;
-
- qedi_send_iscsi_tmf(qedi_conn, qedi_cmd->task);
-
- } else {
+ break;
+ case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
+ case ISCSI_TM_FUNC_TARGET_WARM_RESET:
+ case ISCSI_TM_FUNC_TARGET_COLD_RESET:
+ rc = send_iscsi_tmf(qedi_conn, mtask, NULL);
+ break;
+ default:
QEDI_ERR(&qedi->dbg_ctx, "Invalid tmf, cid=0x%x\n",
qedi_conn->iscsi_conn_id);
- return -1;
+ return -EINVAL;
}
- return 0;
+ return rc;
}
int qedi_send_iscsi_text(struct qedi_conn *qedi_conn,
diff --git a/drivers/scsi/qedi/qedi_gbl.h b/drivers/scsi/qedi/qedi_gbl.h
index 116645c08c71..fb44a282613e 100644
--- a/drivers/scsi/qedi/qedi_gbl.h
+++ b/drivers/scsi/qedi/qedi_gbl.h
@@ -31,8 +31,7 @@ int qedi_send_iscsi_login(struct qedi_conn *qedi_conn,
struct iscsi_task *task);
int qedi_send_iscsi_logout(struct qedi_conn *qedi_conn,
struct iscsi_task *task);
-int qedi_iscsi_abort_work(struct qedi_conn *qedi_conn,
- struct iscsi_task *mtask);
+int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, struct iscsi_task *mtask);
int qedi_send_iscsi_text(struct qedi_conn *qedi_conn,
struct iscsi_task *task);
int qedi_send_iscsi_nopout(struct qedi_conn *qedi_conn,
diff --git a/drivers/scsi/qedi/qedi_iscsi.c b/drivers/scsi/qedi/qedi_iscsi.c
index 08c05403cd72..bf581ecea897 100644
--- a/drivers/scsi/qedi/qedi_iscsi.c
+++ b/drivers/scsi/qedi/qedi_iscsi.c
@@ -330,12 +330,22 @@ free_conn:
void qedi_mark_device_missing(struct iscsi_cls_session *cls_session)
{
- iscsi_block_session(cls_session);
+ struct iscsi_session *session = cls_session->dd_data;
+ struct qedi_conn *qedi_conn = session->leadconn->dd_data;
+
+ spin_lock_bh(&session->frwd_lock);
+ set_bit(QEDI_BLOCK_IO, &qedi_conn->qedi->flags);
+ spin_unlock_bh(&session->frwd_lock);
}
void qedi_mark_device_available(struct iscsi_cls_session *cls_session)
{
- iscsi_unblock_session(cls_session);
+ struct iscsi_session *session = cls_session->dd_data;
+ struct qedi_conn *qedi_conn = session->leadconn->dd_data;
+
+ spin_lock_bh(&session->frwd_lock);
+ clear_bit(QEDI_BLOCK_IO, &qedi_conn->qedi->flags);
+ spin_unlock_bh(&session->frwd_lock);
}
static int qedi_bind_conn_to_iscsi_cid(struct qedi_ctx *qedi,
@@ -377,6 +387,7 @@ static int qedi_conn_bind(struct iscsi_cls_session *cls_session,
struct qedi_ctx *qedi = iscsi_host_priv(shost);
struct qedi_endpoint *qedi_ep;
struct iscsi_endpoint *ep;
+ int rc = 0;
ep = iscsi_lookup_endpoint(transport_fd);
if (!ep)
@@ -384,11 +395,16 @@ static int qedi_conn_bind(struct iscsi_cls_session *cls_session,
qedi_ep = ep->dd_data;
if ((qedi_ep->state == EP_STATE_TCP_FIN_RCVD) ||
- (qedi_ep->state == EP_STATE_TCP_RST_RCVD))
- return -EINVAL;
+ (qedi_ep->state == EP_STATE_TCP_RST_RCVD)) {
+ rc = -EINVAL;
+ goto put_ep;
+ }
+
+ if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) {
+ rc = -EINVAL;
+ goto put_ep;
+ }
- if (iscsi_conn_bind(cls_session, cls_conn, is_leading))
- return -EINVAL;
qedi_ep->conn = qedi_conn;
qedi_conn->ep = qedi_ep;
@@ -398,13 +414,18 @@ static int qedi_conn_bind(struct iscsi_cls_session *cls_session,
qedi_conn->cmd_cleanup_req = 0;
qedi_conn->cmd_cleanup_cmpl = 0;
- if (qedi_bind_conn_to_iscsi_cid(qedi, qedi_conn))
- return -EINVAL;
+ if (qedi_bind_conn_to_iscsi_cid(qedi, qedi_conn)) {
+ rc = -EINVAL;
+ goto put_ep;
+ }
+
spin_lock_init(&qedi_conn->tmf_work_lock);
INIT_LIST_HEAD(&qedi_conn->tmf_work_list);
init_waitqueue_head(&qedi_conn->wait_queue);
- return 0;
+put_ep:
+ iscsi_put_endpoint(ep);
+ return rc;
}
static int qedi_iscsi_update_conn(struct qedi_ctx *qedi,
@@ -582,7 +603,11 @@ static int qedi_conn_start(struct iscsi_cls_conn *cls_conn)
goto start_err;
}
- clear_bit(QEDI_CONN_FW_CLEANUP, &qedi_conn->flags);
+ spin_lock(&qedi_conn->tmf_work_lock);
+ qedi_conn->fw_cleanup_works = 0;
+ qedi_conn->ep_disconnect_starting = false;
+ spin_unlock(&qedi_conn->tmf_work_lock);
+
qedi_conn->abrt_conn = 0;
rval = iscsi_conn_start(cls_conn);
@@ -742,7 +767,7 @@ static int qedi_iscsi_send_generic_request(struct iscsi_task *task)
rc = qedi_send_iscsi_logout(qedi_conn, task);
break;
case ISCSI_OP_SCSI_TMFUNC:
- rc = qedi_iscsi_abort_work(qedi_conn, task);
+ rc = qedi_send_iscsi_tmf(qedi_conn, task);
break;
case ISCSI_OP_TEXT:
rc = qedi_send_iscsi_text(qedi_conn, task);
@@ -772,7 +797,6 @@ static int qedi_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task)
}
cmd->conn = conn->dd_data;
- cmd->scsi_cmd = NULL;
return qedi_iscsi_send_generic_request(task);
}
@@ -783,9 +807,16 @@ static int qedi_task_xmit(struct iscsi_task *task)
struct qedi_cmd *cmd = task->dd_data;
struct scsi_cmnd *sc = task->sc;
+ /* Clear now so in cleanup_task we know it didn't make it */
+ cmd->scsi_cmd = NULL;
+ cmd->task_id = U16_MAX;
+
if (test_bit(QEDI_IN_SHUTDOWN, &qedi_conn->qedi->flags))
return -ENODEV;
+ if (test_bit(QEDI_BLOCK_IO, &qedi_conn->qedi->flags))
+ return -EACCES;
+
cmd->state = 0;
cmd->task = NULL;
cmd->use_slowpath = false;
@@ -988,12 +1019,10 @@ static void qedi_ep_disconnect(struct iscsi_endpoint *ep)
{
struct qedi_endpoint *qedi_ep;
struct qedi_conn *qedi_conn = NULL;
- struct iscsi_conn *conn = NULL;
struct qedi_ctx *qedi;
int ret = 0;
int wait_delay;
int abrt_conn = 0;
- int count = 10;
wait_delay = 60 * HZ + DEF_MAX_RT_TIME;
qedi_ep = ep->dd_data;
@@ -1007,17 +1036,21 @@ static void qedi_ep_disconnect(struct iscsi_endpoint *ep)
if (qedi_ep->conn) {
qedi_conn = qedi_ep->conn;
- conn = qedi_conn->cls_conn->dd_data;
- iscsi_suspend_queue(conn);
abrt_conn = qedi_conn->abrt_conn;
- while (count--) {
- if (!test_bit(QEDI_CONN_FW_CLEANUP,
- &qedi_conn->flags)) {
- break;
- }
+ QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO,
+ "cid=0x%x qedi_ep=%p waiting for %d tmfs\n",
+ qedi_ep->iscsi_cid, qedi_ep,
+ qedi_conn->fw_cleanup_works);
+
+ spin_lock(&qedi_conn->tmf_work_lock);
+ qedi_conn->ep_disconnect_starting = true;
+ while (qedi_conn->fw_cleanup_works > 0) {
+ spin_unlock(&qedi_conn->tmf_work_lock);
msleep(1000);
+ spin_lock(&qedi_conn->tmf_work_lock);
}
+ spin_unlock(&qedi_conn->tmf_work_lock);
if (test_bit(QEDI_IN_RECOVERY, &qedi->flags)) {
if (qedi_do_not_recover) {
@@ -1383,13 +1416,24 @@ static umode_t qedi_attr_is_visible(int param_type, int param)
static void qedi_cleanup_task(struct iscsi_task *task)
{
- if (!task->sc || task->state == ISCSI_TASK_PENDING) {
+ struct qedi_cmd *cmd;
+
+ if (task->state == ISCSI_TASK_PENDING) {
QEDI_INFO(NULL, QEDI_LOG_IO, "Returning ref_cnt=%d\n",
refcount_read(&task->refcount));
return;
}
- qedi_iscsi_unmap_sg_list(task->dd_data);
+ if (task->sc)
+ qedi_iscsi_unmap_sg_list(task->dd_data);
+
+ cmd = task->dd_data;
+ if (cmd->task_id != U16_MAX)
+ qedi_clear_task_idx(iscsi_host_priv(task->conn->session->host),
+ cmd->task_id);
+
+ cmd->task_id = U16_MAX;
+ cmd->scsi_cmd = NULL;
}
struct iscsi_transport qedi_iscsi_transport = {
@@ -1401,6 +1445,7 @@ struct iscsi_transport qedi_iscsi_transport = {
.destroy_session = qedi_session_destroy,
.create_conn = qedi_conn_create,
.bind_conn = qedi_conn_bind,
+ .unbind_conn = iscsi_conn_unbind,
.start_conn = qedi_conn_start,
.stop_conn = iscsi_conn_stop,
.destroy_conn = qedi_conn_destroy,
@@ -1620,8 +1665,11 @@ void qedi_clear_session_ctx(struct iscsi_cls_session *cls_sess)
struct iscsi_conn *conn = session->leadconn;
struct qedi_conn *qedi_conn = conn->dd_data;
- if (iscsi_is_session_online(cls_sess))
+ if (iscsi_is_session_online(cls_sess)) {
+ if (conn)
+ iscsi_suspend_queue(conn);
qedi_ep_disconnect(qedi_conn->iscsi_ep);
+ }
qedi_conn_destroy(qedi_conn->cls_conn);
diff --git a/drivers/scsi/qedi/qedi_iscsi.h b/drivers/scsi/qedi/qedi_iscsi.h
index 39dc27c85e3c..758735209e15 100644
--- a/drivers/scsi/qedi/qedi_iscsi.h
+++ b/drivers/scsi/qedi/qedi_iscsi.h
@@ -169,8 +169,8 @@ struct qedi_conn {
struct list_head tmf_work_list;
wait_queue_head_t wait_queue;
spinlock_t tmf_work_lock; /* tmf work lock */
- unsigned long flags;
-#define QEDI_CONN_FW_CLEANUP 1
+ bool ep_disconnect_starting;
+ int fw_cleanup_works;
};
struct qedi_cmd {
@@ -212,6 +212,7 @@ struct qedi_cmd {
struct qedi_work_map {
struct list_head list;
struct qedi_cmd *qedi_cmd;
+ struct iscsi_task *ctask;
int rtid;
int state;
diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c
index 2455d1448a7e..edf915432704 100644
--- a/drivers/scsi/qedi/qedi_main.c
+++ b/drivers/scsi/qedi/qedi_main.c
@@ -640,7 +640,7 @@ static struct qedi_ctx *qedi_host_alloc(struct pci_dev *pdev)
goto exit_setup_shost;
}
- shost->max_id = QEDI_MAX_ISCSI_CONNS_PER_HBA;
+ shost->max_id = QEDI_MAX_ISCSI_CONNS_PER_HBA - 1;
shost->max_channel = 0;
shost->max_lun = ~0;
shost->max_cmd_len = 16;
diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c
index 0cacb667a88b..e119f8b24e33 100644
--- a/drivers/scsi/qla2xxx/qla_nvme.c
+++ b/drivers/scsi/qla2xxx/qla_nvme.c
@@ -671,7 +671,7 @@ void qla_nvme_unregister_remote_port(struct fc_port *fcport)
if (!IS_ENABLED(CONFIG_NVME_FC))
return;
- ql_log(ql_log_warn, NULL, 0x2112,
+ ql_log(ql_log_warn, fcport->vha, 0x2112,
"%s: unregister remoteport on %p %8phN\n",
__func__, fcport, fcport->port_name);
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index ad3afe30f617..0e7a7e82e028 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -259,6 +259,7 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
.start_conn = qla4xxx_conn_start,
.create_conn = qla4xxx_conn_create,
.bind_conn = qla4xxx_conn_bind,
+ .unbind_conn = iscsi_conn_unbind,
.stop_conn = iscsi_conn_stop,
.destroy_conn = qla4xxx_conn_destroy,
.set_param = iscsi_set_param,
@@ -3234,6 +3235,7 @@ static int qla4xxx_conn_bind(struct iscsi_cls_session *cls_session,
conn = cls_conn->dd_data;
qla_conn = conn->dd_data;
qla_conn->qla_ep = ep->dd_data;
+ iscsi_put_endpoint(ep);
return 0;
}
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 441f0152193f..b07105ae7c91 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -86,16 +86,10 @@ struct iscsi_internal {
struct transport_container session_cont;
};
-/* Worker to perform connection failure on unresponsive connections
- * completely in kernel space.
- */
-static void stop_conn_work_fn(struct work_struct *work);
-static DECLARE_WORK(stop_conn_work, stop_conn_work_fn);
-
static atomic_t iscsi_session_nr; /* sysfs session id for next new session */
static struct workqueue_struct *iscsi_eh_timer_workq;
-static struct workqueue_struct *iscsi_destroy_workq;
+static struct workqueue_struct *iscsi_conn_cleanup_workq;
static DEFINE_IDA(iscsi_sess_ida);
/*
@@ -268,9 +262,20 @@ void iscsi_destroy_endpoint(struct iscsi_endpoint *ep)
}
EXPORT_SYMBOL_GPL(iscsi_destroy_endpoint);
+void iscsi_put_endpoint(struct iscsi_endpoint *ep)
+{
+ put_device(&ep->dev);
+}
+EXPORT_SYMBOL_GPL(iscsi_put_endpoint);
+
+/**
+ * iscsi_lookup_endpoint - get ep from handle
+ * @handle: endpoint handle
+ *
+ * Caller must do a iscsi_put_endpoint.
+ */
struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle)
{
- struct iscsi_endpoint *ep;
struct device *dev;
dev = class_find_device(&iscsi_endpoint_class, NULL, &handle,
@@ -278,13 +283,7 @@ struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle)
if (!dev)
return NULL;
- ep = iscsi_dev_to_endpoint(dev);
- /*
- * we can drop this now because the interface will prevent
- * removals and lookups from racing.
- */
- put_device(dev);
- return ep;
+ return iscsi_dev_to_endpoint(dev);
}
EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint);
@@ -1620,12 +1619,6 @@ static DECLARE_TRANSPORT_CLASS(iscsi_connection_class,
static struct sock *nls;
static DEFINE_MUTEX(rx_queue_mutex);
-/*
- * conn_mutex protects the {start,bind,stop,destroy}_conn from racing
- * against the kernel stop_connection recovery mechanism
- */
-static DEFINE_MUTEX(conn_mutex);
-
static LIST_HEAD(sesslist);
static DEFINE_SPINLOCK(sesslock);
static LIST_HEAD(connlist);
@@ -1976,6 +1969,8 @@ static void __iscsi_unblock_session(struct work_struct *work)
*/
void iscsi_unblock_session(struct iscsi_cls_session *session)
{
+ flush_work(&session->block_work);
+
queue_work(iscsi_eh_timer_workq, &session->unblock_work);
/*
* Blocking the session can be done from any context so we only
@@ -2242,6 +2237,123 @@ void iscsi_remove_session(struct iscsi_cls_session *session)
}
EXPORT_SYMBOL_GPL(iscsi_remove_session);
+static void iscsi_stop_conn(struct iscsi_cls_conn *conn, int flag)
+{
+ ISCSI_DBG_TRANS_CONN(conn, "Stopping conn.\n");
+
+ switch (flag) {
+ case STOP_CONN_RECOVER:
+ conn->state = ISCSI_CONN_FAILED;
+ break;
+ case STOP_CONN_TERM:
+ conn->state = ISCSI_CONN_DOWN;
+ break;
+ default:
+ iscsi_cls_conn_printk(KERN_ERR, conn, "invalid stop flag %d\n",
+ flag);
+ return;
+ }
+
+ conn->transport->stop_conn(conn, flag);
+ ISCSI_DBG_TRANS_CONN(conn, "Stopping conn done.\n");
+}
+
+static int iscsi_if_stop_conn(struct iscsi_transport *transport,
+ struct iscsi_uevent *ev)
+{
+ int flag = ev->u.stop_conn.flag;
+ struct iscsi_cls_conn *conn;
+
+ conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid);
+ if (!conn)
+ return -EINVAL;
+
+ ISCSI_DBG_TRANS_CONN(conn, "iscsi if conn stop.\n");
+ /*
+ * If this is a termination we have to call stop_conn with that flag
+ * so the correct states get set. If we haven't run the work yet try to
+ * avoid the extra run.
+ */
+ if (flag == STOP_CONN_TERM) {
+ cancel_work_sync(&conn->cleanup_work);
+ iscsi_stop_conn(conn, flag);
+ } else {
+ /*
+ * Figure out if it was the kernel or userspace initiating this.
+ */
+ if (!test_and_set_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) {
+ iscsi_stop_conn(conn, flag);
+ } else {
+ ISCSI_DBG_TRANS_CONN(conn,
+ "flush kernel conn cleanup.\n");
+ flush_work(&conn->cleanup_work);
+ }
+ /*
+ * Only clear for recovery to avoid extra cleanup runs during
+ * termination.
+ */
+ clear_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags);
+ }
+ ISCSI_DBG_TRANS_CONN(conn, "iscsi if conn stop done.\n");
+ return 0;
+}
+
+static void iscsi_ep_disconnect(struct iscsi_cls_conn *conn, bool is_active)
+{
+ struct iscsi_cls_session *session = iscsi_conn_to_session(conn);
+ struct iscsi_endpoint *ep;
+
+ ISCSI_DBG_TRANS_CONN(conn, "disconnect ep.\n");
+ conn->state = ISCSI_CONN_FAILED;
+
+ if (!conn->ep || !session->transport->ep_disconnect)
+ return;
+
+ ep = conn->ep;
+ conn->ep = NULL;
+
+ session->transport->unbind_conn(conn, is_active);
+ session->transport->ep_disconnect(ep);
+ ISCSI_DBG_TRANS_CONN(conn, "disconnect ep done.\n");
+}
+
+static void iscsi_cleanup_conn_work_fn(struct work_struct *work)
+{
+ struct iscsi_cls_conn *conn = container_of(work, struct iscsi_cls_conn,
+ cleanup_work);
+ struct iscsi_cls_session *session = iscsi_conn_to_session(conn);
+
+ mutex_lock(&conn->ep_mutex);
+ /*
+ * If we are not at least bound there is nothing for us to do. Userspace
+ * will do a ep_disconnect call if offload is used, but will not be
+ * doing a stop since there is nothing to clean up, so we have to clear
+ * the cleanup bit here.
+ */
+ if (conn->state != ISCSI_CONN_BOUND && conn->state != ISCSI_CONN_UP) {
+ ISCSI_DBG_TRANS_CONN(conn, "Got error while conn is already failed. Ignoring.\n");
+ clear_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags);
+ mutex_unlock(&conn->ep_mutex);
+ return;
+ }
+
+ iscsi_ep_disconnect(conn, false);
+
+ if (system_state != SYSTEM_RUNNING) {
+ /*
+ * If the user has set up for the session to never timeout
+ * then hang like they wanted. For all other cases fail right
+ * away since userspace is not going to relogin.
+ */
+ if (session->recovery_tmo > 0)
+ session->recovery_tmo = 0;
+ }
+
+ iscsi_stop_conn(conn, STOP_CONN_RECOVER);
+ mutex_unlock(&conn->ep_mutex);
+ ISCSI_DBG_TRANS_CONN(conn, "cleanup done.\n");
+}
+
void iscsi_free_session(struct iscsi_cls_session *session)
{
ISCSI_DBG_TRANS_SESSION(session, "Freeing session\n");
@@ -2281,7 +2393,7 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid)
mutex_init(&conn->ep_mutex);
INIT_LIST_HEAD(&conn->conn_list);
- INIT_LIST_HEAD(&conn->conn_list_err);
+ INIT_WORK(&conn->cleanup_work, iscsi_cleanup_conn_work_fn);
conn->transport = transport;
conn->cid = cid;
conn->state = ISCSI_CONN_DOWN;
@@ -2338,7 +2450,6 @@ int iscsi_destroy_conn(struct iscsi_cls_conn *conn)
spin_lock_irqsave(&connlock, flags);
list_del(&conn->conn_list);
- list_del(&conn->conn_list_err);
spin_unlock_irqrestore(&connlock, flags);
transport_unregister_device(&conn->dev);
@@ -2348,6 +2459,18 @@ int iscsi_destroy_conn(struct iscsi_cls_conn *conn)
}
EXPORT_SYMBOL_GPL(iscsi_destroy_conn);
+void iscsi_put_conn(struct iscsi_cls_conn *conn)
+{
+ put_device(&conn->dev);
+}
+EXPORT_SYMBOL_GPL(iscsi_put_conn);
+
+void iscsi_get_conn(struct iscsi_cls_conn *conn)
+{
+ get_device(&conn->dev);
+}
+EXPORT_SYMBOL_GPL(iscsi_get_conn);
+
/*
* iscsi interface functions
*/
@@ -2453,77 +2576,6 @@ int iscsi_offload_mesg(struct Scsi_Host *shost,
}
EXPORT_SYMBOL_GPL(iscsi_offload_mesg);
-/*
- * This can be called without the rx_queue_mutex, if invoked by the kernel
- * stop work. But, in that case, it is guaranteed not to race with
- * iscsi_destroy by conn_mutex.
- */
-static void iscsi_if_stop_conn(struct iscsi_cls_conn *conn, int flag)
-{
- /*
- * It is important that this path doesn't rely on
- * rx_queue_mutex, otherwise, a thread doing allocation on a
- * start_session/start_connection could sleep waiting on a
- * writeback to a failed iscsi device, that cannot be recovered
- * because the lock is held. If we don't hold it here, the
- * kernel stop_conn_work_fn has a chance to stop the broken
- * session and resolve the allocation.
- *
- * Still, the user invoked .stop_conn() needs to be serialized
- * with stop_conn_work_fn by a private mutex. Not pretty, but
- * it works.
- */
- mutex_lock(&conn_mutex);
- switch (flag) {
- case STOP_CONN_RECOVER:
- conn->state = ISCSI_CONN_FAILED;
- break;
- case STOP_CONN_TERM:
- conn->state = ISCSI_CONN_DOWN;
- break;
- default:
- iscsi_cls_conn_printk(KERN_ERR, conn,
- "invalid stop flag %d\n", flag);
- goto unlock;
- }
-
- conn->transport->stop_conn(conn, flag);
-unlock:
- mutex_unlock(&conn_mutex);
-}
-
-static void stop_conn_work_fn(struct work_struct *work)
-{
- struct iscsi_cls_conn *conn, *tmp;
- unsigned long flags;
- LIST_HEAD(recovery_list);
-
- spin_lock_irqsave(&connlock, flags);
- if (list_empty(&connlist_err)) {
- spin_unlock_irqrestore(&connlock, flags);
- return;
- }
- list_splice_init(&connlist_err, &recovery_list);
- spin_unlock_irqrestore(&connlock, flags);
-
- list_for_each_entry_safe(conn, tmp, &recovery_list, conn_list_err) {
- uint32_t sid = iscsi_conn_get_sid(conn);
- struct iscsi_cls_session *session;
-
- session = iscsi_session_lookup(sid);
- if (session) {
- if (system_state != SYSTEM_RUNNING) {
- session->recovery_tmo = 0;
- iscsi_if_stop_conn(conn, STOP_CONN_TERM);
- } else {
- iscsi_if_stop_conn(conn, STOP_CONN_RECOVER);
- }
- }
-
- list_del_init(&conn->conn_list_err);
- }
-}
-
void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
{
struct nlmsghdr *nlh;
@@ -2531,12 +2583,9 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
struct iscsi_uevent *ev;
struct iscsi_internal *priv;
int len = nlmsg_total_size(sizeof(*ev));
- unsigned long flags;
- spin_lock_irqsave(&connlock, flags);
- list_add(&conn->conn_list_err, &connlist_err);
- spin_unlock_irqrestore(&connlock, flags);
- queue_work(system_unbound_wq, &stop_conn_work);
+ if (!test_and_set_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags))
+ queue_work(iscsi_conn_cleanup_workq, &conn->cleanup_work);
priv = iscsi_if_transport_lookup(conn->transport);
if (!priv)
@@ -2866,26 +2915,17 @@ static int
iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
{
struct iscsi_cls_conn *conn;
- unsigned long flags;
conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid);
if (!conn)
return -EINVAL;
- spin_lock_irqsave(&connlock, flags);
- if (!list_empty(&conn->conn_list_err)) {
- spin_unlock_irqrestore(&connlock, flags);
- return -EAGAIN;
- }
- spin_unlock_irqrestore(&connlock, flags);
-
+ ISCSI_DBG_TRANS_CONN(conn, "Flushing cleanup during destruction\n");
+ flush_work(&conn->cleanup_work);
ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n");
- mutex_lock(&conn_mutex);
if (transport->destroy_conn)
transport->destroy_conn(conn);
- mutex_unlock(&conn_mutex);
-
return 0;
}
@@ -2975,15 +3015,31 @@ static int iscsi_if_ep_disconnect(struct iscsi_transport *transport,
ep = iscsi_lookup_endpoint(ep_handle);
if (!ep)
return -EINVAL;
+
conn = ep->conn;
- if (conn) {
- mutex_lock(&conn->ep_mutex);
- conn->ep = NULL;
+ if (!conn) {
+ /*
+ * conn was not even bound yet, so we can't get iscsi conn
+ * failures yet.
+ */
+ transport->ep_disconnect(ep);
+ goto put_ep;
+ }
+
+ mutex_lock(&conn->ep_mutex);
+ /* Check if this was a conn error and the kernel took ownership */
+ if (test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) {
+ ISCSI_DBG_TRANS_CONN(conn, "flush kernel conn cleanup.\n");
mutex_unlock(&conn->ep_mutex);
- conn->state = ISCSI_CONN_FAILED;
+
+ flush_work(&conn->cleanup_work);
+ goto put_ep;
}
- transport->ep_disconnect(ep);
+ iscsi_ep_disconnect(conn, false);
+ mutex_unlock(&conn->ep_mutex);
+put_ep:
+ iscsi_put_endpoint(ep);
return 0;
}
@@ -3009,6 +3065,7 @@ iscsi_if_transport_ep(struct iscsi_transport *transport,
ev->r.retcode = transport->ep_poll(ep,
ev->u.ep_poll.timeout_ms);
+ iscsi_put_endpoint(ep);
break;
case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
rc = iscsi_if_ep_disconnect(transport,
@@ -3639,18 +3696,129 @@ exit_host_stats:
return err;
}
+static int iscsi_if_transport_conn(struct iscsi_transport *transport,
+ struct nlmsghdr *nlh)
+{
+ struct iscsi_uevent *ev = nlmsg_data(nlh);
+ struct iscsi_cls_session *session;
+ struct iscsi_cls_conn *conn = NULL;
+ struct iscsi_endpoint *ep;
+ uint32_t pdu_len;
+ int err = 0;
+
+ switch (nlh->nlmsg_type) {
+ case ISCSI_UEVENT_CREATE_CONN:
+ return iscsi_if_create_conn(transport, ev);
+ case ISCSI_UEVENT_DESTROY_CONN:
+ return iscsi_if_destroy_conn(transport, ev);
+ case ISCSI_UEVENT_STOP_CONN:
+ return iscsi_if_stop_conn(transport, ev);
+ }
+
+ /*
+ * The following cmds need to be run under the ep_mutex so in kernel
+ * conn cleanup (ep_disconnect + unbind and conn) is not done while
+ * these are running. They also must not run if we have just run a conn
+ * cleanup because they would set the state in a way that might allow
+ * IO or send IO themselves.
+ */
+ switch (nlh->nlmsg_type) {
+ case ISCSI_UEVENT_START_CONN:
+ conn = iscsi_conn_lookup(ev->u.start_conn.sid,
+ ev->u.start_conn.cid);
+ break;
+ case ISCSI_UEVENT_BIND_CONN:
+ conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid);
+ break;
+ case ISCSI_UEVENT_SEND_PDU:
+ conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid);
+ break;
+ }
+
+ if (!conn)
+ return -EINVAL;
+
+ mutex_lock(&conn->ep_mutex);
+ if (test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) {
+ mutex_unlock(&conn->ep_mutex);
+ ev->r.retcode = -ENOTCONN;
+ return 0;
+ }
+
+ switch (nlh->nlmsg_type) {
+ case ISCSI_UEVENT_BIND_CONN:
+ if (conn->ep) {
+ /*
+ * For offload boot support where iscsid is restarted
+ * during the pivot root stage, the ep will be intact
+ * here when the new iscsid instance starts up and
+ * reconnects.
+ */
+ iscsi_ep_disconnect(conn, true);
+ }
+
+ session = iscsi_session_lookup(ev->u.b_conn.sid);
+ if (!session) {
+ err = -EINVAL;
+ break;
+ }
+
+ ev->r.retcode = transport->bind_conn(session, conn,
+ ev->u.b_conn.transport_eph,
+ ev->u.b_conn.is_leading);
+ if (!ev->r.retcode)
+ conn->state = ISCSI_CONN_BOUND;
+
+ if (ev->r.retcode || !transport->ep_connect)
+ break;
+
+ ep = iscsi_lookup_endpoint(ev->u.b_conn.transport_eph);
+ if (ep) {
+ ep->conn = conn;
+ conn->ep = ep;
+ iscsi_put_endpoint(ep);
+ } else {
+ err = -ENOTCONN;
+ iscsi_cls_conn_printk(KERN_ERR, conn,
+ "Could not set ep conn binding\n");
+ }
+ break;
+ case ISCSI_UEVENT_START_CONN:
+ ev->r.retcode = transport->start_conn(conn);
+ if (!ev->r.retcode)
+ conn->state = ISCSI_CONN_UP;
+ break;
+ case ISCSI_UEVENT_SEND_PDU:
+ pdu_len = nlh->nlmsg_len - sizeof(*nlh) - sizeof(*ev);
+
+ if ((ev->u.send_pdu.hdr_size > pdu_len) ||
+ (ev->u.send_pdu.data_size > (pdu_len - ev->u.send_pdu.hdr_size))) {
+ err = -EINVAL;
+ break;
+ }
+
+ ev->r.retcode = transport->send_pdu(conn,
+ (struct iscsi_hdr *)((char *)ev + sizeof(*ev)),
+ (char *)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size,
+ ev->u.send_pdu.data_size);
+ break;
+ default:
+ err = -ENOSYS;
+ }
+
+ mutex_unlock(&conn->ep_mutex);
+ return err;
+}
static int
iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
{
int err = 0;
u32 portid;
- u32 pdu_len;
struct iscsi_uevent *ev = nlmsg_data(nlh);
struct iscsi_transport *transport = NULL;
struct iscsi_internal *priv;
struct iscsi_cls_session *session;
- struct iscsi_cls_conn *conn;
struct iscsi_endpoint *ep = NULL;
if (!netlink_capable(skb, CAP_SYS_ADMIN))
@@ -3691,6 +3859,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
ev->u.c_bound_session.initial_cmdsn,
ev->u.c_bound_session.cmds_max,
ev->u.c_bound_session.queue_depth);
+ iscsi_put_endpoint(ep);
break;
case ISCSI_UEVENT_DESTROY_SESSION:
session = iscsi_session_lookup(ev->u.d_session.sid);
@@ -3715,7 +3884,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
list_del_init(&session->sess_list);
spin_unlock_irqrestore(&sesslock, flags);
- queue_work(iscsi_destroy_workq, &session->destroy_work);
+ queue_work(system_unbound_wq, &session->destroy_work);
}
break;
case ISCSI_UEVENT_UNBIND_SESSION:
@@ -3726,89 +3895,16 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
else
err = -EINVAL;
break;
- case ISCSI_UEVENT_CREATE_CONN:
- err = iscsi_if_create_conn(transport, ev);
- break;
- case ISCSI_UEVENT_DESTROY_CONN:
- err = iscsi_if_destroy_conn(transport, ev);
- break;
- case ISCSI_UEVENT_BIND_CONN:
- session = iscsi_session_lookup(ev->u.b_conn.sid);
- conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid);
-
- if (conn && conn->ep)
- iscsi_if_ep_disconnect(transport, conn->ep->id);
-
- if (!session || !conn) {
- err = -EINVAL;
- break;
- }
-
- mutex_lock(&conn_mutex);
- ev->r.retcode = transport->bind_conn(session, conn,
- ev->u.b_conn.transport_eph,
- ev->u.b_conn.is_leading);
- if (!ev->r.retcode)
- conn->state = ISCSI_CONN_BOUND;
- mutex_unlock(&conn_mutex);
-
- if (ev->r.retcode || !transport->ep_connect)
- break;
-
- ep = iscsi_lookup_endpoint(ev->u.b_conn.transport_eph);
- if (ep) {
- ep->conn = conn;
-
- mutex_lock(&conn->ep_mutex);
- conn->ep = ep;
- mutex_unlock(&conn->ep_mutex);
- } else
- iscsi_cls_conn_printk(KERN_ERR, conn,
- "Could not set ep conn "
- "binding\n");
- break;
case ISCSI_UEVENT_SET_PARAM:
err = iscsi_set_param(transport, ev);
break;
- case ISCSI_UEVENT_START_CONN:
- conn = iscsi_conn_lookup(ev->u.start_conn.sid, ev->u.start_conn.cid);
- if (conn) {
- mutex_lock(&conn_mutex);
- ev->r.retcode = transport->start_conn(conn);
- if (!ev->r.retcode)
- conn->state = ISCSI_CONN_UP;
- mutex_unlock(&conn_mutex);
- }
- else
- err = -EINVAL;
- break;
+ case ISCSI_UEVENT_CREATE_CONN:
+ case ISCSI_UEVENT_DESTROY_CONN:
case ISCSI_UEVENT_STOP_CONN:
- conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid);
- if (conn)
- iscsi_if_stop_conn(conn, ev->u.stop_conn.flag);
- else
- err = -EINVAL;
- break;
+ case ISCSI_UEVENT_START_CONN:
+ case ISCSI_UEVENT_BIND_CONN:
case ISCSI_UEVENT_SEND_PDU:
- pdu_len = nlh->nlmsg_len - sizeof(*nlh) - sizeof(*ev);
-
- if ((ev->u.send_pdu.hdr_size > pdu_len) ||
- (ev->u.send_pdu.data_size > (pdu_len - ev->u.send_pdu.hdr_size))) {
- err = -EINVAL;
- break;
- }
-
- conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid);
- if (conn) {
- mutex_lock(&conn_mutex);
- ev->r.retcode = transport->send_pdu(conn,
- (struct iscsi_hdr*)((char*)ev + sizeof(*ev)),
- (char*)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size,
- ev->u.send_pdu.data_size);
- mutex_unlock(&conn_mutex);
- }
- else
- err = -EINVAL;
+ err = iscsi_if_transport_conn(transport, nlh);
break;
case ISCSI_UEVENT_GET_STATS:
err = iscsi_if_get_stats(transport, nlh);
@@ -4656,6 +4752,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
int err;
BUG_ON(!tt);
+ WARN_ON(tt->ep_disconnect && !tt->unbind_conn);
priv = iscsi_if_transport_lookup(tt);
if (priv)
@@ -4810,10 +4907,10 @@ static __init int iscsi_transport_init(void)
goto release_nls;
}
- iscsi_destroy_workq = alloc_workqueue("%s",
- WQ_SYSFS | __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_UNBOUND,
- 1, "iscsi_destroy");
- if (!iscsi_destroy_workq) {
+ iscsi_conn_cleanup_workq = alloc_workqueue("%s",
+ WQ_SYSFS | WQ_MEM_RECLAIM | WQ_UNBOUND, 0,
+ "iscsi_conn_cleanup");
+ if (!iscsi_conn_cleanup_workq) {
err = -ENOMEM;
goto destroy_wq;
}
@@ -4843,7 +4940,7 @@ unregister_transport_class:
static void __exit iscsi_transport_exit(void)
{
- destroy_workqueue(iscsi_destroy_workq);
+ destroy_workqueue(iscsi_conn_cleanup_workq);
destroy_workqueue(iscsi_eh_timer_workq);
netlink_kernel_release(nls);
bus_unregister(&iscsi_flashnode_bus);
diff --git a/drivers/scsi/ufs/ufs-mediatek.c b/drivers/scsi/ufs/ufs-mediatek.c
index 9912e208c2a1..bbe0596afc9d 100644
--- a/drivers/scsi/ufs/ufs-mediatek.c
+++ b/drivers/scsi/ufs/ufs-mediatek.c
@@ -844,6 +844,9 @@ static int ufs_mtk_device_reset(struct ufs_hba *hba)
{
struct arm_smccc_res res;
+ /* disable hba before device reset */
+ ufshcd_hba_stop(hba);
+
ufs_mtk_device_reset_ctrl(0, res);
/*
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 02267b090729..23df69de06f3 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -4492,7 +4492,7 @@ EXPORT_SYMBOL_GPL(ufshcd_make_hba_operational);
* ufshcd_hba_stop - Send controller to reset state
* @hba: per adapter instance
*/
-static inline void ufshcd_hba_stop(struct ufs_hba *hba)
+void ufshcd_hba_stop(struct ufs_hba *hba)
{
unsigned long flags;
int err;
@@ -4511,6 +4511,7 @@ static inline void ufshcd_hba_stop(struct ufs_hba *hba)
if (err)
dev_err(hba->dev, "%s: Controller disable failed\n", __func__);
}
+EXPORT_SYMBOL_GPL(ufshcd_hba_stop);
/**
* ufshcd_hba_execute_hce - initialize the controller
@@ -9769,10 +9770,7 @@ static const struct dev_pm_ops ufs_rpmb_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(NULL, ufshcd_rpmb_resume)
};
-/**
- * Describes the ufs rpmb wlun.
- * Used only to send uac.
- */
+/* ufs_rpmb_wlun_template - Describes UFS RPMB WLUN. Used only to send UAC. */
static struct scsi_driver ufs_rpmb_wlun_template = {
.gendrv = {
.name = "ufs_rpmb_wlun",
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 5735a9516faa..8b1d0dad0764 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -970,6 +970,7 @@ int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
unsigned long timeout_ms);
void ufshcd_parse_dev_ref_clk_freq(struct ufs_hba *hba, struct clk *refclk);
void ufshcd_update_evt_hist(struct ufs_hba *hba, u32 id, u32 val);
+void ufshcd_hba_stop(struct ufs_hba *hba);
static inline void check_upiu_size(void)
{
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index 02f966e9358f..4ee233e5a6ff 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -145,6 +145,13 @@ static inline void* iscsi_next_hdr(struct iscsi_task *task)
return (void*)task->hdr + task->hdr_len;
}
+static inline bool iscsi_task_is_completed(struct iscsi_task *task)
+{
+ return task->state == ISCSI_TASK_COMPLETED ||
+ task->state == ISCSI_TASK_ABRT_TMF ||
+ task->state == ISCSI_TASK_ABRT_SESS_RECOV;
+}
+
/* Connection's states */
enum {
ISCSI_CONN_INITIAL_STAGE,
@@ -195,12 +202,6 @@ struct iscsi_conn {
unsigned long suspend_tx; /* suspend Tx */
unsigned long suspend_rx; /* suspend Rx */
- /* abort */
- wait_queue_head_t ehwait; /* used in eh_abort() */
- struct iscsi_tm tmhdr;
- struct timer_list tmf_timer;
- int tmf_state; /* see TMF_INITIAL, etc.*/
-
/* negotiated params */
unsigned max_recv_dlength; /* initiator_max_recv_dsl*/
unsigned max_xmit_dlength; /* target_max_recv_dsl */
@@ -270,6 +271,12 @@ struct iscsi_session {
* and recv lock.
*/
struct mutex eh_mutex;
+ /* abort */
+ wait_queue_head_t ehwait; /* used in eh_abort() */
+ struct iscsi_tm tmhdr;
+ struct timer_list tmf_timer;
+ int tmf_state; /* see TMF_INITIAL, etc.*/
+ struct iscsi_task *running_aborted_task;
/* iSCSI session-wide sequencing */
uint32_t cmdsn;
@@ -424,6 +431,7 @@ extern int iscsi_conn_start(struct iscsi_cls_conn *);
extern void iscsi_conn_stop(struct iscsi_cls_conn *, int);
extern int iscsi_conn_bind(struct iscsi_cls_session *, struct iscsi_cls_conn *,
int);
+extern void iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active);
extern void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err);
extern void iscsi_session_failure(struct iscsi_session *session,
enum iscsi_err err);
diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
index fc5a39839b4b..c5d7810fd792 100644
--- a/include/scsi/scsi_transport_iscsi.h
+++ b/include/scsi/scsi_transport_iscsi.h
@@ -82,6 +82,7 @@ struct iscsi_transport {
void (*destroy_session) (struct iscsi_cls_session *session);
struct iscsi_cls_conn *(*create_conn) (struct iscsi_cls_session *sess,
uint32_t cid);
+ void (*unbind_conn) (struct iscsi_cls_conn *conn, bool is_active);
int (*bind_conn) (struct iscsi_cls_session *session,
struct iscsi_cls_conn *cls_conn,
uint64_t transport_eph, int is_leading);
@@ -196,15 +197,23 @@ enum iscsi_connection_state {
ISCSI_CONN_BOUND,
};
+#define ISCSI_CLS_CONN_BIT_CLEANUP 1
+
struct iscsi_cls_conn {
struct list_head conn_list; /* item in connlist */
- struct list_head conn_list_err; /* item in connlist_err */
void *dd_data; /* LLD private data */
struct iscsi_transport *transport;
uint32_t cid; /* connection id */
+ /*
+ * This protects the conn startup and binding/unbinding of the ep to
+ * the conn. Unbinding includes ep_disconnect and stop_conn.
+ */
struct mutex ep_mutex;
struct iscsi_endpoint *ep;
+ unsigned long flags;
+ struct work_struct cleanup_work;
+
struct device dev; /* sysfs transport/container device */
enum iscsi_connection_state state;
};
@@ -434,6 +443,8 @@ extern void iscsi_remove_session(struct iscsi_cls_session *session);
extern void iscsi_free_session(struct iscsi_cls_session *session);
extern struct iscsi_cls_conn *iscsi_create_conn(struct iscsi_cls_session *sess,
int dd_size, uint32_t cid);
+extern void iscsi_put_conn(struct iscsi_cls_conn *conn);
+extern void iscsi_get_conn(struct iscsi_cls_conn *conn);
extern int iscsi_destroy_conn(struct iscsi_cls_conn *conn);
extern void iscsi_unblock_session(struct iscsi_cls_session *session);
extern void iscsi_block_session(struct iscsi_cls_session *session);
@@ -441,6 +452,7 @@ extern int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time);
extern struct iscsi_endpoint *iscsi_create_endpoint(int dd_size);
extern void iscsi_destroy_endpoint(struct iscsi_endpoint *ep);
extern struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle);
+extern void iscsi_put_endpoint(struct iscsi_endpoint *ep);
extern int iscsi_block_scsi_eh(struct scsi_cmnd *cmd);
extern struct iscsi_iface *iscsi_create_iface(struct Scsi_Host *shost,
struct iscsi_transport *t,