From: Viswas G <Viswas.G@xxxxxxxxxxxxx> Added the ioctl functionality to collect phy status and phy error. Signed-off-by: Deepak Ukey <deepak.ukey@xxxxxxxxxxxxx> Signed-off-by: Viswas G <Viswas.G@xxxxxxxxxxxxx> Signed-off-by: Vishakha Channapattan <vishakhavc@xxxxxxxxxx> Signed-off-by: Bhavesh Jashnani <bjashnani@xxxxxxxxxx> Signed-off-by: Radha Ramachandran <radha@xxxxxxxxxx> Signed-off-by: Akshat Jain <akshatzen@xxxxxxxxxx> Signed-off-by: Yu Zheng <yuuzheng@xxxxxxxxxx> --- drivers/scsi/pm8001/pm8001_ctl.c | 145 +++++++++++++++++++++++++++++++++++++++ drivers/scsi/pm8001/pm8001_ctl.h | 35 +++++++++- drivers/scsi/pm8001/pm8001_sas.h | 9 +++ drivers/scsi/pm8001/pm80xx_hwi.c | 82 ++++++++++++++++++++++ drivers/scsi/pm8001/pm80xx_hwi.h | 2 + 5 files changed, 272 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c index 704c0daa7937..6daae852d5ac 100644 --- a/drivers/scsi/pm8001/pm8001_ctl.c +++ b/drivers/scsi/pm8001/pm8001_ctl.c @@ -41,6 +41,8 @@ #include <linux/slab.h> #include "pm8001_sas.h" #include "pm8001_ctl.h" +#include "pm80xx_hwi.h" + int pm80xx_major = -1; /* scsi host attributes */ @@ -939,6 +941,143 @@ static long pm8001_info_ioctl(struct pm8001_hba_info *pm8001_ha, return ret; } +static int pm8001_ioctl_get_phy_profile(struct pm8001_hba_info *pm8001_ha, + unsigned long arg) +{ + struct phy_profile phy_prof[MAX_NUM_PHYS]; + int nphys; + DECLARE_COMPLETION_ONSTACK(completion); + unsigned long timeout = msecs_to_jiffies(2000); + u32 ret = 0, i; + int page_code = SAS_PHY_GENERAL_STATUS_PAGE; + + if (pm8001_ha->pdev->device == 0x8001 || + pm8001_ha->pdev->device == 0x8081) { + return ADPT_IOCTL_CALL_INVALID_DEVICE; + } + + if (copy_from_user(&phy_prof[0], (struct phy_profile *)arg, + sizeof(struct phy_profile))) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("copy_from_user failed\n")); + return ADPT_IOCTL_CALL_FAILED; + } + + mutex_lock(&pm8001_ha->ioctl_mutex); + nphys = phy_prof[0].phy_id; + if (nphys == -1) { + for (i = 0; i < pm8001_ha->chip->n_phy; i++) { + pm8001_ha->ioctl_completion = &completion; + ret = PM8001_CHIP_DISP->get_phy_profile_req(pm8001_ha, + i, page_code); + if (ret != 0) { + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk( + "Get phy profile request failed\n")); + ret = ADPT_IOCTL_CALL_FAILED; + goto exit; + } + timeout = wait_for_completion_timeout(&completion, + timeout); + if (timeout == 0) { + ret = ADPT_IOCTL_CALL_FAILED; + goto exit; + } + memcpy((void *)&phy_prof[i], + (void *)&pm8001_ha->phy_profile_resp, + sizeof(struct phy_profile)); + } + + if (copy_to_user((void *)arg, (void *)&phy_prof, + sizeof(struct phy_profile) * (i))) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("copy_to_user failed\n")); + ret = ADPT_IOCTL_CALL_FAILED; + } + } else { + pm8001_ha->ioctl_completion = &completion; + ret = PM8001_CHIP_DISP->get_phy_profile_req(pm8001_ha, + nphys, page_code); + if (ret != 0) { + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk( + "Get phy profile request failed\n")); + ret = ADPT_IOCTL_CALL_FAILED; + goto exit; + } + timeout = wait_for_completion_timeout(&completion, + timeout); + if (timeout == 0) { + ret = ADPT_IOCTL_CALL_FAILED; + goto exit; + } + + if (copy_to_user((void *)arg, + (void *)&pm8001_ha->phy_profile_resp, + sizeof(struct phy_profile))) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("copy_to_user failed\n")); + ret = ADPT_IOCTL_CALL_FAILED; + } + } +exit: + spin_lock_irq(&pm8001_ha->ioctl_lock); + pm8001_ha->ioctl_completion = NULL; + spin_unlock_irq(&pm8001_ha->ioctl_lock); + mutex_unlock(&pm8001_ha->ioctl_mutex); + return ret; +} + +static int pm8001_ioctl_get_phy_err(struct pm8001_hba_info *pm8001_ha, + unsigned long arg) +{ + struct phy_errcnt phy_err[MAX_NUM_PHYS]; + DECLARE_COMPLETION_ONSTACK(completion); + unsigned long timeout = msecs_to_jiffies(2000); + u32 ret = 0, i; + int page_code = SAS_PHY_ERR_COUNTERS_PAGE; + /*6H card does not support phyerr*/ + if (pm8001_ha->pdev->device == 0x8001 || + pm8001_ha->pdev->device == 0x8081) { + return ADPT_IOCTL_CALL_INVALID_DEVICE; + } + + mutex_lock(&pm8001_ha->ioctl_mutex); + + for (i = 0; i < pm8001_ha->chip->n_phy; i++) { + pm8001_ha->ioctl_completion = &completion; + ret = PM8001_CHIP_DISP->get_phy_profile_req(pm8001_ha, + i, page_code); + if (ret != 0) { + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk( + "Get phy profile request failed\n")); + ret = ADPT_IOCTL_CALL_FAILED; + goto exit; + } + timeout = wait_for_completion_timeout(&completion, + timeout); + if (timeout == 0) { + ret = ADPT_IOCTL_CALL_FAILED; + goto exit; + } + memcpy((void *)&phy_err[i], + (void *)&pm8001_ha->phy_profile_resp, + sizeof(struct phy_errcnt)); + } + + if (copy_to_user((void *)arg, (void *)&phy_err, + sizeof(struct phy_errcnt) * (i))) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("copy_to_user failed\n")); + ret = ADPT_IOCTL_CALL_FAILED; + } + +exit: + spin_lock_irq(&pm8001_ha->ioctl_lock); + pm8001_ha->ioctl_completion = NULL; + spin_unlock_irq(&pm8001_ha->ioctl_lock); + mutex_unlock(&pm8001_ha->ioctl_mutex); + return ret; +} + /** * pm8001_ioctl - pm8001 configuration request * @inode: inode of device @@ -962,6 +1101,12 @@ static long pm8001_ioctl(struct file *file, case ADPT_IOCTL_INFO: ret = pm8001_info_ioctl(pm8001_ha, arg); break; + case ADPT_IOCTL_GET_PHY_PROFILE: + ret = pm8001_ioctl_get_phy_profile(pm8001_ha, arg); + return ret; + case ADPT_IOCTL_GET_PHY_ERR_CNT: + ret = pm8001_ioctl_get_phy_err(pm8001_ha, arg); + break; default: ret = ADPT_IOCTL_CALL_INVALID_CODE; } diff --git a/drivers/scsi/pm8001/pm8001_ctl.h b/drivers/scsi/pm8001/pm8001_ctl.h index f0f8b1deae27..686ad69f0e0c 100644 --- a/drivers/scsi/pm8001/pm8001_ctl.h +++ b/drivers/scsi/pm8001/pm8001_ctl.h @@ -63,6 +63,9 @@ #define ADPT_IOCTL_CALL_SUCCESS 0x00 #define ADPT_IOCTL_CALL_FAILED 0x01 #define ADPT_IOCTL_CALL_INVALID_CODE 0x03 +#define ADPT_IOCTL_CALL_INVALID_DEVICE 0x04 + +#define MAX_NUM_PHYS 16 struct ioctl_header { u32 io_controller_num; @@ -88,8 +91,38 @@ struct ioctl_info_buffer { struct ioctl_drv_info information; }; -#define ADPT_IOCTL_INFO _IOR(ADPT_MAGIC_NUMBER, 0, struct ioctl_info_buffer *) +struct phy_profile { + char phy_id; + unsigned int phys:4; + unsigned int nlr:4; + unsigned int plr:4; + unsigned int reserved1:12; + unsigned char port_id; + unsigned int prts:4; + unsigned int reserved2:20; +} __packed; + +struct phy_errcnt { + unsigned int InvalidDword; + unsigned int runningDisparityError; + unsigned int codeViolation; + unsigned int LossOfSyncDW; + unsigned int phyResetProblem; + unsigned int inboundCRCError; +}; +struct phy_prof_resp { + union { + struct phy_profile status; + struct phy_errcnt errcnt; + } phy; +}; + +#define ADPT_IOCTL_INFO _IOR(ADPT_MAGIC_NUMBER, 0, struct ioctl_info_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, \ + struct phy_err*) #define ADPT_MAGIC_NUMBER 'm' #endif /* PM8001_CTL_H_INCLUDED */ diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h index 479aac34d7cc..99920d53ac09 100644 --- a/drivers/scsi/pm8001/pm8001_sas.h +++ b/drivers/scsi/pm8001/pm8001_sas.h @@ -56,6 +56,7 @@ #include <scsi/sas_ata.h> #include <linux/atomic.h> #include "pm8001_defs.h" +#include "pm8001_ctl.h" #define DRV_NAME "pm80xx" #define DRV_VERSION "0.1.39" @@ -246,6 +247,8 @@ struct pm8001_dispatch { int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha, u32 state); int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha); + int (*get_phy_profile_req)(struct pm8001_hba_info *pm8001_ha, + int phy, int page); }; struct pm8001_chip_info { @@ -560,6 +563,12 @@ struct pm8001_hba_info { bool controller_fatal_error; const struct firmware *fw_image; struct isr_param irq_vector[PM8001_MAX_MSIX_VEC]; + spinlock_t ioctl_lock; + struct mutex ioctl_mutex; + struct completion *ioctl_completion; + struct timer_list ioctl_timer; + u32 ioctl_timer_expired; + struct phy_prof_resp phy_profile_resp; u32 reset_in_progress; }; diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index 37b82d7aa3d7..7f2b7b1d4110 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -3688,9 +3688,62 @@ static int mpi_get_controller_config_resp(struct pm8001_hba_info *pm8001_ha, static int mpi_get_phy_profile_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) { + u32 tag, page_code; + struct phy_profile *phy_profile, *phy_prof; + struct phy_errcnt *phy_err, *phy_err_cnt; + struct get_phy_profile_resp *pPayload = + (struct get_phy_profile_resp *)(piomb + 4); + u32 status = le32_to_cpu(pPayload->status); + + page_code = (u8)((pPayload->ppc_phyid & 0xFF00) >> 8); + PM8001_MSG_DBG(pm8001_ha, pm8001_printk(" pm80xx_addition_functionality\n")); + if (status) { + /* status is FAILED */ + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk( + "mpiGetPhyProfileReq failed with status 0x%08x\n", + status)); + } + + tag = le32_to_cpu(pPayload->tag); + + spin_lock(&pm8001_ha->ioctl_lock); + if (pm8001_ha->ioctl_completion != NULL) { + if (status) { + /* signal fail status */ + memset(&pm8001_ha->phy_profile_resp, 0xff, + sizeof(pm8001_ha->phy_profile_resp)); + } else if (page_code == SAS_PHY_ERR_COUNTERS_PAGE) { + phy_err = + (struct phy_errcnt *)&pm8001_ha->phy_profile_resp; + phy_err_cnt = + (struct phy_errcnt *)pPayload->ppc_specific_rsp; + phy_err->InvalidDword = + le32_to_cpu(phy_err_cnt->InvalidDword); + phy_err->runningDisparityError = + le32_to_cpu(phy_err_cnt->runningDisparityError); + phy_err->LossOfSyncDW = + le32_to_cpu(phy_err_cnt->LossOfSyncDW); + phy_err->phyResetProblem = + le32_to_cpu(phy_err_cnt->phyResetProblem); + } else if (page_code == SAS_PHY_GENERAL_STATUS_PAGE) { + phy_profile = + (struct phy_profile *)&pm8001_ha->phy_profile_resp; + phy_prof = + (struct phy_profile *)pPayload->ppc_specific_rsp; + phy_profile->phy_id = le32_to_cpu(phy_prof->phy_id); + phy_profile->phys = le32_to_cpu(phy_prof->phys); + phy_profile->plr = le32_to_cpu(phy_prof->plr); + phy_profile->nlr = le32_to_cpu(phy_prof->nlr); + phy_profile->port_id = le32_to_cpu(phy_prof->port_id); + phy_profile->prts = le32_to_cpu(phy_prof->prts); + } + complete(pm8001_ha->ioctl_completion); + } + spin_unlock(&pm8001_ha->ioctl_lock); + pm8001_tag_free(pm8001_ha, tag); return 0; } @@ -4883,6 +4936,34 @@ pm80xx_chip_isr(struct pm8001_hba_info *pm8001_ha, u8 vec) return IRQ_HANDLED; } +int pm8001_chip_get_phy_profile(struct pm8001_hba_info *pm8001_ha, + int phy_id, int page_code) +{ + + u32 tag; + struct get_phy_profile_req payload; + struct inbound_queue_table *circularQ; + int rc, ppc_phyid; + u32 opc = OPC_INB_GET_PHY_PROFILE; + + memset(&payload, 0, sizeof(payload)); + + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("Invalid tag\n")); + + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + + payload.tag = cpu_to_le32(tag); + ppc_phyid = (page_code & 0xFF) << 8 | (phy_id & 0xFF); + payload.ppc_phyid = cpu_to_le32(ppc_phyid); + + pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, + sizeof(payload), 0); + + return rc; +} + void mpi_set_phy_profile_req(struct pm8001_hba_info *pm8001_ha, u32 operation, u32 phyid, u32 length, u32 *buf) { @@ -4983,4 +5064,5 @@ const struct pm8001_dispatch pm8001_80xx_dispatch = { .set_nvmd_req = pm8001_chip_set_nvmd_req, .fw_flash_update_req = pm8001_chip_fw_flash_update_req, .set_dev_state_req = pm8001_chip_set_dev_state_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 701951a0f715..b5119c5479da 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.h +++ b/drivers/scsi/pm8001/pm80xx_hwi.h @@ -175,7 +175,9 @@ #define PHY_STOP_ERR_DEVICE_ATTACHED 0x1046 /* phy_profile */ +#define SAS_PHY_ERR_COUNTERS_PAGE 0x01 #define SAS_PHY_ANALOG_SETTINGS_PAGE 0x04 +#define SAS_PHY_GENERAL_STATUS_PAGE 0x05 #define PHY_DWORD_LENGTH 0xC /* Thermal related */ -- 2.16.3