[PATCH V2 10/13] pm80xx : IOCTL functionality for SGPIO.

Deepak Ukey
2020-01-17T07:10:50Z

From: Deepak Ukey <Deepak.Ukey@microchip.com>

Added the IOCTL functionality for SGPIO through which management
utility can controls SGPIO LEDs on the enclosure of locally attached
drives only. It is used to read from/write into SGPIO registers and
sets one or more SGPIO registers.

Signed-off-by: Deepak Ukey <deepak.ukey@microchip.com>
Signed-off-by: Viswas G <Viswas.G@microchip.com>
Signed-off-by: Radha Ramachandran <radha@google.com>
Reported-by: kbuild test robot <lkp@intel.com>
---
drivers/scsi/pm8001/pm8001_ctl.c | 71 ++++++++++++++++
drivers/scsi/pm8001/pm8001_ctl.h | 72 ++++++++++++++++
drivers/scsi/pm8001/pm8001_hwi.c | 172 +++++++++++++++++++++++++++++++++++++-
drivers/scsi/pm8001/pm8001_hwi.h | 17 ++++
drivers/scsi/pm8001/pm8001_init.c | 3 +
drivers/scsi/pm8001/pm8001_sas.c | 37 ++++++++
drivers/scsi/pm8001/pm8001_sas.h | 20 +++++
drivers/scsi/pm8001/pm80xx_hwi.c | 6 ++
drivers/scsi/pm8001/pm80xx_hwi.h | 3 +
9 files changed, 400 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
index cadb5be394b6..d557b423edf6 100644
--- a/drivers/scsi/pm8001/pm8001_ctl.c
+++ b/drivers/scsi/pm8001/pm8001_ctl.c
@@ -1009,6 +1009,74 @@ static long pm8001_gpio_ioctl(struct pm8001_hba_info *pm8001_ha,
return ret;
}

+static long pm8001_sgpio_ioctl(struct pm8001_hba_info *pm8001_ha,
+ unsigned long arg)
+{
+ struct sgpio_buffer buffer;
+ struct read_write_req_resp *req = &buffer.sgpio_req;
+ struct sgpio_req payload;
+ struct sgpio_ioctl_resp *sgpio_resp;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ unsigned long timeout;
+ u32 ret = 0, i;
+
+ if (copy_from_user(&buffer, (struct sgpio_buffer *)arg,
+ sizeof(struct sgpio_buffer))) {
+ return ADPT_IOCTL_CALL_FAILED;
+ }
+ mutex_lock(&pm8001_ha->ioctl_mutex);
+ pm8001_ha->ioctl_completion = &completion;
+
+ payload.func_reg_index = cpu_to_le32((req->register_index << 24) |
+ (req->register_type << 16) | (req->function << 8) |
+ SMP_FRAME_REQ);
+ payload.count = req->register_count;
+
+ if (req->function == WRITE_SGPIO_REGISTER) {
+ if (req->register_count > MAX_SGPIO_REQ_PAYLOAD) {
+ ret = ADPT_IOCTL_CALL_FAILED;
+ goto exit;
+ }
+ for (i = 0; i < req->register_count; i++)
+ payload.value[i] = req->read_write_data[i];
+ }
+
+ ret = PM8001_CHIP_DISP->sgpio_req(pm8001_ha, &payload);
+ if (ret != 0) {
+ ret = ADPT_IOCTL_CALL_FAILED;
+ goto exit;
+ }
+
+ timeout = wait_for_completion_timeout(&completion,
+ msecs_to_jiffies(2000));
+ if (timeout == 0) {
+ ret = ADPT_IOCTL_CALL_TIMEOUT;
+ goto exit;
+ }
+
+ sgpio_resp = &pm8001_ha->sgpio_resp;
+ req->frame_type = sgpio_resp->func_result & 0xff;
+ req->function = (sgpio_resp->func_result >> 8) & 0xff;
+ req->function_result = (sgpio_resp->func_result >> 16) & 0xff;
+ if (req->function == READ_SGPIO_REGISTER) {
+ for (i = 0; i < req->register_count; i++)
+ req->read_write_data[i] = sgpio_resp->value[i];
+ }
+ ret = ADPT_IOCTL_CALL_SUCCESS;
+exit:
+ spin_lock_irq(&pm8001_ha->ioctl_lock);
+ pm8001_ha->ioctl_completion = NULL;
+ spin_unlock_irq(&pm8001_ha->ioctl_lock);
+ buffer.header.return_code = ret;
+ if (copy_to_user((void *)arg, (void *)&buffer,
+ sizeof(struct sgpio_buffer))) {
+ ret = ADPT_IOCTL_CALL_FAILED;
+ }
+ mutex_unlock(&pm8001_ha->ioctl_mutex);
+
+ return ret;
+}
+
static int pm8001_ioctl_get_phy_profile(struct pm8001_hba_info *pm8001_ha,
unsigned long arg)
{
@@ -1171,6 +1239,9 @@ static long pm8001_ioctl(struct file *file,
case ADPT_IOCTL_GPIO:
ret = pm8001_gpio_ioctl(pm8001_ha, arg);
break;
+ case ADPT_IOCTL_SGPIO:
+ ret = pm8001_sgpio_ioctl(pm8001_ha, arg);
+ break;
case ADPT_IOCTL_GET_PHY_PROFILE:
ret = pm8001_ioctl_get_phy_profile(pm8001_ha, arg);
return ret;
diff --git a/drivers/scsi/pm8001/pm8001_ctl.h b/drivers/scsi/pm8001/pm8001_ctl.h
index 5be43b2672d4..b1be0bc065d5 100644
--- a/drivers/scsi/pm8001/pm8001_ctl.h
+++ b/drivers/scsi/pm8001/pm8001_ctl.h
@@ -68,6 +68,39 @@

#define MAX_NUM_PHYS 16

+/************************************************************
+ * SGPIO Function and Register type
+ ************************************************************/
+#define READ_SGPIO_REGISTER 0x02
+#define WRITE_SGPIO_REGISTER 0x82
+
+#define SMP_FRAME_REQ 0x40
+#define SMP_FRAME_RESP 0x41
+
+#define SGPIO_CONFIG_REG 0x0
+#define SGPIO_DRIVE_BY_DRIVE_RECEIVE_REG 0x1
+#define SGPIO_GENERAL_PURPOSE_RECEIVE_REG 0x2
+#define SGPIO_DRIVE_BY_DRIVE_TRANSMIT_REG 0x3
+#define SGPIO_GENERAL_PURPOSE_TRANSMIT_REG 0x4
+
+/************************************************************
+ * SGPIO Function result
+ ************************************************************/
+#define SGPIO_COMMAND_SUCCESS 0x00
+#define SGPIO_CMD_ERROR_WRONG_FRAME_TYPE 0x01
+#define SGPIO_CMD_ERROR_WRONG_REG_TYPE 0x02
+#define SGPIO_CMD_ERROR_WRONG_REG_INDEX 0x03
+#define SGPIO_CMD_ERROR_WRONG_REG_COUNT 0x04
+#define SGPIO_CMD_ERROR_WRONG_FRAME_REG_TYPE 0x05
+#define SGPIO_CMD_ERROR_WRONG_FUNCTION 0x06
+#define SGPIO_CMD_ERROR_WRONG_FRAME_TYPE_REG_INDEX 0x19
+#define SGPIO_CMD_ERROR_WRONG_FRAME_TYPE_REG_CNT 0x81
+#define SGPIO_CMD_ERROR_WRONG_REG_TYPE_REG_INDEX 0x1A
+#define SGPIO_CMD_ERROR_WRONG_REG_TYPE_REG_COUNT 0x82
+#define SGPIO_CMD_ERROR_WRONG_REG_INDEX_REG_COUNT 0x83
+#define SGPIO_CMD_ERROR_WRONG_FRAME_REG_TYPE_REG_INDEX 0x1D
+#define SGPIO_CMD_ERROR_WRONG_ALL_HEADER_PARAMS 0x9D
+
struct ioctl_header {
u32 io_controller_num;
u32 length;
@@ -104,6 +137,39 @@ struct pm8001_gpio {
u32 event_falling_edge;
};

+#define MAX_SGPIO_REQ_PAYLOAD 12
+#define MAX_SGPIO_RESP_PAYLOAD 13
+
+struct read_write_req_resp {
+ u8 frame_type; /* =0x40 */
+ u8 function; /* 0x02 for read, 0x82 for write */
+ u8 register_type;
+ u8 register_index;
+ u8 register_count;
+ u8 function_result;
+ u32 read_write_data[MAX_SGPIO_RESP_PAYLOAD];
+};
+
+struct sgpio_cfg_0 {
+ u8 reserved;
+ u8 version:4;
+ u8 reserved1:4;
+ u8 gp_register_count:4;
+ u8 cfg_register_count:3;
+ u8 enable:1;
+ u8 supported_drive_cnt;
+};
+
+struct sgpio_cfg_1 {
+ u8 reserved;
+ u8 blink_gen_a:4;
+ u8 blink_gen_b:4;
+ u8 max_act_on:4;
+ u8 forced_act_off:4;
+ u8 stretch_act_on:4;
+ u8 stretch_act_off:4;
+};
+
struct ioctl_info_buffer {
struct ioctl_header header;
struct ioctl_drv_info information;
@@ -114,6 +180,11 @@ struct gpio_buffer {
struct pm8001_gpio gpio_payload;
};

+struct sgpio_buffer {
+ struct ioctl_header header;
+ struct read_write_req_resp sgpio_req;
+};
+
struct phy_profile {
char phy_id;
unsigned int phys:4;
@@ -143,6 +214,7 @@ struct phy_prof_resp {

#define ADPT_IOCTL_INFO _IOR(ADPT_MAGIC_NUMBER, 0, struct ioctl_info_buffer *)
#define ADPT_IOCTL_GPIO _IOWR(ADPT_MAGIC_NUMBER, 1, struct gpio_buffer *)
+#define ADPT_IOCTL_SGPIO _IOWR(ADPT_MAGIC_NUMBER, 2, struct sgpio_buffer *)
#define ADPT_IOCTL_GET_PHY_PROFILE _IOWR(ADPT_MAGIC_NUMBER, 8, \
struct phy_profile*)
#define ADPT_IOCTL_GET_PHY_ERR_CNT _IOWR(ADPT_MAGIC_NUMBER, 9, \
diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c
index 2328ff1349ac..f9395d9fd530 100644
--- a/drivers/scsi/pm8001/pm8001_hwi.c
+++ b/drivers/scsi/pm8001/pm8001_hwi.c
@@ -3666,6 +3666,49 @@ int pm8001_mpi_dereg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
return 0;
}

+/**
+ *pm8001_sgpio_resp - pm8001 SGPIO response
+ *@pm8001_ha: HBA controller information
+ *@piomb: SGPIO payload
+ *Handles SGPIO response from HBA.
+ */
+
+int pm8001_sgpio_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ u32 func_result;
+ u32 tag, i;
+ u32 value;
+ struct sgpio_ioctl_resp *sgpio_resp;
+ struct sgpio_reg_resp *registerRespPayload =
+ (struct sgpio_reg_resp *)(piomb + 4);
+
+ tag = le32_to_cpu(registerRespPayload->tag);
+ func_result = le32_to_cpu(registerRespPayload->func_result);
+ value = le32_to_cpu(registerRespPayload->value[0]);
+
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "SGPIO func result = 0x%x tag %x value %x\n",
+ func_result, tag, value));
+
+ spin_lock(&pm8001_ha->ioctl_lock);
+ if (pm8001_ha->ioctl_completion != NULL) {
+ sgpio_resp = &pm8001_ha->sgpio_resp;
+ sgpio_resp->func_result = func_result;
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("SGPIO response value hexdump\n"));
+ for (i = 0; i < MAX_SGPIO_RESP_PAYLOAD; i++) {
+ sgpio_resp->value[i] =
+ le32_to_cpu(registerRespPayload->value[i]);
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "value[%d] = %08x\n", i, sgpio_resp->value[i]));
+ }
+ complete(pm8001_ha->ioctl_completion);
+ }
+ spin_unlock(&pm8001_ha->ioctl_lock);
+ pm8001_tag_free(pm8001_ha, tag);
+ return 0;
+}
+
/**
* fw_flash_update_resp - Response from FW for flash update command.
* @pm8001_ha: our hba card information
@@ -4035,7 +4078,7 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void* piomb)
static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
__le32 pHeader = *(__le32 *)piomb;
- u8 opc = (u8)((le32_to_cpu(pHeader)) & 0xFFF);
+ u16 opc = (u8)((le32_to_cpu(pHeader)) & 0xFFF);

PM8001_MSG_DBG(pm8001_ha, pm8001_printk("process_one_iomb:"));

@@ -4190,6 +4233,11 @@ static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
PM8001_MSG_DBG(pm8001_ha,
pm8001_printk("OPC_OUB_SAS_RE_INITIALIZE\n"));
break;
+ case OPC_OUB_SGPIO_RESP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SGPIO RESPONSE\n"));
+ pm8001_sgpio_resp(pm8001_ha, piomb);
+ break;
default:
PM8001_DEVIO_DBG(pm8001_ha,
pm8001_printk("Unknown outbound Queue IOMB OPC = %x\n",
@@ -5136,6 +5184,88 @@ pm8001_chip_set_dev_state_req(struct pm8001_hba_info *pm8001_ha,

}

+/**
+ *pm8001_setup_sgpio - Setup SGPIO configuration for SPC/SPCv controllers
+ *pm8001_ha - controller information
+ */
+int pm8001_setup_sgpio(struct pm8001_hba_info *pm8001_ha)
+{
+ struct sgpio_req payload;
+ struct sgpio_cfg_0 *cfg_0;
+ struct sgpio_cfg_1 *cfg_1;
+ int rc, index = 0, i;
+ u32 value = 0;
+ DECLARE_COMPLETION_ONSTACK(completion);
+
+ pm8001_ha->ioctl_completion = &completion;
+ payload.func_reg_index = ((index << 24) | (SGPIO_CONFIG_REG << 16)
+ | (WRITE_SGPIO_REGISTER << 8) | SMP_FRAME_REQ);
+ payload.count = 2;
+
+ cfg_0 = (struct sgpio_cfg_0 *)(&value);
+ cfg_0->enable = 0x1;
+ payload.value[0] = value;
+
+ /*Initialize GPIO CFG 1 register to default as per SFF-8485 spec*/
+ cfg_1 = (struct sgpio_cfg_1 *)(&value);
+ cfg_1->blink_gen_a = 0;
+ cfg_1->blink_gen_b = 0;
+ cfg_1->max_act_on = 0x2;
+ cfg_1->forced_act_off = 0x1;
+ cfg_1->stretch_act_on = 0;
+ cfg_1->stretch_act_off = 0;
+
+ payload.value[1] = value;
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Setting up sgpio. index %x count %x\n",
+ payload.func_reg_index, payload.count));
+
+ mutex_lock(&pm8001_ha->ioctl_mutex);
+ pm8001_ha->ioctl_completion = &completion;
+ rc = PM8001_CHIP_DISP->sgpio_req(pm8001_ha, &payload);
+ if (rc) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("failed sgpio_req:%d\n", rc));
+ goto exit;
+ }
+ rc = wait_for_completion_timeout(&completion, msecs_to_jiffies(2000));
+ if (rc == 0) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("failed sgpio_req timeout\n"));
+ rc = ADPT_IOCTL_CALL_TIMEOUT;
+ goto exit;
+ }
+ payload.func_reg_index = ((index << 24) |
+ (SGPIO_DRIVE_BY_DRIVE_TRANSMIT_REG << 16) |
+ (WRITE_SGPIO_REGISTER << 8) | SMP_FRAME_REQ);
+ payload.count = pm8001_ha->chip->n_phy/4;
+ value = 0xA0A0A0A0; //Activity=0x5, Locate=0, Error=0
+ for (i = 0; i < payload.count; i++)
+ payload.value[i] = value;
+ reinit_completion(&completion);
+ pm8001_ha->ioctl_completion = &completion;
+ rc = PM8001_CHIP_DISP->sgpio_req(pm8001_ha, &payload);
+ if (rc) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("failed sgpio_req:%d\n", rc));
+ goto exit;
+ }
+ rc = wait_for_completion_timeout(&completion, msecs_to_jiffies(2000));
+ if (rc == 0) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("failed sgpio_req timeout\n"));
+ rc = ADPT_IOCTL_CALL_TIMEOUT;
+ goto exit;
+ }
+
+exit:
+ spin_lock(&pm8001_ha->ioctl_lock);
+ pm8001_ha->ioctl_completion = NULL;
+ spin_unlock(&pm8001_ha->ioctl_lock);
+ mutex_unlock(&pm8001_ha->ioctl_mutex);
+ return rc;
+}
+
static int
pm8001_chip_sas_re_initialization(struct pm8001_hba_info *pm8001_ha)
{
@@ -5164,6 +5294,45 @@ pm8001_chip_sas_re_initialization(struct pm8001_hba_info *pm8001_ha)

}

