From: Deepak Ukey <Deepak.Ukey@xxxxxxxxxxxxx> Added the IOCTL functionality for TWI device so that management utility can manage the TWI device attached to the controller's TWI. The TWI device includes (but not limited to) the SEEPROM device. When NVME field set to 0000b (TWI devices), this command also allows the host to specify: a. TWI device address b. TWI Bus number c. TWI device page size (1, 8, 16 or 32 bytes) d. TWI device address size (1 or 2 bytes) Also added the module parameter for TWI device address and page size in order to add support for different TWI devices. Signed-off-by: Deepak Ukey <deepak.ukey@xxxxxxxxxxxxx> Signed-off-by: Viswas G <Viswas.G@xxxxxxxxxxxxx> Signed-off-by: Radha Ramachandran <radha@xxxxxxxxxx> --- drivers/scsi/pm8001/pm8001_ctl.c | 133 ++++++++++++++++++++++++++++++++++++++ drivers/scsi/pm8001/pm8001_ctl.h | 23 +++++++ drivers/scsi/pm8001/pm8001_hwi.c | 113 ++++++++++++++++++++++++++------ drivers/scsi/pm8001/pm8001_hwi.h | 1 + drivers/scsi/pm8001/pm8001_init.c | 10 +++ drivers/scsi/pm8001/pm8001_sas.h | 13 ++++ drivers/scsi/pm8001/pm80xx_hwi.c | 41 ++++++++++++ drivers/scsi/pm8001/pm80xx_hwi.h | 1 + 8 files changed, 314 insertions(+), 21 deletions(-) diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c index 887a15ee69f6..ce9d1ee5e995 100644 --- a/drivers/scsi/pm8001/pm8001_ctl.c +++ b/drivers/scsi/pm8001/pm8001_ctl.c @@ -1122,6 +1122,126 @@ static long pm8001_sgpio_ioctl(struct pm8001_hba_info *pm8001_ha, return ret; } +static int icotl_twi_wr_request(int twi_wr, + struct pm8001_hba_info *pm8001_ha, unsigned long arg) +{ + int ret = 0; + unsigned char *buf; + int bus; + int addr; + bool addr_mode_7bit = true; + int rd_size = 0; + int wr_size = 0; + int offset = 0; + bool direct_addr = true; + struct twi_ioctl_payload *twi_ioctl; + + twi_ioctl = kmalloc(sizeof(struct twi_ioctl_payload), GFP_KERNEL); + if (!twi_ioctl) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Failed to allocate ioctl\n")); + return -ENOMEM; + } + ret = copy_from_user(twi_ioctl, (u8 *)arg, sizeof(*twi_ioctl)); + if (ret) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("Failed to get ioctl args\n")); + return -EIO; + } + + bus = twi_ioctl->bus; + addr = twi_ioctl->addr; + + switch (twi_wr) { + case TW_WRITE: + wr_size = twi_ioctl->wr_size; + /* first 1K read/write is not possible, + * from 2k we have customer specific seeprom + */ + offset = twi_ioctl->offset; + if (wr_size > 48) + direct_addr = false; + buf = kzalloc(wr_size, GFP_KERNEL); + + if (buf == NULL) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("No Memory for write\n")); + return -ENOMEM; + } + ret = copy_from_user(buf, (u8 *)twi_ioctl->buf, wr_size); + if (!ret) { + ret = pm80xx_twi_wr_request(pm8001_ha, bus, addr, + addr_mode_7bit, rd_size, wr_size, offset, + direct_addr, buf); + } else { + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk + ("Failed to get data from buffer\n")); + return -EIO; + } + kfree(buf); + break; + case TW_READ: + rd_size = twi_ioctl->rd_size; + offset = twi_ioctl->offset; + if (rd_size > 48) + direct_addr = false; + buf = kzalloc(rd_size, GFP_KERNEL); + if (buf == NULL) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("No Memory for read\n")); + return -ENOMEM; + } + ret = pm80xx_twi_wr_request(pm8001_ha, bus, addr, + addr_mode_7bit, rd_size, wr_size, offset, + direct_addr, buf); + if (!ret) { + ret = copy_to_user((u8 *)twi_ioctl->buf, buf, rd_size); + kfree(buf); + } else { + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk + ("pm80xx_twi_wr_request failed !!!\n")); + return -EIO; + } + break; + case TW_WRITE_READ: + rd_size = twi_ioctl->rd_size; + wr_size = twi_ioctl->wr_size; + offset = twi_ioctl->offset; + if (wr_size > 48 || rd_size > 48) + direct_addr = false; + buf = kzalloc(max_t(int, wr_size, rd_size), GFP_KERNEL); + if (buf == NULL) { + PM8001_FAIL_DBG(pm8001_ha, + pm8001_printk("No Memory for read\n")); + return -ENOMEM; + } + ret = copy_from_user(buf, (u8 *)twi_ioctl->buf, wr_size); + if (!ret) { + ret = pm80xx_twi_wr_request(pm8001_ha, bus, addr, + addr_mode_7bit, rd_size, wr_size, offset, + direct_addr, buf); + if (!ret) + ret = copy_to_user((u8 *)twi_ioctl->buf, + buf, rd_size); + else { + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk + ("Failed to copy data to read buffer\n")); + ret = -EIO; + } + } else { + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk + ("Failed to get data from write buffer\n")); + return -ENOMEM; + } + break; + default: + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk + ("Invalid twi operation\n")); + return -EINVAL; + } + return ret; +} + static int pm8001_ioctl_get_phy_profile(struct pm8001_hba_info *pm8001_ha, unsigned long arg) { @@ -1274,6 +1394,7 @@ static long pm8001_ioctl(struct file *file, u32 ret = -EACCES; struct pm8001_hba_info *pm8001_ha; struct ioctl_header header; + u8 twi_wr = 0; pm8001_ha = file->private_data; @@ -1293,6 +1414,18 @@ static long pm8001_ioctl(struct file *file, case ADPT_IOCTL_GET_PHY_ERR_CNT: ret = pm8001_ioctl_get_phy_err(pm8001_ha, arg); break; + case ADPT_IOCTL_TWI_WRITE: + twi_wr = 0x01; + ret = icotl_twi_wr_request(twi_wr, pm8001_ha, arg); + return ret; + case ADPT_IOCTL_TWI_READ: + twi_wr = 0x02; + ret = icotl_twi_wr_request(twi_wr, pm8001_ha, arg); + return ret; + case ADPT_IOCTL_TWI_WRITE_READ: + twi_wr = 0x03; + ret = icotl_twi_wr_request(twi_wr, pm8001_ha, arg); + return ret; default: ret = ADPT_IOCTL_CALL_INVALID_CODE; } diff --git a/drivers/scsi/pm8001/pm8001_ctl.h b/drivers/scsi/pm8001/pm8001_ctl.h index b1be0bc065d5..57bf597aa9c3 100644 --- a/drivers/scsi/pm8001/pm8001_ctl.h +++ b/drivers/scsi/pm8001/pm8001_ctl.h @@ -101,6 +101,26 @@ #define SGPIO_CMD_ERROR_WRONG_FRAME_REG_TYPE_REG_INDEX 0x1D #define SGPIO_CMD_ERROR_WRONG_ALL_HEADER_PARAMS 0x9D +#define TWI_SEEPROM_BUS_NUMBER 0 +#define TWI_SEEPROM_DEV_ADDR 0xa0 +#define TWI_SEEPROM_READ_SIZE 1024 +#define TWI_SEEPROM_WRITE_SIZE 1024 + +#define TW_IOCTL_STATUS_SUCCESS 0 +#define TW_IOCTL_STATUS_FAILURE 1 +#define TW_IOCTL_STATUS_WRONG_ADDR 0xA + +struct twi_ioctl_payload { + u32 bus; + u32 addr; + u32 rd_size; + u32 wr_size; + u32 offset; + u8 addr_mode; + u8 *buf; + u32 status; +}; + struct ioctl_header { u32 io_controller_num; u32 length; @@ -219,6 +239,9 @@ struct phy_prof_resp { struct phy_profile*) #define ADPT_IOCTL_GET_PHY_ERR_CNT _IOWR(ADPT_MAGIC_NUMBER, 9, \ struct phy_err*) +#define ADPT_IOCTL_TWI_READ _IOR('m', 10, char *) +#define ADPT_IOCTL_TWI_WRITE _IOW('m', 11, char *) +#define ADPT_IOCTL_TWI_WRITE_READ _IOWR('m', 12, char *) #define ADPT_MAGIC_NUMBER 'm' #endif /* PM8001_CTL_H_INCLUDED */ diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index 16dc7a92ad68..0736c4b8cf7b 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -3152,13 +3152,34 @@ void pm8001_mpi_set_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) u32 tag = le32_to_cpu(pPayload->tag); struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag]; u32 dlen_status = le32_to_cpu(pPayload->dlen_status); - complete(pm8001_ha->nvmd_completion); - PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Set nvm data complete!\n")); + u32 *nvmd_data_addr; + u32 ir_tds_bn_dps_das_nvm = + le32_to_cpu(pPayload->ir_tda_bn_dps_das_nvm); + int tw_wr = (ir_tds_bn_dps_das_nvm & TR_MASK) >> 29; + struct fw_control_ex *fw_control_context; + + fw_control_context = ccb->fw_control_context; if ((dlen_status & NVMD_STAT) != 0) { PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("Set nvm data error!\n")); + complete(pm8001_ha->nvmd_completion); return; } + + if ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == TWI_DEVICE) { + if (tw_wr == TW_READ || tw_wr == TW_WRITE_READ) { + if (ir_tds_bn_dps_das_nvm & IPMode) { + nvmd_data_addr = + pm8001_ha->memoryMap.region[NVMD].virt_ptr; + } else { + nvmd_data_addr = pPayload->nvm_data; + } + memcpy(fw_control_context->usrAddr, nvmd_data_addr, + fw_control_context->len); + } + } + complete(pm8001_ha->nvmd_completion); + PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Set nvm data complete!\n")); ccb->task = NULL; ccb->ccb_tag = 0xFFFFFFFF; pm8001_tag_free(pm8001_ha, tag); @@ -3223,11 +3244,11 @@ pm8001_mpi_get_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) memcpy(fw_control_context->usrAddr, pm8001_ha->memoryMap.region[NVMD].virt_ptr, fw_control_context->len); + complete(pm8001_ha->nvmd_completion); kfree(ccb->fw_control_context); ccb->task = NULL; ccb->ccb_tag = 0xFFFFFFFF; pm8001_tag_free(pm8001_ha, tag); - complete(pm8001_ha->nvmd_completion); } int pm8001_mpi_local_phy_ctl(struct pm8001_hba_info *pm8001_ha, void *piomb) @@ -4856,12 +4877,17 @@ int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha, switch (nvmd_type) { case TWI_DEVICE: { - u32 twi_addr, twi_page_size; - twi_addr = 0xa8; - twi_page_size = 2; + u32 twi_addr, twi_page_size, twi_addr_size, twi_busno; + + twi_addr = pm8001_ha->twi_address; + twi_page_size = pm8001_ha->twi_page_size; + twi_addr_size = 1; + twi_busno = 0; nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | twi_addr << 16 | - twi_page_size << 8 | TWI_DEVICE); + twi_busno << 12 | twi_page_size << 8 | + twi_addr_size << 4 | TWI_DEVICE); + nvmd_req.vpd_offset = cpu_to_le32(ioctl_payload->offset); nvmd_req.resp_len = cpu_to_le32(ioctl_payload->rd_length); nvmd_req.resp_addr_hi = cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi); @@ -4936,32 +4962,77 @@ int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha, if (!fw_control_context) return -ENOMEM; circularQ = &pm8001_ha->inbnd_q_tbl[0]; - memcpy(pm8001_ha->memoryMap.region[NVMD].virt_ptr, - &ioctl_payload->func_specific, - ioctl_payload->wr_length); + if (nvmd_type != TWI_DEVICE) { + memcpy(pm8001_ha->memoryMap.region[NVMD].virt_ptr, + &ioctl_payload->func_specific, + ioctl_payload->wr_length); + } memset(&nvmd_req, 0, sizeof(nvmd_req)); rc = pm8001_tag_alloc(pm8001_ha, &tag); if (rc) { kfree(fw_control_context); - return -EBUSY; + return rc; } ccb = &pm8001_ha->ccb_info[tag]; ccb->fw_control_context = fw_control_context; ccb->ccb_tag = tag; nvmd_req.tag = cpu_to_le32(tag); + switch (nvmd_type) { case TWI_DEVICE: { - u32 twi_addr, twi_page_size; - twi_addr = 0xa8; - twi_page_size = 2; + u32 twi_addr, twi_page_size, twi_addr_size, twi_busno; + u32 addr_mode; + u8 *nvmd_data_addr; + u8 tr = 0, d_len = 0; + u32 ipdl = 0;//indirect data payload len + u32 tr_dl = 0; //twi read data len + + addr_mode = (ioctl_payload->twi_direct_addr == true) ? + 0 : IPMode; + if (ioctl_payload->rd_length) { + tr_dl = ioctl_payload->rd_length; + fw_control_context->usrAddr = + (u8 *)ioctl_payload->func_specific; + fw_control_context->len = ioctl_payload->rd_length; + tr = TW_READ; + } + if (ioctl_payload->wr_length) { + if (addr_mode == 0) { + d_len = ioctl_payload->wr_length; + nvmd_data_addr = (u8 *)&(nvmd_req.reserved[0]); + } else { + ipdl = ioctl_payload->wr_length; + nvmd_data_addr = + pm8001_ha->memoryMap.region[NVMD].virt_ptr; + } + memcpy(nvmd_data_addr, ioctl_payload->func_specific, + ioctl_payload->wr_length); + tr = TW_WRITE; + } + if (ioctl_payload->wr_length && ioctl_payload->rd_length) + tr = TW_WRITE_READ; + twi_addr = ioctl_payload->twi_addr; + if (twi_addr == TWI_SEEPROM_DEV_ADDR) + tr = 0;//Reserved for SEEPROM access ,as by fw + twi_page_size = 0; + twi_addr_size = 1; + twi_busno = ioctl_payload->bus; nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98); - nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | twi_addr << 16 | - twi_page_size << 8 | TWI_DEVICE); - nvmd_req.resp_len = cpu_to_le32(ioctl_payload->wr_length); - nvmd_req.resp_addr_hi = - cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi); - nvmd_req.resp_addr_lo = - cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo); + nvmd_req.len_ir_vpdd = cpu_to_le32(addr_mode | (tr << 29) | + twi_addr << 16 | twi_busno << 12 | + twi_page_size << 8 | twi_addr_size << 4 | + TWI_DEVICE); + nvmd_req.vpd_offset = cpu_to_le32(d_len << 24 | + ioctl_payload->offset); + nvmd_req.resp_len = cpu_to_le32(ipdl); + + if (addr_mode == IPMode) { + nvmd_req.resp_addr_hi = cpu_to_le32 + (pm8001_ha->memoryMap.region[NVMD].phys_addr_hi); + nvmd_req.resp_addr_lo = cpu_to_le32 + (pm8001_ha->memoryMap.region[NVMD].phys_addr_lo); + } + nvmd_req.tr_dl = cpu_to_le32(tr_dl); break; } case C_SEEPROM: diff --git a/drivers/scsi/pm8001/pm8001_hwi.h b/drivers/scsi/pm8001/pm8001_hwi.h index aad2322467d2..98e9ea6a4d6e 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.h +++ b/drivers/scsi/pm8001/pm8001_hwi.h @@ -634,6 +634,7 @@ struct set_nvm_data_req { __le32 resp_addr_hi; __le32 resp_len; u32 reserved1; + __le32 tr_dl; } __attribute__((packed, aligned(4))); diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index 6e037638656d..61fb9f622a5d 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -55,6 +55,14 @@ MODULE_PARM_DESC(link_rate, "Enable link rate.\n" " 4: Link rate 6.0G\n" " 8: Link rate 12.0G\n"); +static ulong twi_address = 0xa0; +module_param(twi_address, ulong, 0644); +MODULE_PARM_DESC(twi_address, "set the address of twi device."); + +static ulong twi_page_size; +module_param(twi_page_size, ulong, 0644); +MODULE_PARM_DESC(twi_page_size, "set the page size of twi device."); + static struct scsi_transport_template *pm8001_stt; /** @@ -484,6 +492,8 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev, pm8001_ha->shost = shost; pm8001_ha->id = pm8001_id++; pm8001_ha->logging_level = logging_level; + pm8001_ha->twi_address = twi_address; + pm8001_ha->twi_page_size = twi_page_size; pm8001_ha->non_fatal_count = 0; if (link_rate >= 1 && link_rate <= 15) pm8001_ha->link_rate = (link_rate << 8); diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h index 13d7813b8d74..9074c3a14120 100644 --- a/drivers/scsi/pm8001/pm8001_sas.h +++ b/drivers/scsi/pm8001/pm8001_sas.h @@ -169,6 +169,9 @@ struct pm8001_ioctl_payload { u16 id; u32 wr_length; u32 rd_length; + bool twi_direct_addr; + u8 bus; + u8 twi_addr; u8 *func_specific; }; @@ -586,6 +589,8 @@ struct pm8001_hba_info { #endif u32 logging_level; u32 link_rate; + u32 twi_address; + u32 twi_page_size; u32 fw_status; u32 smp_exp_mode; bool controller_fatal_error; @@ -648,6 +653,11 @@ struct pm8001_fw_image_header { #define DS_IN_ERROR 0x04 #define DS_NON_OPERATIONAL 0x07 +#define TR_MASK 0x60000000 +#define TW_WRITE 0x01 +#define TW_READ 0x02 +#define TW_WRITE_READ 0x03 + /** * brief param structure for firmware flash update. */ @@ -794,6 +804,9 @@ ssize_t pm80xx_get_fatal_dump(struct device *cdev, ssize_t pm80xx_get_non_fatal_dump(struct device *cdev, struct device_attribute *attr, char *buf); ssize_t pm8001_get_gsm_dump(struct device *cdev, u32, char *buf); +int pm80xx_twi_wr_request(struct pm8001_hba_info *pm8001_ha, int bus, int addr, + bool addr_mode_7bit, int rd_size, int wr_size, + int offset, bool direct_addr, unsigned char *buf); /* ctl shared API */ extern struct device_attribute *pm8001_host_attrs[]; diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index a4e265292d17..767914688b8a 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -46,6 +46,47 @@ #define SMP_DIRECT 1 #define SMP_INDIRECT 2 +int pm80xx_twi_wr_request(struct pm8001_hba_info *pm8001_ha, int bus, int addr, + bool addr_mode_7bit, int rd_size, int wr_size, + int offset, bool direct_addr, unsigned char *buf) +{ + struct pm8001_ioctl_payload *payload; + u8 *ioctlbuffer = NULL; + int length, ret; + DECLARE_COMPLETION_ONSTACK(completion); + + if ((wr_size > 48 || rd_size > 48) && direct_addr == true) { + PM8001_FAIL_DBG(pm8001_ha, pm8001_printk + ("Direct addressing mode used if payload size < 48\n")); + return -EINVAL; + } + + length = sizeof(*payload); + ioctlbuffer = kzalloc(length, GFP_KERNEL); + if (!ioctlbuffer) + return -ENOMEM; + payload = (struct pm8001_ioctl_payload *)ioctlbuffer; + + pm8001_ha->nvmd_completion = &completion; + payload->minor_function = 0; + + payload->rd_length = rd_size; + payload->wr_length = wr_size; + payload->bus = bus; + payload->twi_addr = addr; + payload->twi_direct_addr = direct_addr; + payload->offset = offset; + + payload->func_specific = buf; + + ret = PM8001_CHIP_DISP->set_nvmd_req(pm8001_ha, payload); + + wait_for_completion(&completion); + + kfree(ioctlbuffer); + + return ret; +} int pm80xx_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shift_value) { diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h b/drivers/scsi/pm8001/pm80xx_hwi.h index 2d7f67b1cd93..ba49d0abd6f4 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.h +++ b/drivers/scsi/pm8001/pm80xx_hwi.h @@ -966,6 +966,7 @@ struct set_nvm_data_req { __le32 resp_addr_hi; __le32 resp_len; u32 reserved1[17]; + __le32 tr_dl; } __attribute__((packed, aligned(4))); /** -- 2.16.3