diff options
author | bo yang <bo.yang@lsi.com> | 2007-11-07 17:09:50 (GMT) |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-01-12 00:22:43 (GMT) |
commit | 31ea7088974c2405e19d72f17c2afb103ef19e02 (patch) | |
tree | 6956005710481785d7c7aa04f2e72a87e5db4496 | |
parent | 1e6c38cec08f88b0df88a34e80f15492cace74e9 (diff) | |
download | linux-31ea7088974c2405e19d72f17c2afb103ef19e02.tar.xz |
[SCSI] megaraid_sas: add hibernation support
Adding hibernation support. suspend, resume routine implemented.
Signed-off-by: Bo Yang <bo.yang@lsi.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas.c | 302 | ||||
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas.h | 1 |
2 files changed, 233 insertions, 70 deletions
diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c index 3ad379f..9aee254 100644 --- a/drivers/scsi/megaraid/megaraid_sas.c +++ b/drivers/scsi/megaraid/megaraid_sas.c @@ -1804,6 +1804,81 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr) } /** + * megasas_issue_init_mfi - Initializes the FW + * @instance: Adapter soft state + * + * Issues the INIT MFI cmd + */ +static int +megasas_issue_init_mfi(struct megasas_instance *instance) +{ + u32 context; + + struct megasas_cmd *cmd; + + 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; + + /* + * 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; + + context = init_frame->context; + memset(init_frame, 0, MEGAMFI_FRAME_SIZE); + memset(initq_info, 0, sizeof(struct megasas_init_queue_info)); + init_frame->context = context; + + 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); + + /* + * disable the intr before firing the init frame to FW + */ + instance->instancet->disable_intr(instance->reg_set); + + /* + * Issue the init frame in polled mode + */ + + if (megasas_issue_polled(instance, cmd)) { + printk(KERN_ERR "megasas: Failed to init firmware\n"); + megasas_return_cmd(instance, cmd); + goto fail_fw_init; + } + + megasas_return_cmd(instance, cmd); + + return 0; + +fail_fw_init: + return -EINVAL; +} + +/** * megasas_init_mfi - Initializes the FW * @instance: Adapter soft state * @@ -1816,15 +1891,7 @@ static int megasas_init_mfi(struct megasas_instance *instance) u32 max_sectors_1; u32 max_sectors_2; struct megasas_register_set __iomem *reg_set; - - struct megasas_cmd *cmd; struct megasas_ctrl_info *ctrl_info; - - 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 */ @@ -1901,52 +1968,8 @@ static int megasas_init_mfi(struct megasas_instance *instance) 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); - - /* - * disable the intr before firing the init frame to FW - */ - instance->instancet->disable_intr(instance->reg_set); - - /* - * Issue the init frame in polled mode - */ - if (megasas_issue_polled(instance, cmd)) { - printk(KERN_DEBUG "megasas: Failed to init firmware\n"); + if (megasas_issue_init_mfi(instance)) goto fail_fw_init; - } - - megasas_return_cmd(instance, cmd); ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL); @@ -1982,7 +2005,6 @@ static int megasas_init_mfi(struct megasas_instance *instance) 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); @@ -2264,6 +2286,28 @@ static int megasas_io_attach(struct megasas_instance *instance) return 0; } +static int +megasas_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_one - PCI hotplug entry point * @pdev: PCI device structure @@ -2297,19 +2341,8 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) 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; - } + if (megasas_set_dma_mask(pdev)) + goto fail_set_dma_mask; host = scsi_host_alloc(&megasas_template, sizeof(struct megasas_instance)); @@ -2491,8 +2524,10 @@ static void megasas_flush_cache(struct megasas_instance *instance) /** * megasas_shutdown_controller - Instructs FW to shutdown the controller * @instance: Adapter soft state + * @opcode: Shutdown/Hibernate */ -static void megasas_shutdown_controller(struct megasas_instance *instance) +static void megasas_shutdown_controller(struct megasas_instance *instance, + u32 opcode) { struct megasas_cmd *cmd; struct megasas_dcmd_frame *dcmd; @@ -2515,7 +2550,7 @@ static void megasas_shutdown_controller(struct megasas_instance *instance) dcmd->flags = MFI_FRAME_DIR_NONE; dcmd->timeout = 0; dcmd->data_xfer_len = 0; - dcmd->opcode = MR_DCMD_CTRL_SHUTDOWN; + dcmd->opcode = opcode; megasas_issue_blocked_cmd(instance, cmd); @@ -2525,6 +2560,131 @@ static void megasas_shutdown_controller(struct megasas_instance *instance) } /** + * megasas_suspend - driver suspend entry point + * @pdev: PCI device structure + * @state: PCI power state to suspend routine + */ +static int __devinit +megasas_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct Scsi_Host *host; + struct megasas_instance *instance; + + instance = pci_get_drvdata(pdev); + host = instance->host; + + megasas_flush_cache(instance); + megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN); + tasklet_kill(&instance->isr_tasklet); + + pci_set_drvdata(instance->pdev, instance); + instance->instancet->disable_intr(instance->reg_set); + free_irq(instance->pdev->irq, instance); + + pci_save_state(pdev); + pci_disable_device(pdev); + + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +/** + * megasas_resume- driver resume entry point + * @pdev: PCI device structure + */ +static int __devinit +megasas_resume(struct pci_dev *pdev) +{ + int rval; + struct Scsi_Host *host; + struct megasas_instance *instance; + + instance = pci_get_drvdata(pdev); + host = instance->host; + pci_set_power_state(pdev, PCI_D0); + pci_enable_wake(pdev, PCI_D0, 0); + pci_restore_state(pdev); + + /* + * PCI prepping: enable device set bus mastering and dma mask + */ + rval = pci_enable_device(pdev); + + if (rval) { + printk(KERN_ERR "megasas: Enable device failed\n"); + return rval; + } + + pci_set_master(pdev); + + if (megasas_set_dma_mask(pdev)) + goto fail_set_dma_mask; + + /* + * Initialize MFI Firmware + */ + + *instance->producer = 0; + *instance->consumer = 0; + + atomic_set(&instance->fw_outstanding, 0); + + /* + * We expect the FW state to be READY + */ + if (megasas_transition_to_ready(instance)) + goto fail_ready_state; + + if (megasas_issue_init_mfi(instance)) + goto fail_init_mfi; + + tasklet_init(&instance->isr_tasklet, megasas_complete_cmd_dpc, + (unsigned long)instance); + + /* + * Register IRQ + */ + if (request_irq(pdev->irq, megasas_isr, IRQF_SHARED, + "megasas", instance)) { + printk(KERN_ERR "megasas: Failed to register IRQ\n"); + goto fail_irq; + } + + instance->instancet->enable_intr(instance->reg_set); + + /* + * Initiate AEN (Asynchronous Event Notification) + */ + if (megasas_start_aen(instance)) + printk(KERN_ERR "megasas: Start AEN failed\n"); + + return 0; + +fail_irq: +fail_init_mfi: + if (instance->evt_detail) + pci_free_consistent(pdev, sizeof(struct megasas_evt_detail), + instance->evt_detail, + instance->evt_detail_h); + + if (instance->producer) + pci_free_consistent(pdev, sizeof(u32), instance->producer, + instance->producer_h); + if (instance->consumer) + pci_free_consistent(pdev, sizeof(u32), instance->consumer, + instance->consumer_h); + scsi_host_put(host); + +fail_set_dma_mask: +fail_ready_state: + + pci_disable_device(pdev); + + return -ENODEV; +} + +/** * megasas_detach_one - PCI hot"un"plug entry point * @pdev: PCI device structure */ @@ -2539,7 +2699,7 @@ static void megasas_detach_one(struct pci_dev *pdev) scsi_remove_host(instance->host); megasas_flush_cache(instance); - megasas_shutdown_controller(instance); + megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); tasklet_kill(&instance->isr_tasklet); /* @@ -2978,6 +3138,8 @@ static struct pci_driver megasas_pci_driver = { .id_table = megasas_pci_table, .probe = megasas_probe_one, .remove = __devexit_p(megasas_detach_one), + .suspend = megasas_suspend, + .resume = megasas_resume, .shutdown = megasas_shutdown, }; diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 08c74c9..f4ab300 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -117,6 +117,7 @@ #define MR_FLUSH_DISK_CACHE 0x02 #define MR_DCMD_CTRL_SHUTDOWN 0x01050000 +#define MR_DCMD_HIBERNATE_SHUTDOWN 0x01060000 #define MR_ENABLE_DRIVE_SPINDOWN 0x01 #define MR_DCMD_CTRL_EVENT_GET_INFO 0x01040100 |