+/**
+ * pm8001_chip_sgpio_req - support for SGPIO operation
+ * @pm8001_ha: our hba card information.
+ * @ioctl_payload: the payload for the SGPIO operation
+ */
+int pm8001_chip_sgpio_req(struct pm8001_hba_info *pm8001_ha,
+ struct sgpio_req *sgpio_payload)
+{
+ struct sgpio_reg_req payload;
+ struct inbound_queue_table *circularQ;
+ int rc, i;
+ u32 tag;
+ u32 opc = OPC_INB_SGPIO_REG;
+
+ memset(&payload, 0, sizeof(struct sgpio_reg_req));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return -1;
+
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ payload.tag = cpu_to_le32(tag);
+ payload.func_reg_index = cpu_to_le32(sgpio_payload->func_reg_index);
+ payload.count = cpu_to_le32(sgpio_payload->count);
+
+ for (i = 0; i < sgpio_payload->count; i++)
+ payload.value[i] = cpu_to_le32(sgpio_payload->value[i]);
+
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("sgpio operation. tag %x index %x count %x\n",
+ tag, sgpio_payload->func_reg_index, sgpio_payload->count));
+
+ rc = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload,
+ sizeof(payload), 0);
+ if (rc != 0)
+ pm8001_tag_free(pm8001_ha, tag);
+
+ return rc;
+}
+
const struct pm8001_dispatch pm8001_8001_dispatch = {
.name = "pmc8001",
.chip_init = pm8001_chip_init,
@@ -5191,4 +5360,5 @@ const struct pm8001_dispatch pm8001_8001_dispatch = {
.fw_flash_update_req = pm8001_chip_fw_flash_update_req,
.set_dev_state_req = pm8001_chip_set_dev_state_req,
.sas_re_init_req = pm8001_chip_sas_re_initialization,
+ .sgpio_req = pm8001_chip_sgpio_req,
};
diff --git a/drivers/scsi/pm8001/pm8001_hwi.h b/drivers/scsi/pm8001/pm8001_hwi.h
index 6d91e2446542..aad2322467d2 100644
--- a/drivers/scsi/pm8001/pm8001_hwi.h
+++ b/drivers/scsi/pm8001/pm8001_hwi.h
@@ -82,6 +82,7 @@
#define OPC_INB_GET_DEVICE_STATE 43 /* 0x02B */
#define OPC_INB_SET_DEV_INFO 44 /* 0x02C */
#define OPC_INB_SAS_RE_INITIALIZE 45 /* 0x02D */
+#define OPC_INB_SGPIO_REG 46 /* 0x02E */

/* for Response Opcode of IOMB */
#define OPC_OUB_ECHO 1 /* 0x001 */
@@ -120,6 +121,7 @@
#define OPC_OUB_GET_DEVICE_STATE 39 /* 0x027 */
#define OPC_OUB_SET_DEV_INFO 40 /* 0x028 */
#define OPC_OUB_SAS_RE_INITIALIZE 41 /* 0x029 */
+#define OPC_OUB_SGPIO_RESP 2094 /* 0x82E */

/* for phy start*/
#define SPINHOLD_DISABLE (0x00 << 14)
@@ -697,6 +699,18 @@ struct set_dev_state_resp {
u32 reserved[11];
} __attribute__((packed, aligned(4)));

+struct sgpio_reg_req {
+ __le32 tag;
+ __le32 func_reg_index;
+ __le32 count;
+ __le32 value[12];
+} __packed __aligned(4);
+
+struct sgpio_reg_resp {
+ __le32 tag;
+ __le32 func_result;
+ __le32 value[13];
+} __packed __aligned(4);

#define NDS_BITS 0x0F
#define PDS_BITS 0xF0
@@ -922,6 +936,9 @@ struct set_dev_state_resp {
#define MAIN_HDA_FLAGS_OFFSET 0x84/* DWORD 0x21 */
#define MAIN_ANALOG_SETUP_OFFSET 0x88/* DWORD 0x22 */

+/*FATAL ERROR INTERRUPT bit definition*/
+#define MAIN_CFG_SGPIO_ENABLE (0x1 << 2)
+
/* Gereral Status Table offset - byte offset */
#define GST_GSTLEN_MPIS_OFFSET 0x00
#define GST_IQ_FREEZE_STATE0_OFFSET 0x04
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
index 6e2512aa5f6e..c8414f1b9652 100644
--- a/drivers/scsi/pm8001/pm8001_init.c
+++ b/drivers/scsi/pm8001/pm8001_init.c
@@ -122,6 +122,7 @@ static struct sas_domain_function_template pm8001_transport_ops = {
.lldd_I_T_nexus_reset = pm8001_I_T_nexus_reset,
.lldd_lu_reset = pm8001_lu_reset,
.lldd_query_task = pm8001_query_task,
+ .lldd_write_gpio = pm8001_write_sgpio,
};

/**
@@ -1110,6 +1111,8 @@ static int pm8001_pci_probe(struct pci_dev *pdev,
if (pm8001_configure_phy_settings(pm8001_ha))
goto err_out_shost;

+ pm8001_setup_sgpio(pm8001_ha);
+
pm8001_post_sas_ha_init(shost, chip);
rc = sas_register_ha(SHOST_TO_SAS_HA(shost));
if (rc) {
diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c
index b7cbc312843e..1d03c62d8c99 100644
--- a/drivers/scsi/pm8001/pm8001_sas.c
+++ b/drivers/scsi/pm8001/pm8001_sas.c
@@ -1361,3 +1361,40 @@ int pm8001_clear_task_set(struct domain_device *dev, u8 *lun)
return pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
}

+int pm8001_write_sgpio(struct sas_ha_struct *sas_ha, u8 reg_type,
+ u8 reg_index, u8 reg_count, u8 *write_data)
+{
+ struct pm8001_hba_info *pm8001_ha = sas_ha->lldd_ha;
+ struct sgpio_req payload;
+ u32 ret = 0, i, j, value;
+
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "reg_type=%x, reg_index:%x, reg_count:%x\n",
+ reg_type, reg_index, reg_count));
+
+ mutex_lock(&pm8001_ha->ioctl_mutex);
+
+ payload.func_reg_index = cpu_to_le32((reg_index << 24) |
+ (reg_type << 16) | (WRITE_SGPIO_REGISTER << 8) |
+ SMP_FRAME_REQ);
+
+ payload.count = reg_count;
+
+ for (i = 0; i < reg_count; i++) {
+ value = 0;
+ for (j = 0; j < 4; j++) {
+ value |= (u32)(*write_data) << (24-(j*8));
+ write_data++;
+ }
+ payload.value[i] = value;
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "payload value: %x\n", payload.value[i]));
+ }
+
+ ret = PM8001_CHIP_DISP->sgpio_req(pm8001_ha, &payload);
+ if (ret != 0)
+ ret = -1;
+
+ mutex_unlock(&pm8001_ha->ioctl_mutex);
+ return ret;
+}
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h
index 7d9920376fb7..4c6c7d86f75e 100644
--- a/drivers/scsi/pm8001/pm8001_sas.h
+++ b/drivers/scsi/pm8001/pm8001_sas.h
@@ -144,6 +144,17 @@ struct gpio_ioctl_resp {
u32 gpio_evt_fall;
};

+struct sgpio_req {
+ u32 func_reg_index;
+ u32 count;
+ u32 value[MAX_SGPIO_REQ_PAYLOAD];
+};
+
+struct sgpio_ioctl_resp {
+ u32 func_result;
+ u32 value[MAX_SGPIO_RESP_PAYLOAD];
+};
+
/* define task management IU */
struct pm8001_tmf_task {
u8 tmf;
@@ -261,6 +272,8 @@ struct pm8001_dispatch {
int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha);
int (*gpio_req)(struct pm8001_hba_info *pm8001_ha,
struct pm8001_gpio *gpio_payload);
+ int (*sgpio_req)(struct pm8001_hba_info *pm8001_ha,
+ struct sgpio_req *sgpio_payload);
int (*get_phy_profile_req)(struct pm8001_hba_info *pm8001_ha,
int phy, int page);
};
@@ -583,6 +596,7 @@ struct pm8001_hba_info {
spinlock_t ioctl_lock;
struct mutex ioctl_mutex;
struct completion *ioctl_completion;
+ struct sgpio_ioctl_resp sgpio_resp;
struct phy_prof_resp phy_profile_resp;
u32 reset_in_progress;
};
@@ -696,6 +710,8 @@ int pm8001_lu_reset(struct domain_device *dev, u8 *lun);
int pm8001_I_T_nexus_reset(struct domain_device *dev);
int pm8001_I_T_nexus_event_handler(struct domain_device *dev);
int pm8001_query_task(struct sas_task *task);
+int pm8001_write_sgpio(struct sas_ha_struct *sas_ha, u8 reg_type,
+ u8 reg_index, u8 reg_count, u8 *write_data);
void pm8001_open_reject_retry(
struct pm8001_hba_info *pm8001_ha,
struct sas_task *task_to_close,
@@ -731,6 +747,8 @@ int pm8001_chip_abort_task(struct pm8001_hba_info *pm8001_ha,
struct pm8001_device *pm8001_dev,
u8 flag, u32 task_tag, u32 cmd_tag);
int pm8001_chip_dereg_dev_req(struct pm8001_hba_info *pm8001_ha, u32 device_id);
+int pm8001_chip_sgpio_req(struct pm8001_hba_info *pm8001_ha,
+ struct sgpio_req *sgpio_payload);
void pm8001_chip_make_sg(struct scatterlist *scatter, int nr, void *prd);
void pm8001_work_fn(struct work_struct *work);
int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha,
@@ -752,6 +770,7 @@ int pm8001_mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha,
void *piomb);
int pm8001_mpi_general_event(struct pm8001_hba_info *pm8001_ha , void *piomb);
int pm8001_mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb);
+int pm8001_sgpio_resp(struct pm8001_hba_info *pm8001_ha, void *piomb);
struct sas_task *pm8001_alloc_task(void);
void pm8001_task_done(struct sas_task *task);
void pm8001_free_task(struct sas_task *task);
@@ -759,6 +778,7 @@ void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag);
struct pm8001_device *pm8001_find_dev(struct pm8001_hba_info *pm8001_ha,
u32 device_id);
int pm80xx_set_thermal_config(struct pm8001_hba_info *pm8001_ha);
+int pm8001_setup_sgpio(struct pm8001_hba_info *pm8001_ha);

