📄 megaraid_sas.c
字号:
struct megasas_init_frame *init_frame; struct megasas_init_queue_info *initq_info; dma_addr_t init_frame_h; dma_addr_t initq_info_h; /* * 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; /* * We expect the FW state to be READY */ if (megasas_transition_to_ready(instance->reg_set)) goto fail_ready_state; /* * Get various operational parameters from status register */ instance->max_fw_cmds = readl(®_set->outbound_msg_0) & 0x00FFFF; instance->max_num_sge = (readl(®_set->outbound_msg_0) & 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; } /* * Prepare a init frame. Note the init frame points to queue info * structure. Each frame has SGL allocated after first 64 bytes. For * this frame - since we don't need any SGL - we use SGL's space as * queue info structure * * We will not get a NULL command below. We just created the pool. */ cmd = megasas_get_cmd(instance); init_frame = (struct megasas_init_frame *)cmd->frame; initq_info = (struct megasas_init_queue_info *) ((unsigned long)init_frame + 64); init_frame_h = cmd->frame_phys_addr; initq_info_h = init_frame_h + 64; memset(init_frame, 0, MEGAMFI_FRAME_SIZE); memset(initq_info, 0, sizeof(struct megasas_init_queue_info)); initq_info->reply_queue_entries = instance->max_fw_cmds + 1; initq_info->reply_queue_start_phys_addr_lo = instance->reply_queue_h; initq_info->producer_index_phys_addr_lo = instance->producer_h; initq_info->consumer_index_phys_addr_lo = instance->consumer_h; init_frame->cmd = MFI_CMD_INIT; init_frame->cmd_status = 0xFF; init_frame->queue_info_new_phys_addr_lo = initq_info_h; init_frame->data_xfer_len = sizeof(struct megasas_init_queue_info); /* * Issue the init frame in polled mode */ if (megasas_issue_polled(instance, cmd)) { printk(KERN_DEBUG "megasas: Failed to init firmware\n"); goto fail_fw_init; } megasas_return_cmd(instance, cmd); 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. */ 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; instance->max_sectors_per_req = (max_sectors_1 < max_sectors_2) ? max_sectors_1 : max_sectors_2; } else instance->max_sectors_per_req = instance->max_num_sge * PAGE_SIZE / 512; kfree(ctrl_info); return 0; fail_fw_init: megasas_return_cmd(instance, cmd); 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 */ writel(cmd->frame_phys_addr >> 3, &instance->reg_set->inbound_queue_port); 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);}/** * 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; /* * 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; host->max_sectors = instance->max_sectors_per_req; host->cmd_per_lun = 128; host->max_channel = MEGASAS_MAX_CHANNELS - 1; host->max_id = MEGASAS_MAX_DEV_PER_CHANNEL; host->max_lun = MEGASAS_MAX_LUN; /* * 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; } /* * Trigger SCSI to scan our drives */ scsi_scan_host(host); return 0;}/** * megasas_probe_one - PCI hotplug entry point * @pdev: PCI device structure * @id: PCI ids of supported hotplugged adapter */static int __devinitmegasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id){ int rval; struct Scsi_Host *host; struct megasas_instance *instance; /* * Announce PCI information */ printk(KERN_INFO "megasas: %#4.04x:%#4.04x:%#4.04x:%#4.04x: ", pdev->vendor, pdev->device, pdev->subsystem_vendor, pdev->subsystem_device); printk("bus %d:slot %d:func %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); /* * PCI prepping: enable device set bus mastering and dma mask */ rval = pci_enable_device(pdev); if (rval) { return rval; } pci_set_master(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; } host = scsi_host_alloc(&megasas_template, sizeof(struct megasas_instance)); if (!host) { printk(KERN_DEBUG "megasas: scsi_host_alloc failed\n"); goto fail_alloc_instance; } instance = (struct megasas_instance *)host->hostdata; memset(instance, 0, sizeof(*instance)); instance->producer = pci_alloc_consistent(pdev, sizeof(u32), &instance->producer_h); instance->consumer = pci_alloc_consistent(pdev, sizeof(u32), &instance->consumer_h); if (!instance->producer || !instance->consumer) { printk(KERN_DEBUG "megasas: Failed to allocate memory for " "producer, consumer\n"); goto fail_alloc_dma_buf; } *instance->producer = 0; *instance->consumer = 0; instance->evt_detail = pci_alloc_consistent(pdev, sizeof(struct megasas_evt_detail), &instance->evt_detail_h); if (!instance->evt_detail) { printk(KERN_DEBUG "megasas: Failed to allocate memory for " "event detail structure\n"); goto fail_alloc_dma_buf; } /* * Initialize locks and queues */ INIT_LIST_HEAD(&instance->cmd_pool); init_waitqueue_head(&instance->int_cmd_wait_q); init_waitqueue_head(&instance->abort_cmd_wait_q); spin_lock_init(&instance->cmd_pool_lock); spin_lock_init(&instance->instance_lock); sema_init(&instance->aen_mutex, 1); sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS); /* * Initialize PCI related and misc parameters */ instance->pdev = pdev; instance->host = host; instance->unique_id = pdev->bus->number << 8 | pdev->devfn; instance->init_id = MEGASAS_DEFAULT_INIT_ID; /* * Initialize MFI Firmware */ if (megasas_init_mfi(instance)) goto fail_init_mfi; /* * Register IRQ */ if (request_irq(pdev->irq, megasas_isr, SA_SHIRQ, "megasas", instance)) { printk(KERN_DEBUG "megasas: Failed to register IRQ\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -