@@ -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;
}
@@ -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 */
@@ -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:
@@ -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)));
@@ -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);
@@ -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[];
@@ -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)
{
@@ -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)));
/**