[SCSI] megaraid_sas: add hibernation support
authorbo yang <bo.yang@lsi.com>
Wed, 7 Nov 2007 17:09:50 +0000 (12:09 -0500)
committerJames Bottomley <James.Bottomley@HansenPartnership.com>
Sat, 12 Jan 2008 00:22:43 +0000 (18:22 -0600)
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>
drivers/scsi/megaraid/megaraid_sas.c
drivers/scsi/megaraid/megaraid_sas.h

index 3ad379fcd18f1069c06de059a6b0775398e5ff56..9aee2544798c32b1c6df8c681b1945485546e239 100644 (file)
@@ -1803,6 +1803,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);
 
@@ -2524,6 +2559,131 @@ static void megasas_shutdown_controller(struct megasas_instance *instance)
        return;
 }
 
+/**
+ * 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,
 };
 
index 08c74c991781cd52dc3898842cc9b98499354685..f4ab300946214b85ef1fdc0d3d10301c645968e8 100644 (file)
 #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