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);
}
Example #3
0
/**
  * 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);
}
Example #8
0
/**
 * 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);
}
Example #10
0
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;
}
Example #12
0
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;
}