⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 megaraid_sas.c

📁 最新的Megaraid SAS卡驱动源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/** * megasas_init_mfi -	Initializes the FW * @instance:		Adapter soft state * * This is the main function for initializing MFI firmware. */static int megasas_init_mfi(struct megasas_instance *instance){	u32 context_sz;	u32 reply_q_sz;	u32 max_sectors_1;	u32 max_sectors_2;	u32 tmp_sectors;	struct megasas_register_set __iomem *reg_set;	struct megasas_ctrl_info *ctrl_info;	/*	 * Map the message registers	 */	instance->base_addr = pci_resource_start(instance->pdev, 0);	if (pci_request_regions(instance->pdev, "megasas: LSI Logic")) {		printk(KERN_DEBUG "megasas: IO memory region busy!\n");		return -EBUSY;	}	instance->reg_set = ioremap_nocache(instance->base_addr, 8192);	if (!instance->reg_set) {		printk(KERN_DEBUG "megasas: Failed to map IO mem\n");		goto fail_ioremap;	}	reg_set = instance->reg_set;	switch(instance->pdev->device)	{		case PCI_DEVICE_ID_LSI_SAS1078R:				instance->instancet = &megasas_instance_template_ppc;			break;		case PCI_DEVICE_ID_LSI_SAS1064R:		case PCI_DEVICE_ID_DELL_PERC5:		default:			instance->instancet = &megasas_instance_template_xscale;			break;	}	/*	 * We expect the FW state to be READY	 */	if (megasas_transition_to_ready(instance))		goto fail_ready_state;	/*	 * Get various operational parameters from status register	 */	instance->max_fw_cmds = instance->instancet->read_fw_status_reg(reg_set) & 0x00FFFF;	/*	 * Reduce the max supported cmds by 1. This is to ensure that the	 * reply_q_sz (1 more than the max cmd that driver may send)	 * does not exceed max cmds that the FW can support	 */	instance->max_fw_cmds = instance->max_fw_cmds-1;	instance->max_num_sge = (instance->instancet->read_fw_status_reg(reg_set) & 0xFF0000) >> 					0x10;	/*	 * Create a pool of commands	 */	if (megasas_alloc_cmds(instance))		goto fail_alloc_cmds;	/*	 * Allocate memory for reply queue. Length of reply queue should	 * be _one_ more than the maximum commands handled by the firmware.	 *	 * Note: When FW completes commands, it places corresponding contex	 * values in this circular reply queue. This circular queue is a fairly	 * typical producer-consumer queue. FW is the producer (of completed	 * commands) and the driver is the consumer.	 */	context_sz = sizeof(u32);	reply_q_sz = context_sz * (instance->max_fw_cmds + 1);	instance->reply_queue = pci_alloc_consistent(instance->pdev,						     reply_q_sz,						     &instance->reply_queue_h);	if (!instance->reply_queue) {		printk(KERN_DEBUG "megasas: Out of DMA mem for reply queue\n");		goto fail_reply_queue;	}		if (megasas_issue_init_mfi(instance))		goto fail_fw_init;	ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL);	/*	 * Compute the max allowed sectors per IO: The controller info has two	 * limits on max sectors. Driver should use the minimum of these two.	 *	 * 1 << stripe_sz_ops.min = max sectors per strip	 *	 * Note that older firmwares ( < FW ver 30) didn't report information	 * to calculate max_sectors_1. So the number ended up as zero always.	 */	tmp_sectors = 0;	if (ctrl_info && !megasas_get_ctrl_info(instance, ctrl_info)) {		max_sectors_1 = (1 << ctrl_info->stripe_sz_ops.min) *		    ctrl_info->max_strips_per_io;		max_sectors_2 = ctrl_info->max_request_size;		tmp_sectors = (max_sectors_1 < max_sectors_2)		    ? max_sectors_1 : max_sectors_2;	} 	instance->max_sectors_per_req = instance->max_num_sge *		    PAGE_SIZE / 512;	if (tmp_sectors && (instance->max_sectors_per_req > tmp_sectors))		instance->max_sectors_per_req = tmp_sectors;	kfree(ctrl_info);        /*	* Setup tasklet for cmd completion	*/	tasklet_init(&instance->isr_tasklet, megasas_complete_cmd_dpc,                        (unsigned long)instance);	return 0;      fail_fw_init:	pci_free_consistent(instance->pdev, reply_q_sz,			    instance->reply_queue, instance->reply_queue_h);      fail_reply_queue:	megasas_free_cmds(instance);      fail_alloc_cmds:      fail_ready_state:	iounmap(instance->reg_set);      fail_ioremap:	pci_release_regions(instance->pdev);	return -EINVAL;}/** * megasas_release_mfi -	Reverses the FW initialization * @intance:			Adapter soft state */static void megasas_release_mfi(struct megasas_instance *instance){	u32 reply_q_sz = sizeof(u32) * (instance->max_fw_cmds + 1);	pci_free_consistent(instance->pdev, reply_q_sz,			    instance->reply_queue, instance->reply_queue_h);	megasas_free_cmds(instance);	iounmap(instance->reg_set);	pci_release_regions(instance->pdev);}/** * megasas_get_seq_num -	Gets latest event sequence numbers * @instance:			Adapter soft state * @eli:			FW event log sequence numbers information * * FW maintains a log of all events in a non-volatile area. Upper layers would * usually find out the latest sequence number of the events, the seq number at * the boot etc. They would "read" all the events below the latest seq number * by issuing a direct fw cmd (DCMD). For the future events (beyond latest seq * number), they would subsribe to AEN (asynchronous event notification) and * wait for the events to happen. */static intmegasas_get_seq_num(struct megasas_instance *instance,		    struct megasas_evt_log_info *eli){	struct megasas_cmd *cmd;	struct megasas_dcmd_frame *dcmd;	struct megasas_evt_log_info *el_info;	dma_addr_t el_info_h = 0;	cmd = megasas_get_cmd(instance);	if (!cmd) {		return -ENOMEM;	}	dcmd = &cmd->frame->dcmd;	el_info = pci_alloc_consistent(instance->pdev,				       sizeof(struct megasas_evt_log_info),				       &el_info_h);	if (!el_info) {		megasas_return_cmd(instance, cmd);		return -ENOMEM;	}	memset(el_info, 0, sizeof(*el_info));	memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);	dcmd->cmd = MFI_CMD_DCMD;	dcmd->cmd_status = 0x0;	dcmd->sge_count = 1;	dcmd->flags = MFI_FRAME_DIR_READ;	dcmd->timeout = 0;	dcmd->data_xfer_len = sizeof(struct megasas_evt_log_info);	dcmd->opcode = MR_DCMD_CTRL_EVENT_GET_INFO;	dcmd->sgl.sge32[0].phys_addr = el_info_h;	dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_log_info);	megasas_issue_blocked_cmd(instance, cmd);	/*	 * Copy the data back into callers buffer	 */	memcpy(eli, el_info, sizeof(struct megasas_evt_log_info));	pci_free_consistent(instance->pdev, sizeof(struct megasas_evt_log_info),			    el_info, el_info_h);	megasas_return_cmd(instance, cmd);	return 0;}/** * megasas_register_aen -	Registers for asynchronous event notification * @instance:			Adapter soft state * @seq_num:			The starting sequence number * @class_locale:		Class of the event * * This function subscribes for AEN for events beyond the @seq_num. It requests * to be notified if and only if the event is of type @class_locale */static intmegasas_register_aen(struct megasas_instance *instance, u32 seq_num,		     u32 class_locale_word){	int ret_val;	struct megasas_cmd *cmd;	struct megasas_dcmd_frame *dcmd;	union megasas_evt_class_locale curr_aen;	union megasas_evt_class_locale prev_aen;	/*	 * If there an AEN pending already (aen_cmd), check if the	 * class_locale of that pending AEN is inclusive of the new	 * AEN request we currently have. If it is, then we don't have	 * to do anything. In other words, whichever events the current	 * AEN request is subscribing to, have already been subscribed	 * to.	 *	 * If the old_cmd is _not_ inclusive, then we have to abort	 * that command, form a class_locale that is superset of both	 * old and current and re-issue to the FW	 */	curr_aen.word = class_locale_word;	if (instance->aen_cmd) {		prev_aen.word = instance->aen_cmd->frame->dcmd.mbox.w[1];		/*		 * A class whose enum value is smaller is inclusive of all		 * higher values. If a PROGRESS (= -1) was previously		 * registered, then a new registration requests for higher		 * classes need not be sent to FW. They are automatically		 * included.		 *		 * Locale numbers don't have such hierarchy. They are bitmap		 * values		 */		if ((prev_aen.members.class <= curr_aen.members.class) &&		    !((prev_aen.members.locale & curr_aen.members.locale) ^		      curr_aen.members.locale)) {			/*			 * Previously issued event registration includes			 * current request. Nothing to do.			 */			return 0;		} else {			curr_aen.members.locale |= prev_aen.members.locale;			if (prev_aen.members.class < curr_aen.members.class)				curr_aen.members.class = prev_aen.members.class;			instance->aen_cmd->abort_aen = 1;			ret_val = megasas_issue_blocked_abort_cmd(instance,								  instance->								  aen_cmd);			if (ret_val) {				printk(KERN_DEBUG "megasas: Failed to abort "				       "previous AEN command\n");				return ret_val;			}		}	}	cmd = megasas_get_cmd(instance);	if (!cmd)		return -ENOMEM;	dcmd = &cmd->frame->dcmd;	memset(instance->evt_detail, 0, sizeof(struct megasas_evt_detail));	/*	 * Prepare DCMD for aen registration	 */	memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);	dcmd->cmd = MFI_CMD_DCMD;	dcmd->cmd_status = 0x0;	dcmd->sge_count = 1;	dcmd->flags = MFI_FRAME_DIR_READ;	dcmd->timeout = 0;	dcmd->data_xfer_len = sizeof(struct megasas_evt_detail);	dcmd->opcode = MR_DCMD_CTRL_EVENT_WAIT;	dcmd->mbox.w[0] = seq_num;	dcmd->mbox.w[1] = curr_aen.word;	dcmd->sgl.sge32[0].phys_addr = (u32) instance->evt_detail_h;	dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_detail);	/*	 * Store reference to the cmd used to register for AEN. When an	 * application wants us to register for AEN, we have to abort this	 * cmd and re-register with a new EVENT LOCALE supplied by that app	 */	instance->aen_cmd = cmd;	/*	 * Issue the aen registration frame	 */	instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);	return 0;}/** * megasas_start_aen -	Subscribes to AEN during driver load time * @instance:		Adapter soft state */static int megasas_start_aen(struct megasas_instance *instance){	struct megasas_evt_log_info eli;	union megasas_evt_class_locale class_locale;	/*	 * Get the latest sequence number from FW	 */	memset(&eli, 0, sizeof(eli));	if (megasas_get_seq_num(instance, &eli))		return -1;	/*	 * Register AEN with FW for latest sequence number plus 1	 */	class_locale.members.reserved = 0;	class_locale.members.locale = MR_EVT_LOCALE_ALL;	class_locale.members.class = MR_EVT_CLASS_DEBUG;	return megasas_register_aen(instance, eli.newest_seq_num + 1,				    class_locale.word);}static ssize_tsysfs_max_sectors_read(struct kobject *kobj, char *buf,			loff_t off, size_t count){	struct Scsi_Host *host = class_to_shost(container_of(kobj,					struct class_device, kobj));	struct megasas_instance *instance = 				(struct megasas_instance *)host->hostdata;	count = sprintf(buf,"%u\n", instance->max_sectors_per_req);	return count+1;}static struct bin_attribute sysfs_max_sectors_attr = {	.attr = {		.name = "max_sectors",		.mode = S_IRUSR|S_IRGRP|S_IROTH,		.owner = THIS_MODULE,	},	.size = 7,	.read = sysfs_max_sectors_read,};/** * megasas_io_attach -	Attaches this driver to SCSI mid-layer * @instance:		Adapter soft state */static int megasas_io_attach(struct megasas_instance *instance){	struct Scsi_Host *host = instance->host;	int error;	/*	 * Export parameters required by SCSI mid-layer	 */	host->irq = instance->pdev->irq;	host->unique_id = instance->unique_id;	host->can_queue = instance->max_fw_cmds - MEGASAS_INT_CMDS;	host->this_id = instance->init_id;	host->sg_tablesize = instance->max_num_sge;	/*	 * Check if the module parameter value for max_sectors can be used	 */	if (max_sectors && max_sectors < instance->max_sectors_per_req)		instance->max_sectors_per_req = max_sectors;	else {		if (max_sectors)			printk(KERN_INFO "megasas: max_sectors should be > 0 and"			 	"<= %d\n",instance->max_sectors_per_req);	}	host->max_sectors = instance->max_sectors_per_req;	/*	 * Check if the module parameter value for cmd_per_lun can be used	 */	instance->cmd_per_lun = MEGASAS_DEFAULT_CMD_PER_LUN;	if (cmd_per_lun && cmd_per_lun <= MEGASAS_DEFAULT_CMD_PER_LUN)		instance->cmd_per_lun = cmd_per_lun;	else		printk(KERN_INFO "megasas: cmd_per_lun should be > 0 and"			 	"<= %d\n",MEGASAS_DEFAULT_CMD_PER_LUN);			host->cmd_per_lun = instance->cmd_per_lun;	printk(KERN_DEBUG "megasas: max_sectors : 0x%x, cmd_per_lun : 0x%x\n",			instance->max_sectors_per_req, instance->cmd_per_lun);	host->max_channel = MEGASAS_MAX_CHANNELS - 1;	host->max_id = MEGASAS_MAX_DEV_PER_CHANNEL;	host->max_lun = MEGASAS_MAX_LUN;	host->max_cmd_len = 16;	/*	 * Notify the mid-layer about the new controller	 */	if (scsi_add_host(host, &instance->pdev->dev)) {		printk(KERN_DEBUG "megasas: scsi_add_host failed\n");		return -ENODEV;	}	/*	 * Create sysfs entries for module paramaters	 */	error = sysfs_create_bin_file(&instance->host->shost_classdev.kobj,			&sysfs_max_sectors_attr);	if (error) {		printk(KERN_INFO "megasas: Error in creating the sysfs entry"				" max_sectors.\n");		goto out_remove_host;	}	/*	 * Trigger SCSI to scan our drives	 */	scsi_scan_host(host);	return 0;out_remove_host:	scsi_remove_host(host);	return error;}static intmegasas_set_dma_mask(struct pci_dev *pdev){	/*	 * All our contollers are capable of performing 64-bit DMA	 */	if (IS_DMA64) {		if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) != 0) {			if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0)				goto fail_set_dma_mask;		}	} else {		if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0)			goto fail_set_dma_mask;	}	return 0;fail_set_dma_mask:	return 1;}/** * megasas_probe_o

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -