static ssize_t pm8001_ctl_sas_spec_support_show(struct device *cdev, struct device_attribute *attr, char *buf) { unsigned int mode; 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; mode = (pm8001_ha->main_cfg_tbl.ctrl_cap_flag & 0xfe000000)>>25; return show_sas_spec_support_status(mode, buf); }
static ssize_t pm8001_ctl_max_sg_list_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 snprintf(buf, PAGE_SIZE, "%04d\n", pm8001_ha->main_cfg_tbl.max_sgl & 0x0000FFFF); }
/** * pm8001_scan_start - we should enable all HBA phys by sending the phy_start * command to HBA. * @shost: the scsi host data. */ void pm8001_scan_start(struct Scsi_Host *shost) { int i; struct pm8001_hba_info *pm8001_ha; struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); pm8001_ha = sha->lldd_ha; PM8001_CHIP_DISP->sas_re_init_req(pm8001_ha); for (i = 0; i < pm8001_ha->chip->n_phy; ++i) PM8001_CHIP_DISP->phy_start_req(pm8001_ha, i); }
int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time) { struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); /* give the phy enabling interrupt event time to come in (1s * is empirically about all it takes) */ if (time < HZ) return 0; /* Wait for discovery to finish */ sas_drain_work(ha); return 1; }
/** * pm8001_scan_start - we should enable all HBA phys by sending the phy_start * command to HBA. * @shost: the scsi host data. */ void pm8001_scan_start(struct Scsi_Host *shost) { int i; struct pm8001_hba_info *pm8001_ha; struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); pm8001_ha = sha->lldd_ha; /* SAS_RE_INITIALIZATION not available in SPCv/ve */ if (pm8001_ha->chip_id == chip_8001) PM8001_CHIP_DISP->sas_re_init_req(pm8001_ha); for (i = 0; i < pm8001_ha->chip->n_phy; ++i) PM8001_CHIP_DISP->phy_start_req(pm8001_ha, i); }
static ssize_t pm8001_ctl_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; return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x.%02x\n", (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 24), (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 16), (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 8), (u8)(pm8001_ha->main_cfg_tbl.firmware_rev)); }
static ssize_t pm8001_ctl_logging_level_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 (sscanf(buf, "%x", &val) != 1) return -EINVAL; pm8001_ha->logging_level = val; return strlen(buf); }
/** * pm8001_ctl_mpi_interface_rev_show - MPI interface revision number * @cdev: pointer to embedded class device * @buf: the buffer returned * * A sysfs 'read-only' shost attribute. */ static ssize_t pm8001_ctl_mpi_interface_rev_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 snprintf(buf, PAGE_SIZE, "%d\n", pm8001_ha->main_cfg_tbl.pm8001_tbl.interface_rev); } else { return snprintf(buf, PAGE_SIZE, "%d\n", pm8001_ha->main_cfg_tbl.pm80xx_tbl.interface_rev); } }
static ssize_t pm8001_show_update_fw(struct device *cdev, struct device_attribute *attr, char *buf) { int i; 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; for (i = 0; flash_error_table[i].err_code != 0; i++) { if (flash_error_table[i].err_code == pm8001_ha->fw_status) break; } 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); }
int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, struct request *rsp) { u8 *req_data = NULL, *resp_data = NULL, *buf; struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); int error = -EINVAL; /* eight is the minimum size for request and response frames */ if (blk_rq_bytes(req) < 8 || blk_rq_bytes(rsp) < 8) goto out; if (bio_offset(req->bio) + blk_rq_bytes(req) > PAGE_SIZE || bio_offset(rsp->bio) + blk_rq_bytes(rsp) > PAGE_SIZE) { shost_printk(KERN_ERR, shost, "SMP request/response frame crosses page boundary"); goto out; } req_data = kzalloc(blk_rq_bytes(req), GFP_KERNEL); /* make sure frame can always be built ... we copy * back only the requested length */ resp_data = kzalloc(max(blk_rq_bytes(rsp), 128U), GFP_KERNEL); if (!req_data || !resp_data) { error = -ENOMEM; goto out; } local_irq_disable(); buf = kmap_atomic(bio_page(req->bio), KM_USER0) + bio_offset(req->bio); memcpy(req_data, buf, blk_rq_bytes(req)); kunmap_atomic(buf - bio_offset(req->bio), KM_USER0); local_irq_enable(); if (req_data[0] != SMP_REQUEST) goto out; /* always succeeds ... even if we can't process the request * the result is in the response frame */ error = 0; /* set up default don't know response */ resp_data[0] = SMP_RESPONSE; resp_data[1] = req_data[1]; resp_data[2] = SMP_RESP_FUNC_UNK; switch (req_data[1]) { case SMP_REPORT_GENERAL: req->resid_len -= 8; rsp->resid_len -= 32; resp_data[2] = SMP_RESP_FUNC_ACC; resp_data[9] = sas_ha->num_phys; break; case SMP_REPORT_MANUF_INFO: req->resid_len -= 8; rsp->resid_len -= 64; resp_data[2] = SMP_RESP_FUNC_ACC; memcpy(resp_data + 12, shost->hostt->name, SAS_EXPANDER_VENDOR_ID_LEN); memcpy(resp_data + 20, "libsas virt phy", SAS_EXPANDER_PRODUCT_ID_LEN); break; case SMP_READ_GPIO_REG: /* FIXME: need GPIO support in the transport class */ break; case SMP_DISCOVER: req->resid_len -= 16; if ((int)req->resid_len < 0) { req->resid_len = 0; error = -EINVAL; goto out; } rsp->resid_len -= 56; sas_host_smp_discover(sas_ha, resp_data, req_data[9]); break; case SMP_REPORT_PHY_ERR_LOG: /* FIXME: could implement this with additional * libsas callbacks providing the HW supports it */ break; case SMP_REPORT_PHY_SATA: req->resid_len -= 16; if ((int)req->resid_len < 0) { req->resid_len = 0; error = -EINVAL; goto out; } rsp->resid_len -= 60; sas_report_phy_sata(sas_ha, resp_data, req_data[9]); break; case SMP_REPORT_ROUTE_INFO: /* Can't implement; hosts have no routes */ break; case SMP_WRITE_GPIO_REG: /* FIXME: need GPIO support in the transport class */ break; case SMP_CONF_ROUTE_INFO: /* Can't implement; hosts have no routes */ break; case SMP_PHY_CONTROL: req->resid_len -= 44; if ((int)req->resid_len < 0) { req->resid_len = 0; error = -EINVAL; goto out; } rsp->resid_len -= 8; sas_phy_control(sas_ha, req_data[9], req_data[10], req_data[32] >> 4, req_data[33] >> 4, resp_data); break; case SMP_PHY_TEST_FUNCTION: /* FIXME: should this be implemented? */ break; default: /* probably a 2.0 function */ break; } local_irq_disable(); buf = kmap_atomic(bio_page(rsp->bio), KM_USER0) + bio_offset(rsp->bio); memcpy(buf, resp_data, blk_rq_bytes(rsp)); flush_kernel_dcache_page(bio_page(rsp->bio)); kunmap_atomic(buf - bio_offset(rsp->bio), KM_USER0); local_irq_enable(); out: kfree(req_data); kfree(resp_data); return error; }
static ssize_t pm8001_store_update_fw(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; char *cmd_ptr, *filename_ptr; int res, i; int flash_command = FLASH_CMD_NONE; int err = 0; if (!capable(CAP_SYS_ADMIN)) return -EACCES; cmd_ptr = kzalloc(count*2, GFP_KERNEL); if (!cmd_ptr) { err = FAIL_OUT_MEMORY; goto out; } filename_ptr = cmd_ptr + count; res = sscanf(buf, "%s %s", cmd_ptr, filename_ptr); if (res != 2) { err = FAIL_PARAMETERS; goto out1; } for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) { if (!memcmp(flash_command_table[i].command, cmd_ptr, strlen(cmd_ptr))) { flash_command = flash_command_table[i].code; break; } } if (flash_command == FLASH_CMD_NONE) { err = FAIL_PARAMETERS; goto out1; } if (pm8001_ha->fw_status == FLASH_IN_PROGRESS) { err = FLASH_IN_PROGRESS; goto out1; } err = 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; } 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; } release_firmware(pm8001_ha->fw_image); out1: kfree(cmd_ptr); out: pm8001_ha->fw_status = err; if (!err) return count; else return -err; }
ssize_t pm80xx_get_fatal_dump(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; void __iomem *fatal_table_address = pm8001_ha->fatal_tbl_addr; u32 accum_len , reg_val, index, *temp; unsigned long start; u8 *direct_data; char *fatal_error_data = buf; pm8001_ha->forensic_info.data_buf.direct_data = buf; if (pm8001_ha->chip_id == chip_8001) { pm8001_ha->forensic_info.data_buf.direct_data += sprintf(pm8001_ha->forensic_info.data_buf.direct_data, "Not supported for SPC controller"); return (char *)pm8001_ha->forensic_info.data_buf.direct_data - (char *)buf; } if (pm8001_ha->forensic_info.data_buf.direct_offset == 0) { PM8001_IO_DBG(pm8001_ha, pm8001_printk("forensic_info TYPE_NON_FATAL..............\n")); direct_data = (u8 *)fatal_error_data; pm8001_ha->forensic_info.data_type = TYPE_NON_FATAL; pm8001_ha->forensic_info.data_buf.direct_len = SYSFS_OFFSET; pm8001_ha->forensic_info.data_buf.read_len = 0; pm8001_ha->forensic_info.data_buf.direct_data = direct_data; /* start to get data */ /* Program the MEMBASE II Shifting Register with 0x00.*/ pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER, pm8001_ha->fatal_forensic_shift_offset); pm8001_ha->forensic_last_offset = 0; pm8001_ha->forensic_fatal_step = 0; pm8001_ha->fatal_bar_loc = 0; } /* Read until accum_len is retrived */ accum_len = pm8001_mr32(fatal_table_address, MPI_FATAL_EDUMP_TABLE_ACCUM_LEN); PM8001_IO_DBG(pm8001_ha, pm8001_printk("accum_len 0x%x\n", accum_len)); if (accum_len == 0xFFFFFFFF) { PM8001_IO_DBG(pm8001_ha, pm8001_printk("Possible PCI issue 0x%x not expected\n", accum_len)); return -EIO; } if (accum_len == 0 || accum_len >= 0x100000) { pm8001_ha->forensic_info.data_buf.direct_data += sprintf(pm8001_ha->forensic_info.data_buf.direct_data, "%08x ", 0xFFFFFFFF); return (char *)pm8001_ha->forensic_info.data_buf.direct_data - (char *)buf; } temp = (u32 *)pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr; if (pm8001_ha->forensic_fatal_step == 0) { moreData: if (pm8001_ha->forensic_info.data_buf.direct_data) { /* Data is in bar, copy to host memory */ pm80xx_pci_mem_copy(pm8001_ha, pm8001_ha->fatal_bar_loc, pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr, pm8001_ha->forensic_info.data_buf.direct_len , 1); } pm8001_ha->fatal_bar_loc += pm8001_ha->forensic_info.data_buf.direct_len; pm8001_ha->forensic_info.data_buf.direct_offset += pm8001_ha->forensic_info.data_buf.direct_len; pm8001_ha->forensic_last_offset += pm8001_ha->forensic_info.data_buf.direct_len; pm8001_ha->forensic_info.data_buf.read_len = pm8001_ha->forensic_info.data_buf.direct_len; if (pm8001_ha->forensic_last_offset >= accum_len) { pm8001_ha->forensic_info.data_buf.direct_data += sprintf(pm8001_ha->forensic_info.data_buf.direct_data, "%08x ", 3); for (index = 0; index < (SYSFS_OFFSET / 4); index++) { pm8001_ha->forensic_info.data_buf.direct_data += sprintf(pm8001_ha-> forensic_info.data_buf.direct_data, "%08x ", *(temp + index)); } pm8001_ha->fatal_bar_loc = 0; pm8001_ha->forensic_fatal_step = 1; pm8001_ha->fatal_forensic_shift_offset = 0; pm8001_ha->forensic_last_offset = 0; return (char *)pm8001_ha-> forensic_info.data_buf.direct_data - (char *)buf; } if (pm8001_ha->fatal_bar_loc < (64 * 1024)) { pm8001_ha->forensic_info.data_buf.direct_data += sprintf(pm8001_ha-> forensic_info.data_buf.direct_data, "%08x ", 2); for (index = 0; index < (SYSFS_OFFSET / 4); index++) { pm8001_ha->forensic_info.data_buf.direct_data += sprintf(pm8001_ha-> forensic_info.data_buf.direct_data, "%08x ", *(temp + index)); } return (char *)pm8001_ha-> forensic_info.data_buf.direct_data - (char *)buf; } /* Increment the MEMBASE II Shifting Register value by 0x100.*/ pm8001_ha->forensic_info.data_buf.direct_data += sprintf(pm8001_ha->forensic_info.data_buf.direct_data, "%08x ", 2); for (index = 0; index < 256; index++) { pm8001_ha->forensic_info.data_buf.direct_data += sprintf(pm8001_ha-> forensic_info.data_buf.direct_data, "%08x ", *(temp + index)); } pm8001_ha->fatal_forensic_shift_offset += 0x100; pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER, pm8001_ha->fatal_forensic_shift_offset); pm8001_ha->fatal_bar_loc = 0; return (char *)pm8001_ha->forensic_info.data_buf.direct_data - (char *)buf; } if (pm8001_ha->forensic_fatal_step == 1) { pm8001_ha->fatal_forensic_shift_offset = 0; /* Read 64K of the debug data. */ pm8001_cw32(pm8001_ha, 0, MEMBASE_II_SHIFT_REGISTER, pm8001_ha->fatal_forensic_shift_offset); pm8001_mw32(fatal_table_address, MPI_FATAL_EDUMP_TABLE_HANDSHAKE, MPI_FATAL_EDUMP_HANDSHAKE_RDY); /* Poll FDDHSHK until clear */ start = jiffies + (2 * HZ); /* 2 sec */ do { reg_val = pm8001_mr32(fatal_table_address, MPI_FATAL_EDUMP_TABLE_HANDSHAKE); } while ((reg_val) && time_before(jiffies, start)); if (reg_val != 0) { PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("TIMEOUT:MEMBASE_II_SHIFT_REGISTER" " = 0x%x\n", reg_val)); return -EIO; } /* Read the next 64K of the debug data. */ pm8001_ha->forensic_fatal_step = 0; if (pm8001_mr32(fatal_table_address, MPI_FATAL_EDUMP_TABLE_STATUS) != MPI_FATAL_EDUMP_TABLE_STAT_NF_SUCCESS_DONE) { pm8001_mw32(fatal_table_address, MPI_FATAL_EDUMP_TABLE_HANDSHAKE, 0); goto moreData; } else { pm8001_ha->forensic_info.data_buf.direct_data += sprintf(pm8001_ha-> forensic_info.data_buf.direct_data, "%08x ", 4); pm8001_ha->forensic_info.data_buf.read_len = 0xFFFFFFFF; pm8001_ha->forensic_info.data_buf.direct_len = 0; pm8001_ha->forensic_info.data_buf.direct_offset = 0; pm8001_ha->forensic_info.data_buf.read_len = 0; } } return (char *)pm8001_ha->forensic_info.data_buf.direct_data - (char *)buf; }