int pm8001_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue);
void pm8001_set_phy_profile(struct pm8001_hba_info *pm8001_ha,
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c
index 2cdcb1c64970..e4746fd9c09f 100644
--- a/drivers/scsi/pm8001/pm80xx_hwi.c
+++ b/drivers/scsi/pm8001/pm80xx_hwi.c
@@ -4100,6 +4100,11 @@ static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
"OPC_OUB_SSP_COALESCED_COMP_RESP opcode:%x\n", opc));
ssp_coalesced_comp_resp(pm8001_ha, piomb);
break;
+ case OPC_OUB_SGPIO_RESP:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk(
+ "OPC_OUB_SGPIO RESPONSE opcode: %x\n", opc));
+ pm8001_sgpio_resp(pm8001_ha, piomb);
+ break;
default:
PM8001_DEVIO_DBG(pm8001_ha, pm8001_printk(
"Unknown outbound Queue IOMB OPC = 0x%x\n", opc));
@@ -5170,5 +5175,6 @@ const struct pm8001_dispatch pm8001_80xx_dispatch = {
.fw_flash_update_req = pm8001_chip_fw_flash_update_req,
.set_dev_state_req = pm8001_chip_set_dev_state_req,
.gpio_req = pm80xx_chip_gpio_req,
+ .sgpio_req = pm8001_chip_sgpio_req,
.get_phy_profile_req = pm8001_chip_get_phy_profile,
};
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h b/drivers/scsi/pm8001/pm80xx_hwi.h
index fed193df93d6..2d7f67b1cd93 100644
--- a/drivers/scsi/pm8001/pm80xx_hwi.h
+++ b/drivers/scsi/pm8001/pm80xx_hwi.h
@@ -1509,6 +1509,9 @@ typedef struct SASProtocolTimerConfig SASProtocolTimerConfig_t;
#define MAIN_MPI_ILA_RELEASE_TYPE 0xA4 /* DWORD 0x29 */
#define MAIN_MPI_INACTIVE_FW_VERSION 0XB0 /* DWORD 0x2C */

+/*FATAL ERROR INTERRUPT bit definition*/
+#define MAIN_CFG_SGPIO_ENABLE (0x1 << 2)
+
/* Gereral Status Table offset - byte offset */
#define GST_GSTLEN_MPIS_OFFSET 0x00
#define GST_IQ_FREEZE_STATE0_OFFSET 0x04
--
2.16.3