diff options
Diffstat (limited to 'drivers/scsi/pm8001/pm8001_ctl.c')
| -rw-r--r-- | drivers/scsi/pm8001/pm8001_ctl.c | 774 |
1 files changed, 604 insertions, 170 deletions
diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c index d99f41c2ca13..cbfda8c04e95 100644 --- a/drivers/scsi/pm8001/pm8001_ctl.c +++ b/drivers/scsi/pm8001/pm8001_ctl.c @@ -41,12 +41,14 @@ #include <linux/slab.h> #include "pm8001_sas.h" #include "pm8001_ctl.h" +#include "pm8001_chips.h" /* scsi host attributes */ /** * pm8001_ctl_mpi_interface_rev_show - MPI interface revision number * @cdev: pointer to embedded class device + * @attr: device attribute (unused) * @buf: the buffer returned * * A sysfs 'read-only' shost attribute. @@ -59,10 +61,10 @@ static ssize_t pm8001_ctl_mpi_interface_rev_show(struct device *cdev, struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; if (pm8001_ha->chip_id == chip_8001) { - return snprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", pm8001_ha->main_cfg_tbl.pm8001_tbl.interface_rev); } else { - return snprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", pm8001_ha->main_cfg_tbl.pm80xx_tbl.interface_rev); } } @@ -70,8 +72,29 @@ static DEVICE_ATTR(interface_rev, S_IRUGO, pm8001_ctl_mpi_interface_rev_show, NULL); /** + * controller_fatal_error_show - check controller is under fatal err + * @cdev: pointer to embedded class device + * @attr: device attribute (unused) + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t controller_fatal_error_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return sysfs_emit(buf, "%d\n", + pm8001_ha->controller_fatal_error); +} +static DEVICE_ATTR_RO(controller_fatal_error); + +/** * pm8001_ctl_fw_version_show - firmware version * @cdev: pointer to embedded class device + * @attr: device attribute (unused) * @buf: the buffer returned * * A sysfs 'read-only' shost attribute. @@ -84,13 +107,13 @@ static ssize_t pm8001_ctl_fw_version_show(struct device *cdev, struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; if (pm8001_ha->chip_id == chip_8001) { - return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x.%02x\n", + return sysfs_emit(buf, "%02x.%02x.%02x.%02x\n", (u8)(pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev >> 24), (u8)(pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev >> 16), (u8)(pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev >> 8), (u8)(pm8001_ha->main_cfg_tbl.pm8001_tbl.firmware_rev)); } else { - return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x.%02x\n", + return sysfs_emit(buf, "%02x.%02x.%02x.%02x\n", (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.firmware_rev >> 24), (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.firmware_rev >> 16), (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.firmware_rev >> 8), @@ -98,9 +121,64 @@ static ssize_t pm8001_ctl_fw_version_show(struct device *cdev, } } static DEVICE_ATTR(fw_version, S_IRUGO, pm8001_ctl_fw_version_show, NULL); + +/** + * pm8001_ctl_ila_version_show - ila version + * @cdev: pointer to embedded class device + * @attr: device attribute (unused) + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_ila_version_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + if (pm8001_ha->chip_id != chip_8001) { + return sysfs_emit(buf, "%02x.%02x.%02x.%02x\n", + (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version >> 24), + (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version >> 16), + (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version >> 8), + (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.ila_version)); + } + return 0; +} +static DEVICE_ATTR(ila_version, 0444, pm8001_ctl_ila_version_show, NULL); + +/** + * pm8001_ctl_inactive_fw_version_show - Inactive firmware version number + * @cdev: pointer to embedded class device + * @attr: device attribute (unused) + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_inactive_fw_version_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + if (pm8001_ha->chip_id != chip_8001) { + return sysfs_emit(buf, "%02x.%02x.%02x.%02x\n", + (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version >> 24), + (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version >> 16), + (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version >> 8), + (u8)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.inc_fw_version)); + } + return 0; +} +static +DEVICE_ATTR(inc_fw_ver, 0444, pm8001_ctl_inactive_fw_version_show, NULL); + /** * pm8001_ctl_max_out_io_show - max outstanding io supported * @cdev: pointer to embedded class device + * @attr: device attribute (unused) * @buf: the buffer returned * * A sysfs 'read-only' shost attribute. @@ -113,10 +191,10 @@ static ssize_t pm8001_ctl_max_out_io_show(struct device *cdev, struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; if (pm8001_ha->chip_id == chip_8001) { - return snprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", pm8001_ha->main_cfg_tbl.pm8001_tbl.max_out_io); } else { - return snprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_out_io); } } @@ -124,6 +202,7 @@ static DEVICE_ATTR(max_out_io, S_IRUGO, pm8001_ctl_max_out_io_show, NULL); /** * pm8001_ctl_max_devices_show - max devices support * @cdev: pointer to embedded class device + * @attr: device attribute (unused) * @buf: the buffer returned * * A sysfs 'read-only' shost attribute. @@ -136,13 +215,11 @@ static ssize_t pm8001_ctl_max_devices_show(struct device *cdev, struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; if (pm8001_ha->chip_id == chip_8001) { - return snprintf(buf, PAGE_SIZE, "%04d\n", - (u16)(pm8001_ha->main_cfg_tbl.pm8001_tbl.max_sgl >> 16) - ); + return sysfs_emit(buf, "%04d\n", + (u16)(pm8001_ha->main_cfg_tbl.pm8001_tbl.max_sgl >> 16)); } else { - return snprintf(buf, PAGE_SIZE, "%04d\n", - (u16)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_sgl >> 16) - ); + return sysfs_emit(buf, "%04d\n", + (u16)(pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_sgl >> 16)); } } static DEVICE_ATTR(max_devices, S_IRUGO, pm8001_ctl_max_devices_show, NULL); @@ -150,6 +227,7 @@ static DEVICE_ATTR(max_devices, S_IRUGO, pm8001_ctl_max_devices_show, NULL); * pm8001_ctl_max_sg_list_show - max sg list supported iff not 0.0 for no * hardware limitation * @cdev: pointer to embedded class device + * @attr: device attribute (unused) * @buf: the buffer returned * * A sysfs 'read-only' shost attribute. @@ -162,13 +240,11 @@ static ssize_t pm8001_ctl_max_sg_list_show(struct device *cdev, struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; if (pm8001_ha->chip_id == chip_8001) { - return snprintf(buf, PAGE_SIZE, "%04d\n", - pm8001_ha->main_cfg_tbl.pm8001_tbl.max_sgl & 0x0000FFFF - ); + return sysfs_emit(buf, "%04d\n", + pm8001_ha->main_cfg_tbl.pm8001_tbl.max_sgl & 0x0000FFFF); } else { - return snprintf(buf, PAGE_SIZE, "%04d\n", - pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_sgl & 0x0000FFFF - ); + return sysfs_emit(buf, "%04d\n", + pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_sgl & 0x0000FFFF); } } static DEVICE_ATTR(max_sg_list, S_IRUGO, pm8001_ctl_max_sg_list_show, NULL); @@ -194,6 +270,7 @@ show_sas_spec_support_status(unsigned int mode, char *buf) /** * pm8001_ctl_sas_spec_support_show - sas spec supported * @cdev: pointer to embedded class device + * @attr: device attribute (unused) * @buf: the buffer returned * * A sysfs 'read-only' shost attribute. @@ -219,8 +296,9 @@ static DEVICE_ATTR(sas_spec_support, S_IRUGO, pm8001_ctl_sas_spec_support_show, NULL); /** - * pm8001_ctl_sas_address_show - sas address + * pm8001_ctl_host_sas_address_show - sas address * @cdev: pointer to embedded class device + * @attr: device attribute (unused) * @buf: the buffer returned * * This is the controller sas address @@ -233,7 +311,7 @@ static ssize_t pm8001_ctl_host_sas_address_show(struct device *cdev, struct Scsi_Host *shost = class_to_shost(cdev); struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; - return snprintf(buf, PAGE_SIZE, "0x%016llx\n", + return sysfs_emit(buf, "0x%016llx\n", be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr)); } static DEVICE_ATTR(host_sas_address, S_IRUGO, @@ -242,6 +320,7 @@ static DEVICE_ATTR(host_sas_address, S_IRUGO, /** * pm8001_ctl_logging_level_show - logging level * @cdev: pointer to embedded class device + * @attr: device attribute (unused) * @buf: the buffer returned * * A sysfs 'read/write' shost attribute. @@ -253,8 +332,9 @@ static ssize_t pm8001_ctl_logging_level_show(struct device *cdev, struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; - return snprintf(buf, PAGE_SIZE, "%08xh\n", pm8001_ha->logging_level); + return sysfs_emit(buf, "%08xh\n", pm8001_ha->logging_level); } + static ssize_t pm8001_ctl_logging_level_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { @@ -275,6 +355,7 @@ static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, /** * pm8001_ctl_aap_log_show - aap1 event log * @cdev: pointer to embedded class device + * @attr: device attribute (unused) * @buf: the buffer returned * * A sysfs 'read-only' shost attribute. @@ -285,32 +366,161 @@ static ssize_t pm8001_ctl_aap_log_show(struct device *cdev, struct Scsi_Host *shost = class_to_shost(cdev); struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + u8 *ptr = (u8 *)pm8001_ha->memoryMap.region[AAP1].virt_ptr; int i; -#define AAP1_MEMMAP(r, c) \ - (*(u32 *)((u8*)pm8001_ha->memoryMap.region[AAP1].virt_ptr + (r) * 32 \ - + (c))) char *str = buf; int max = 2; for (i = 0; i < max; i++) { str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x" "0x%08x 0x%08x\n", - AAP1_MEMMAP(i, 0), - AAP1_MEMMAP(i, 4), - AAP1_MEMMAP(i, 8), - AAP1_MEMMAP(i, 12), - AAP1_MEMMAP(i, 16), - AAP1_MEMMAP(i, 20), - AAP1_MEMMAP(i, 24), - AAP1_MEMMAP(i, 28)); + pm8001_ctl_aap1_memmap(ptr, i, 0), + pm8001_ctl_aap1_memmap(ptr, i, 4), + pm8001_ctl_aap1_memmap(ptr, i, 8), + pm8001_ctl_aap1_memmap(ptr, i, 12), + pm8001_ctl_aap1_memmap(ptr, i, 16), + pm8001_ctl_aap1_memmap(ptr, i, 20), + pm8001_ctl_aap1_memmap(ptr, i, 24), + pm8001_ctl_aap1_memmap(ptr, i, 28)); } return str - buf; } static DEVICE_ATTR(aap_log, S_IRUGO, pm8001_ctl_aap_log_show, NULL); /** - * pm8001_ctl_aap_log_show - IOP event log + * pm8001_ctl_ib_queue_log_show - Out bound Queue log + * @cdev:pointer to embedded class device + * @attr: device attribute (unused) + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_ib_queue_log_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + int offset; + char *str = buf; + int start = 0; + u32 ib_offset = pm8001_ha->ib_offset; + u32 queue_size = pm8001_ha->max_q_num * PM8001_MPI_QUEUE * 128; +#define IB_MEMMAP(c) \ + (*(u32 *)((u8 *)pm8001_ha-> \ + memoryMap.region[ib_offset].virt_ptr + \ + pm8001_ha->evtlog_ib_offset + (c))) + + for (offset = 0; offset < IB_OB_READ_TIMES; offset++) { + str += sprintf(str, "0x%08x\n", IB_MEMMAP(start)); + start = start + 4; + } + pm8001_ha->evtlog_ib_offset += SYSFS_OFFSET; + if (((pm8001_ha->evtlog_ib_offset) % queue_size) == 0) + pm8001_ha->evtlog_ib_offset = 0; + + return str - buf; +} + +static DEVICE_ATTR(ib_log, S_IRUGO, pm8001_ctl_ib_queue_log_show, NULL); +/** + * pm8001_ctl_ob_queue_log_show - Out bound Queue log + * @cdev:pointer to embedded class device + * @attr: device attribute (unused) + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ + +static ssize_t pm8001_ctl_ob_queue_log_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + int offset; + char *str = buf; + int start = 0; + u32 ob_offset = pm8001_ha->ob_offset; + u32 queue_size = pm8001_ha->max_q_num * PM8001_MPI_QUEUE * 128; +#define OB_MEMMAP(c) \ + (*(u32 *)((u8 *)pm8001_ha-> \ + memoryMap.region[ob_offset].virt_ptr + \ + pm8001_ha->evtlog_ob_offset + (c))) + + for (offset = 0; offset < IB_OB_READ_TIMES; offset++) { + str += sprintf(str, "0x%08x\n", OB_MEMMAP(start)); + start = start + 4; + } + pm8001_ha->evtlog_ob_offset += SYSFS_OFFSET; + if (((pm8001_ha->evtlog_ob_offset) % queue_size) == 0) + pm8001_ha->evtlog_ob_offset = 0; + + return str - buf; +} +static DEVICE_ATTR(ob_log, S_IRUGO, pm8001_ctl_ob_queue_log_show, NULL); +/** + * pm8001_ctl_bios_version_show - Bios version Display + * @cdev:pointer to embedded class device + * @attr: device attribute (unused) + * @buf:the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_bios_version_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + char *str = buf; + int bios_index; + DECLARE_COMPLETION_ONSTACK(completion); + struct pm8001_ioctl_payload payload; + + pm8001_ha->nvmd_completion = &completion; + payload.minor_function = 7; + payload.offset = 0; + payload.rd_length = 4096; + payload.func_specific = kzalloc(4096, GFP_KERNEL); + if (!payload.func_specific) + return -ENOMEM; + if (PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, &payload)) { + kfree(payload.func_specific); + return -ENOMEM; + } + wait_for_completion(&completion); + for (bios_index = BIOSOFFSET; bios_index < BIOS_OFFSET_LIMIT; + bios_index++) + str += sprintf(str, "%c", + *(payload.func_specific+bios_index)); + kfree(payload.func_specific); + return str - buf; +} +static DEVICE_ATTR(bios_version, S_IRUGO, pm8001_ctl_bios_version_show, NULL); +/** + * event_log_size_show - event log size * @cdev: pointer to embedded class device + * @attr: device attribute (unused) + * @buf: the buffer returned + * + * A sysfs read shost attribute. + */ +static ssize_t event_log_size_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return sysfs_emit(buf, "%d\n", + pm8001_ha->main_cfg_tbl.pm80xx_tbl.event_log_size); +} +static DEVICE_ATTR_RO(event_log_size); +/** + * pm8001_ctl_iop_log_show - IOP event log + * @cdev: pointer to embedded class device + * @attr: device attribute (unused) * @buf: the buffer returned * * A sysfs 'read-only' shost attribute. @@ -321,40 +531,126 @@ static ssize_t pm8001_ctl_iop_log_show(struct device *cdev, struct Scsi_Host *shost = class_to_shost(cdev); struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; -#define IOP_MEMMAP(r, c) \ - (*(u32 *)((u8*)pm8001_ha->memoryMap.region[IOP].virt_ptr + (r) * 32 \ - + (c))) - int i; char *str = buf; - int max = 2; - for (i = 0; i < max; i++) { - str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x" - "0x%08x 0x%08x\n", - IOP_MEMMAP(i, 0), - IOP_MEMMAP(i, 4), - IOP_MEMMAP(i, 8), - IOP_MEMMAP(i, 12), - IOP_MEMMAP(i, 16), - IOP_MEMMAP(i, 20), - IOP_MEMMAP(i, 24), - IOP_MEMMAP(i, 28)); + u32 read_size = + pm8001_ha->main_cfg_tbl.pm80xx_tbl.event_log_size / 1024; + u32 max_read_times = 32; + u32 max_count = (read_size * 1024) / (max_read_times * 4); + u32 *temp = (u32 *)pm8001_ha->memoryMap.region[IOP].virt_ptr; + + mutex_lock(&pm8001_ha->iop_log_lock); + + if ((pm8001_ha->iop_log_count % max_count) == 0) { + pm8001_ha->iop_log_start = 0; + pm8001_ha->iop_log_end = max_read_times; + pm8001_ha->iop_log_count = 0; + } else { + pm8001_ha->iop_log_start = pm8001_ha->iop_log_end; + pm8001_ha->iop_log_end = pm8001_ha->iop_log_end + max_read_times; } + for (; pm8001_ha->iop_log_start < pm8001_ha->iop_log_end; pm8001_ha->iop_log_start++) + str += sprintf(str, "%08x ", *(temp+pm8001_ha->iop_log_start)); + pm8001_ha->iop_log_count++; + mutex_unlock(&pm8001_ha->iop_log_lock); return str - buf; } static DEVICE_ATTR(iop_log, S_IRUGO, pm8001_ctl_iop_log_show, NULL); +/** + * pm8001_ctl_fatal_log_show - fatal error logging + * @cdev:pointer to embedded class device + * @attr: device attribute + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ + +static ssize_t pm8001_ctl_fatal_log_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + ssize_t count; + + count = pm80xx_get_fatal_dump(cdev, attr, buf); + return count; +} + +static DEVICE_ATTR(fatal_log, S_IRUGO, pm8001_ctl_fatal_log_show, NULL); + +/** + * non_fatal_log_show - non fatal error logging + * @cdev:pointer to embedded class device + * @attr: device attribute + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t non_fatal_log_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + u32 count; + + count = pm80xx_get_non_fatal_dump(cdev, attr, buf); + return count; +} +static DEVICE_ATTR_RO(non_fatal_log); + +static ssize_t non_fatal_count_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + + return sysfs_emit(buf, "%08x\n", + pm8001_ha->non_fatal_count); +} + +static ssize_t non_fatal_count_store(struct device *cdev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + int val = 0; + + if (kstrtoint(buf, 16, &val) != 0) + return -EINVAL; + + pm8001_ha->non_fatal_count = val; + return strlen(buf); +} +static DEVICE_ATTR_RW(non_fatal_count); + +/** + * pm8001_ctl_gsm_log_show - gsm dump collection + * @cdev:pointer to embedded class device + * @attr: device attribute (unused) + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t pm8001_ctl_gsm_log_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + ssize_t count; + + count = pm8001_get_gsm_dump(cdev, SYSFS_OFFSET, buf); + return count; +} + +static DEVICE_ATTR(gsm_log, S_IRUGO, pm8001_ctl_gsm_log_show, NULL); + #define FLASH_CMD_NONE 0x00 #define FLASH_CMD_UPDATE 0x01 #define FLASH_CMD_SET_NVMD 0x02 struct flash_command { - u8 command[8]; + u8 command[8] __nonstring; int code; }; -static struct flash_command flash_command_table[] = -{ +static const struct flash_command flash_command_table[] = { {"set_nvmd", FLASH_CMD_SET_NVMD}, {"update", FLASH_CMD_UPDATE}, {"", FLASH_CMD_NONE} /* Last entry should be NULL. */ @@ -365,8 +661,7 @@ struct error_fw { int err_code; }; -static struct error_fw flash_error_table[] = -{ +static const struct error_fw flash_error_table[] = { {"Failed to open fw image file", FAIL_OPEN_BIOS_FILE}, {"image header mismatch", FLASH_UPDATE_HDR_ERR}, {"image offset mismatch", FLASH_UPDATE_OFFSET_ERR}, @@ -386,27 +681,32 @@ static int pm8001_set_nvmd(struct pm8001_hba_info *pm8001_ha) { struct pm8001_ioctl_payload *payload; DECLARE_COMPLETION_ONSTACK(completion); - u8 *ioctlbuffer = NULL; - u32 length = 0; - u32 ret = 0; + u8 *ioctlbuffer; + int ret; + u32 length = 1024 * 5 + sizeof(*payload) - 1; + + if (pm8001_ha->fw_image->size > 4096) { + pm8001_ha->fw_status = FAIL_FILE_SIZE; + return -EFAULT; + } - length = 1024 * 5 + sizeof(*payload) - 1; ioctlbuffer = kzalloc(length, GFP_KERNEL); - if (!ioctlbuffer) + if (!ioctlbuffer) { + pm8001_ha->fw_status = FAIL_OUT_MEMORY; return -ENOMEM; - if ((pm8001_ha->fw_image->size <= 0) || - (pm8001_ha->fw_image->size > 4096)) { - ret = FAIL_FILE_SIZE; - goto out; } payload = (struct pm8001_ioctl_payload *)ioctlbuffer; memcpy((u8 *)&payload->func_specific, (u8 *)pm8001_ha->fw_image->data, pm8001_ha->fw_image->size); - payload->length = pm8001_ha->fw_image->size; + payload->wr_length = pm8001_ha->fw_image->size; payload->id = 0; payload->minor_function = 0x1; pm8001_ha->nvmd_completion = &completion; ret = PM8001_CHIP_DISP->set_nvmd_req(pm8001_ha, payload); + if (ret) { + pm8001_ha->fw_status = FAIL_OUT_MEMORY; + goto out; + } wait_for_completion(&completion); out: kfree(ioctlbuffer); @@ -417,38 +717,37 @@ static int pm8001_update_flash(struct pm8001_hba_info *pm8001_ha) { struct pm8001_ioctl_payload *payload; DECLARE_COMPLETION_ONSTACK(completion); - u8 *ioctlbuffer = NULL; - u32 length = 0; + u8 *ioctlbuffer; struct fw_control_info *fwControl; - u32 loopNumber, loopcount = 0; + __be32 partitionSizeTmp; + u32 partitionSize; + u32 loopNumber, loopcount; + struct pm8001_fw_image_header *image_hdr; u32 sizeRead = 0; - u32 partitionSize, partitionSizeTmp; u32 ret = 0; - u32 partitionNumber = 0; - struct pm8001_fw_image_header *image_hdr; + u32 length = 1024 * 16 + sizeof(*payload) - 1; + u32 fc_len; + u8 *read_buf; - length = 1024 * 16 + sizeof(*payload) - 1; + if (pm8001_ha->fw_image->size < 28) { + pm8001_ha->fw_status = FAIL_FILE_SIZE; + return -EFAULT; + } ioctlbuffer = kzalloc(length, GFP_KERNEL); - image_hdr = (struct pm8001_fw_image_header *)pm8001_ha->fw_image->data; - if (!ioctlbuffer) + if (!ioctlbuffer) { + pm8001_ha->fw_status = FAIL_OUT_MEMORY; return -ENOMEM; - if (pm8001_ha->fw_image->size < 28) { - ret = FAIL_FILE_SIZE; - goto out; } - + image_hdr = (struct pm8001_fw_image_header *)pm8001_ha->fw_image->data; while (sizeRead < pm8001_ha->fw_image->size) { partitionSizeTmp = - *(u32 *)((u8 *)&image_hdr->image_length + sizeRead); + *(__be32 *)((u8 *)&image_hdr->image_length + sizeRead); partitionSize = be32_to_cpu(partitionSizeTmp); - loopcount = (partitionSize + HEADER_LEN)/IOCTL_BUF_SIZE; - if (loopcount % IOCTL_BUF_SIZE) - loopcount++; - if (loopcount == 0) - loopcount++; + loopcount = DIV_ROUND_UP(partitionSize + HEADER_LEN, + IOCTL_BUF_SIZE); for (loopNumber = 0; loopNumber < loopcount; loopNumber++) { payload = (struct pm8001_ioctl_payload *)ioctlbuffer; - payload->length = 1024*16; + payload->wr_length = 1024*16; payload->id = 0; fwControl = (struct fw_control_info *)&payload->func_specific; @@ -457,38 +756,37 @@ static int pm8001_update_flash(struct pm8001_hba_info *pm8001_ha) fwControl->retcode = 0;/* OUT */ fwControl->offset = loopNumber * IOCTL_BUF_SIZE;/*OUT */ - /* for the last chunk of data in case file size is not even with - 4k, load only the rest*/ - if (((loopcount-loopNumber) == 1) && - ((partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE)) { - fwControl->len = - (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE; - memcpy((u8 *)fwControl->buffer, - (u8 *)pm8001_ha->fw_image->data + sizeRead, - (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE); - sizeRead += - (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE; - } else { - memcpy((u8 *)fwControl->buffer, - (u8 *)pm8001_ha->fw_image->data + sizeRead, - IOCTL_BUF_SIZE); - sizeRead += IOCTL_BUF_SIZE; - } + /* + * for the last chunk of data in case file size is + * not even with 4k, load only the rest + */ - pm8001_ha->nvmd_completion = &completion; - ret = PM8001_CHIP_DISP->fw_flash_update_req(pm8001_ha, payload); - wait_for_completion(&completion); - if (ret || (fwControl->retcode > FLASH_UPDATE_IN_PROGRESS)) { - ret = fwControl->retcode; - kfree(ioctlbuffer); - ioctlbuffer = NULL; - break; + read_buf = (u8 *)pm8001_ha->fw_image->data + sizeRead; + fc_len = (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE; + + if (loopcount - loopNumber == 1 && fc_len) { + fwControl->len = fc_len; + memcpy((u8 *)fwControl->buffer, read_buf, fc_len); + sizeRead += fc_len; + } else { + memcpy((u8 *)fwControl->buffer, read_buf, IOCTL_BUF_SIZE); + sizeRead += IOCTL_BUF_SIZE; + } + + pm8001_ha->nvmd_completion = &completion; + ret = PM8001_CHIP_DISP->fw_flash_update_req(pm8001_ha, payload); + if (ret) { + pm8001_ha->fw_status = FAIL_OUT_MEMORY; + goto out; + } + wait_for_completion(&completion); + if (fwControl->retcode > FLASH_UPDATE_IN_PROGRESS) { + pm8001_ha->fw_status = fwControl->retcode; + ret = -EFAULT; + goto out; + } } } - if (ret) - break; - partitionNumber++; -} out: kfree(ioctlbuffer); return ret; @@ -503,22 +801,29 @@ static ssize_t pm8001_store_update_fw(struct device *cdev, char *cmd_ptr, *filename_ptr; int res, i; int flash_command = FLASH_CMD_NONE; - int err = 0; + int ret; + if (!capable(CAP_SYS_ADMIN)) return -EACCES; - cmd_ptr = kzalloc(count*2, GFP_KERNEL); + /* this test protects us from running two flash processes at once, + * so we should start with this test */ + if (pm8001_ha->fw_status == FLASH_IN_PROGRESS) + return -EINPROGRESS; + pm8001_ha->fw_status = FLASH_IN_PROGRESS; + cmd_ptr = kcalloc(count, 2, GFP_KERNEL); if (!cmd_ptr) { - err = FAIL_OUT_MEMORY; - goto out; + pm8001_ha->fw_status = FAIL_OUT_MEMORY; + return -ENOMEM; } filename_ptr = cmd_ptr + count; res = sscanf(buf, "%s %s", cmd_ptr, filename_ptr); if (res != 2) { - err = FAIL_PARAMETERS; - goto out1; + pm8001_ha->fw_status = FAIL_PARAMETERS; + ret = -EINVAL; + goto out; } for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) { @@ -529,50 +834,37 @@ static ssize_t pm8001_store_update_fw(struct device *cdev, } } if (flash_command == FLASH_CMD_NONE) { - err = FAIL_PARAMETERS; - goto out1; + pm8001_ha->fw_status = FAIL_PARAMETERS; + ret = -EINVAL; + goto out; } - if (pm8001_ha->fw_status == FLASH_IN_PROGRESS) { - err = FLASH_IN_PROGRESS; - goto out1; - } - err = request_firmware(&pm8001_ha->fw_image, + ret = request_firmware(&pm8001_ha->fw_image, filename_ptr, pm8001_ha->dev); - if (err) { - PM8001_FAIL_DBG(pm8001_ha, - pm8001_printk("Failed to load firmware image file %s," - " error %d\n", filename_ptr, err)); - err = FAIL_OPEN_BIOS_FILE; - goto out1; + if (ret) { + pm8001_dbg(pm8001_ha, FAIL, + "Failed to load firmware image file %s, error %d\n", + filename_ptr, ret); + pm8001_ha->fw_status = FAIL_OPEN_BIOS_FILE; + goto out; } - switch (flash_command) { - case FLASH_CMD_UPDATE: - pm8001_ha->fw_status = FLASH_IN_PROGRESS; - err = pm8001_update_flash(pm8001_ha); - break; - case FLASH_CMD_SET_NVMD: - pm8001_ha->fw_status = FLASH_IN_PROGRESS; - err = pm8001_set_nvmd(pm8001_ha); - break; - default: - pm8001_ha->fw_status = FAIL_PARAMETERS; - err = FAIL_PARAMETERS; - break; - } + if (FLASH_CMD_UPDATE == flash_command) + ret = pm8001_update_flash(pm8001_ha); + else + ret = pm8001_set_nvmd(pm8001_ha); + release_firmware(pm8001_ha->fw_image); -out1: - kfree(cmd_ptr); out: - pm8001_ha->fw_status = err; + kfree(cmd_ptr); - if (!err) - return count; - else - return -err; + if (ret) + return ret; + + pm8001_ha->fw_status = FLASH_OK; + return count; } static ssize_t pm8001_show_update_fw(struct device *cdev, @@ -590,25 +882,167 @@ static ssize_t pm8001_show_update_fw(struct device *cdev, if (pm8001_ha->fw_status != FLASH_IN_PROGRESS) pm8001_ha->fw_status = FLASH_OK; - return snprintf(buf, PAGE_SIZE, "status=%x %s\n", - flash_error_table[i].err_code, - flash_error_table[i].reason); + return sysfs_emit(buf, "status=%x %s\n", + flash_error_table[i].err_code, + flash_error_table[i].reason); } - -static DEVICE_ATTR(update_fw, S_IRUGO|S_IWUGO, +static DEVICE_ATTR(update_fw, S_IRUGO|S_IWUSR|S_IWGRP, pm8001_show_update_fw, pm8001_store_update_fw); -struct device_attribute *pm8001_host_attrs[] = { - &dev_attr_interface_rev, - &dev_attr_fw_version, - &dev_attr_update_fw, - &dev_attr_aap_log, - &dev_attr_iop_log, - &dev_attr_max_out_io, - &dev_attr_max_devices, - &dev_attr_max_sg_list, - &dev_attr_sas_spec_support, - &dev_attr_logging_level, - &dev_attr_host_sas_address, + +static const char *const mpiStateText[] = { + "MPI is not initialized", + "MPI is successfully initialized", + "MPI termination is in progress", + "MPI initialization failed with error in [31:16]" +}; + +/** + * ctl_mpi_state_show - controller MPI state check + * @cdev: pointer to embedded class device + * @attr: device attribute (unused) + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t ctl_mpi_state_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + unsigned int mpidw0; + + mpidw0 = pm8001_mr32(pm8001_ha->general_stat_tbl_addr, 0); + return sysfs_emit(buf, "%s\n", mpiStateText[mpidw0 & 0x0003]); +} +static DEVICE_ATTR_RO(ctl_mpi_state); + +/** + * ctl_hmi_error_show - controller MPI initialization fails + * @cdev: pointer to embedded class device + * @attr: device attribute (unused) + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t ctl_hmi_error_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + unsigned int mpidw0; + + mpidw0 = pm8001_mr32(pm8001_ha->general_stat_tbl_addr, 0); + return sysfs_emit(buf, "0x%08x\n", (mpidw0 >> 16)); +} +static DEVICE_ATTR_RO(ctl_hmi_error); + +/** + * ctl_raae_count_show - controller raae count check + * @cdev: pointer to embedded class device + * @attr: device attribute (unused) + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t ctl_raae_count_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + unsigned int raaecnt; + + raaecnt = pm8001_mr32(pm8001_ha->general_stat_tbl_addr, 12); + return sysfs_emit(buf, "0x%08x\n", raaecnt); +} +static DEVICE_ATTR_RO(ctl_raae_count); + +/** + * ctl_iop0_count_show - controller iop0 count check + * @cdev: pointer to embedded class device + * @attr: device attribute (unused) + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t ctl_iop0_count_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + unsigned int iop0cnt; + + iop0cnt = pm8001_mr32(pm8001_ha->general_stat_tbl_addr, 16); + return sysfs_emit(buf, "0x%08x\n", iop0cnt); +} +static DEVICE_ATTR_RO(ctl_iop0_count); + +/** + * ctl_iop1_count_show - controller iop1 count check + * @cdev: pointer to embedded class device + * @attr: device attribute (unused) + * @buf: the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t ctl_iop1_count_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + unsigned int iop1cnt; + + iop1cnt = pm8001_mr32(pm8001_ha->general_stat_tbl_addr, 20); + return sysfs_emit(buf, "0x%08x\n", iop1cnt); + +} +static DEVICE_ATTR_RO(ctl_iop1_count); + +static struct attribute *pm8001_host_attrs[] = { + &dev_attr_interface_rev.attr, + &dev_attr_controller_fatal_error.attr, + &dev_attr_fw_version.attr, + &dev_attr_update_fw.attr, + &dev_attr_aap_log.attr, + &dev_attr_iop_log.attr, + &dev_attr_fatal_log.attr, + &dev_attr_non_fatal_log.attr, + &dev_attr_non_fatal_count.attr, + &dev_attr_gsm_log.attr, + &dev_attr_max_out_io.attr, + &dev_attr_max_devices.attr, + &dev_attr_max_sg_list.attr, + &dev_attr_sas_spec_support.attr, + &dev_attr_logging_level.attr, + &dev_attr_event_log_size.attr, + &dev_attr_host_sas_address.attr, + &dev_attr_bios_version.attr, + &dev_attr_ib_log.attr, + &dev_attr_ob_log.attr, + &dev_attr_ila_version.attr, + &dev_attr_inc_fw_ver.attr, + &dev_attr_ctl_mpi_state.attr, + &dev_attr_ctl_hmi_error.attr, + &dev_attr_ctl_raae_count.attr, + &dev_attr_ctl_iop0_count.attr, + &dev_attr_ctl_iop1_count.attr, NULL, }; +static const struct attribute_group pm8001_host_attr_group = { + .attrs = pm8001_host_attrs +}; + +const struct attribute_group *pm8001_host_groups[] = { + &pm8001_host_attr_group, + NULL +}; + +const struct attribute_group *pm8001_sdev_groups[] = { + &sas_ata_sdev_attr_group, + NULL +}; |
