[SCSI] megaraid_sas: use unsigned long for sense_buff ptr
[safe/jmp/linux-2.6] / drivers / scsi / megaraid / megaraid_sas.c
index a487f41..a58ad61 100644 (file)
  *        2 of the License, or (at your option) any later version.
  *
  * FILE                : megaraid_sas.c
- * Version     : v00.00.02.02
+ * Version     : v00.00.03.10-rc5
  *
  * Authors:
- *     Sreenivas Bagalkote     <Sreenivas.Bagalkote@lsil.com>
- *     Sumant Patro            <Sumant.Patro@lsil.com>
+ *     (email-id : megaraidlinux@lsi.com)
+ *     Sreenivas Bagalkote
+ *     Sumant Patro
+ *     Bo Yang
  *
  * List of supported controllers
  *
 #include <linux/moduleparam.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/uio.h>
 #include <asm/uaccess.h>
 #include <linux/fs.h>
 #include <linux/compat.h>
+#include <linux/blkdev.h>
 #include <linux/mutex.h>
 
 #include <scsi/scsi.h>
@@ -45,7 +49,7 @@
 
 MODULE_LICENSE("GPL");
 MODULE_VERSION(MEGASAS_VERSION);
-MODULE_AUTHOR("sreenivas.bagalkote@lsil.com");
+MODULE_AUTHOR("megaraidlinux@lsi.com");
 MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver");
 
 /*
@@ -53,19 +57,15 @@ MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver");
  */
 static struct pci_device_id megasas_pci_table[] = {
 
-       {
-        PCI_VENDOR_ID_LSI_LOGIC,
-        PCI_DEVICE_ID_LSI_SAS1064R, // xscale IOP
-        PCI_ANY_ID,
-        PCI_ANY_ID,
-        },
-       {
-        PCI_VENDOR_ID_DELL,
-        PCI_DEVICE_ID_DELL_PERC5, // xscale IOP
-        PCI_ANY_ID,
-        PCI_ANY_ID,
-        },
-       {0}                     /* Terminating entry */
+       {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064R)},
+       /* xscale IOP */
+       {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078R)},
+       /* ppc IOP */
+       {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VERDE_ZCR)},
+       /* xscale IOP, vega */
+       {PCI_DEVICE(PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_PERC5)},
+       /* xscale IOP */
+       {}
 };
 
 MODULE_DEVICE_TABLE(pci, megasas_pci_table);
@@ -75,6 +75,8 @@ static struct megasas_mgmt_info megasas_mgmt_info;
 static struct fasync_struct *megasas_async_queue;
 static DEFINE_MUTEX(megasas_async_queue_mutex);
 
+static u32 megasas_dbg_lvl;
+
 /**
  * megasas_get_cmd -   Get a command from the free pool
  * @instance:          Adapter soft state
@@ -139,6 +141,19 @@ megasas_enable_intr_xscale(struct megasas_register_set __iomem * regs)
 }
 
 /**
+ * megasas_disable_intr_xscale -Disables interrupt
+ * @regs:                      MFI register set
+ */
+static inline void
+megasas_disable_intr_xscale(struct megasas_register_set __iomem * regs)
+{
+       u32 mask = 0x1f;
+       writel(mask, &regs->outbound_intr_mask);
+       /* Dummy readl to force pci flush */
+       readl(&regs->outbound_intr_mask);
+}
+
+/**
  * megasas_read_fw_status_reg_xscale - returns the current FW status value
  * @regs:                      MFI register set
  */
@@ -189,6 +204,7 @@ static struct megasas_instance_template megasas_instance_template_xscale = {
 
        .fire_cmd = megasas_fire_cmd_xscale,
        .enable_intr = megasas_enable_intr_xscale,
+       .disable_intr = megasas_disable_intr_xscale,
        .clear_intr = megasas_clear_intr_xscale,
        .read_fw_status_reg = megasas_read_fw_status_reg_xscale,
 };
@@ -199,20 +215,100 @@ static struct megasas_instance_template megasas_instance_template_xscale = {
 */
 
 /**
- * megasas_disable_intr -      Disables interrupts
+*      The following functions are defined for ppc (deviceid : 0x60) 
+*      controllers
+*/
+
+/**
+ * megasas_enable_intr_ppc -   Enables interrupts
  * @regs:                      MFI register set
  */
 static inline void
-megasas_disable_intr(struct megasas_register_set __iomem * regs)
+megasas_enable_intr_ppc(struct megasas_register_set __iomem * regs)
 {
-       u32 mask = 0x1f; 
-       writel(mask, &regs->outbound_intr_mask);
+       writel(0xFFFFFFFF, &(regs)->outbound_doorbell_clear);
+    
+       writel(~0x80000004, &(regs)->outbound_intr_mask);
+
+       /* Dummy readl to force pci flush */
+       readl(&regs->outbound_intr_mask);
+}
 
+/**
+ * megasas_disable_intr_ppc -  Disable interrupt
+ * @regs:                      MFI register set
+ */
+static inline void
+megasas_disable_intr_ppc(struct megasas_register_set __iomem * regs)
+{
+       u32 mask = 0xFFFFFFFF;
+       writel(mask, &regs->outbound_intr_mask);
        /* Dummy readl to force pci flush */
        readl(&regs->outbound_intr_mask);
 }
 
 /**
+ * megasas_read_fw_status_reg_ppc - returns the current FW status value
+ * @regs:                      MFI register set
+ */
+static u32
+megasas_read_fw_status_reg_ppc(struct megasas_register_set __iomem * regs)
+{
+       return readl(&(regs)->outbound_scratch_pad);
+}
+
+/**
+ * megasas_clear_interrupt_ppc -       Check & clear interrupt
+ * @regs:                              MFI register set
+ */
+static int 
+megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs)
+{
+       u32 status;
+       /*
+        * Check if it is our interrupt
+        */
+       status = readl(&regs->outbound_intr_status);
+
+       if (!(status & MFI_REPLY_1078_MESSAGE_INTERRUPT)) {
+               return 1;
+       }
+
+       /*
+        * Clear the interrupt by writing back the same value
+        */
+       writel(status, &regs->outbound_doorbell_clear);
+
+       return 0;
+}
+/**
+ * megasas_fire_cmd_ppc -      Sends command to the FW
+ * @frame_phys_addr :          Physical address of cmd
+ * @frame_count :              Number of frames for the command
+ * @regs :                     MFI register set
+ */
+static inline void 
+megasas_fire_cmd_ppc(dma_addr_t frame_phys_addr, u32 frame_count, struct megasas_register_set __iomem *regs)
+{
+       writel((frame_phys_addr | (frame_count<<1))|1, 
+                       &(regs)->inbound_queue_port);
+}
+
+static struct megasas_instance_template megasas_instance_template_ppc = {
+       
+       .fire_cmd = megasas_fire_cmd_ppc,
+       .enable_intr = megasas_enable_intr_ppc,
+       .disable_intr = megasas_disable_intr_ppc,
+       .clear_intr = megasas_clear_intr_ppc,
+       .read_fw_status_reg = megasas_read_fw_status_reg_ppc,
+};
+
+/**
+*      This is the end of set of functions & definitions
+*      specific to ppc (deviceid : 0x60) controllers
+*/
+
+/**
  * megasas_issue_polled -      Issues a polling command
  * @instance:                  Adapter soft state
  * @cmd:                       Command packet to be issued 
@@ -255,6 +351,7 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd)
  * @cmd:                       Command to be issued
  *
  * This function waits on an event for the command to be returned from ISR.
+ * Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs
  * Used to issue ioctl commands.
  */
 static int
@@ -265,7 +362,8 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance,
 
        instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);
 
-       wait_event(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA));
+       wait_event_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA),
+               MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ);
 
        return 0;
 }
@@ -277,7 +375,8 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance,
  *
  * MFI firmware can abort previously issued AEN comamnd (automatic event
  * notification). The megasas_issue_blocked_abort_cmd() issues such abort
- * cmd and blocks till it is completed.
+ * cmd and waits for return status.
+ * Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs
  */
 static int
 megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
@@ -311,7 +410,8 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
        /*
         * Wait for this cmd to complete
         */
-       wait_event(instance->abort_cmd_wait_q, (cmd->cmd_status != 0xFF));
+       wait_event_timeout(instance->abort_cmd_wait_q, (cmd->cmd_status != 0xFF),
+               MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ);
 
        megasas_return_cmd(instance, cmd);
        return 0;
@@ -334,34 +434,15 @@ megasas_make_sgl32(struct megasas_instance *instance, struct scsi_cmnd *scp,
        int sge_count;
        struct scatterlist *os_sgl;
 
-       /*
-        * Return 0 if there is no data transfer
-        */
-       if (!scp->request_buffer || !scp->request_bufflen)
-               return 0;
-
-       if (!scp->use_sg) {
-               mfi_sgl->sge32[0].phys_addr = pci_map_single(instance->pdev,
-                                                            scp->
-                                                            request_buffer,
-                                                            scp->
-                                                            request_bufflen,
-                                                            scp->
-                                                            sc_data_direction);
-               mfi_sgl->sge32[0].length = scp->request_bufflen;
-
-               return 1;
-       }
-
-       os_sgl = (struct scatterlist *)scp->request_buffer;
-       sge_count = pci_map_sg(instance->pdev, os_sgl, scp->use_sg,
-                              scp->sc_data_direction);
+       sge_count = scsi_dma_map(scp);
+       BUG_ON(sge_count < 0);
 
-       for (i = 0; i < sge_count; i++, os_sgl++) {
-               mfi_sgl->sge32[i].length = sg_dma_len(os_sgl);
-               mfi_sgl->sge32[i].phys_addr = sg_dma_address(os_sgl);
+       if (sge_count) {
+               scsi_for_each_sg(scp, os_sgl, sge_count, i) {
+                       mfi_sgl->sge32[i].length = sg_dma_len(os_sgl);
+                       mfi_sgl->sge32[i].phys_addr = sg_dma_address(os_sgl);
+               }
        }
-
        return sge_count;
 }
 
@@ -382,36 +463,56 @@ megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp,
        int sge_count;
        struct scatterlist *os_sgl;
 
-       /*
-        * Return 0 if there is no data transfer
-        */
-       if (!scp->request_buffer || !scp->request_bufflen)
-               return 0;
+       sge_count = scsi_dma_map(scp);
+       BUG_ON(sge_count < 0);
+
+       if (sge_count) {
+               scsi_for_each_sg(scp, os_sgl, sge_count, i) {
+                       mfi_sgl->sge64[i].length = sg_dma_len(os_sgl);
+                       mfi_sgl->sge64[i].phys_addr = sg_dma_address(os_sgl);
+               }
+       }
+       return sge_count;
+}
 
-       if (!scp->use_sg) {
-               mfi_sgl->sge64[0].phys_addr = pci_map_single(instance->pdev,
-                                                            scp->
-                                                            request_buffer,
-                                                            scp->
-                                                            request_bufflen,
-                                                            scp->
-                                                            sc_data_direction);
+ /**
+ * megasas_get_frame_count - Computes the number of frames
+ * @sge_count          : number of sg elements
+ *
+ * Returns the number of frames required for numnber of sge's (sge_count)
+ */
 
-               mfi_sgl->sge64[0].length = scp->request_bufflen;
+static u32 megasas_get_frame_count(u8 sge_count)
+{
+       int num_cnt;
+       int sge_bytes;
+       u32 sge_sz;
+       u32 frame_count=0;
 
-               return 1;
-       }
+       sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
+           sizeof(struct megasas_sge32);
 
-       os_sgl = (struct scatterlist *)scp->request_buffer;
-       sge_count = pci_map_sg(instance->pdev, os_sgl, scp->use_sg,
-                              scp->sc_data_direction);
+       /*
+       * Main frame can contain 2 SGEs for 64-bit SGLs and
+       * 3 SGEs for 32-bit SGLs
+       */
+       if (IS_DMA64)
+               num_cnt = sge_count - 2;
+       else
+               num_cnt = sge_count - 3;
 
-       for (i = 0; i < sge_count; i++, os_sgl++) {
-               mfi_sgl->sge64[i].length = sg_dma_len(os_sgl);
-               mfi_sgl->sge64[i].phys_addr = sg_dma_address(os_sgl);
+       if(num_cnt>0){
+               sge_bytes = sge_sz * num_cnt;
+
+               frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) +
+                   ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) ;
        }
+       /* Main frame */
+       frame_count +=1;
 
-       return sge_count;
+       if (frame_count > 7)
+               frame_count = 8;
+       return frame_count;
 }
 
 /**
@@ -427,8 +528,6 @@ static int
 megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
                   struct megasas_cmd *cmd)
 {
-       u32 sge_sz;
-       int sge_bytes;
        u32 is_logical;
        u32 device_id;
        u16 flags = 0;
@@ -456,16 +555,13 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
        pthru->cdb_len = scp->cmd_len;
        pthru->timeout = 0;
        pthru->flags = flags;
-       pthru->data_xfer_len = scp->request_bufflen;
+       pthru->data_xfer_len = scsi_bufflen(scp);
 
        memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);
 
        /*
         * Construct SGL
         */
-       sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
-           sizeof(struct megasas_sge32);
-
        if (IS_DMA64) {
                pthru->flags |= MFI_FRAME_SGL64;
                pthru->sge_count = megasas_make_sgl64(instance, scp,
@@ -481,17 +577,11 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
        pthru->sense_buf_phys_addr_hi = 0;
        pthru->sense_buf_phys_addr_lo = cmd->sense_phys_addr;
 
-       sge_bytes = sge_sz * pthru->sge_count;
-
        /*
         * Compute the total number of frames this command consumes. FW uses
         * this number to pull sufficient number of frames from host memory.
         */
-       cmd->frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) +
-           ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) + 1;
-
-       if (cmd->frame_count > 7)
-               cmd->frame_count = 8;
+       cmd->frame_count = megasas_get_frame_count(pthru->sge_count);
 
        return cmd->frame_count;
 }
@@ -508,8 +598,6 @@ static int
 megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
                   struct megasas_cmd *cmd)
 {
-       u32 sge_sz;
-       int sge_bytes;
        u32 device_id;
        u8 sc = scp->cmnd[0];
        u16 flags = 0;
@@ -524,7 +612,7 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
                flags = MFI_FRAME_DIR_READ;
 
        /*
-        * Preare the Logical IO frame: 2nd bit is zero for all read cmds
+        * Prepare the Logical IO frame: 2nd bit is zero for all read cmds
         */
        ldio->cmd = (sc & 0x02) ? MFI_CMD_LD_WRITE : MFI_CMD_LD_READ;
        ldio->cmd_status = 0x0;
@@ -593,9 +681,6 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
        /*
         * Construct SGL
         */
-       sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
-           sizeof(struct megasas_sge32);
-
        if (IS_DMA64) {
                ldio->flags |= MFI_FRAME_SGL64;
                ldio->sge_count = megasas_make_sgl64(instance, scp, &ldio->sgl);
@@ -609,13 +694,11 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
        ldio->sense_buf_phys_addr_hi = 0;
        ldio->sense_buf_phys_addr_lo = cmd->sense_phys_addr;
 
-       sge_bytes = sge_sz * ldio->sge_count;
-
-       cmd->frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) +
-           ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) + 1;
-
-       if (cmd->frame_count > 7)
-               cmd->frame_count = 8;
+       /*
+        * Compute the total number of frames this command consumes. FW uses
+        * this number to pull sufficient number of frames from host memory.
+        */
+       cmd->frame_count = megasas_get_frame_count(ldio->sge_count);
 
        return cmd->frame_count;
 }
@@ -646,6 +729,69 @@ static inline int megasas_is_ldio(struct scsi_cmnd *cmd)
        }
 }
 
+ /**
+ * megasas_dump_pending_frames -       Dumps the frame address of all pending cmds
+ *                                     in FW
+ * @instance:                          Adapter soft state
+ */
+static inline void
+megasas_dump_pending_frames(struct megasas_instance *instance)
+{
+       struct megasas_cmd *cmd;
+       int i,n;
+       union megasas_sgl *mfi_sgl;
+       struct megasas_io_frame *ldio;
+       struct megasas_pthru_frame *pthru;
+       u32 sgcount;
+       u32 max_cmd = instance->max_fw_cmds;
+
+       printk(KERN_ERR "\nmegasas[%d]: Dumping Frame Phys Address of all pending cmds in FW\n",instance->host->host_no);
+       printk(KERN_ERR "megasas[%d]: Total OS Pending cmds : %d\n",instance->host->host_no,atomic_read(&instance->fw_outstanding));
+       if (IS_DMA64)
+               printk(KERN_ERR "\nmegasas[%d]: 64 bit SGLs were sent to FW\n",instance->host->host_no);
+       else
+               printk(KERN_ERR "\nmegasas[%d]: 32 bit SGLs were sent to FW\n",instance->host->host_no);
+
+       printk(KERN_ERR "megasas[%d]: Pending OS cmds in FW : \n",instance->host->host_no);
+       for (i = 0; i < max_cmd; i++) {
+               cmd = instance->cmd_list[i];
+               if(!cmd->scmd)
+                       continue;
+               printk(KERN_ERR "megasas[%d]: Frame addr :0x%08lx : ",instance->host->host_no,(unsigned long)cmd->frame_phys_addr);
+               if (megasas_is_ldio(cmd->scmd)){
+                       ldio = (struct megasas_io_frame *)cmd->frame;
+                       mfi_sgl = &ldio->sgl;
+                       sgcount = ldio->sge_count;
+                       printk(KERN_ERR "megasas[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x, lba lo : 0x%x, lba_hi : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n",instance->host->host_no, cmd->frame_count,ldio->cmd,ldio->target_id, ldio->start_lba_lo,ldio->start_lba_hi,ldio->sense_buf_phys_addr_lo,sgcount);
+               }
+               else {
+                       pthru = (struct megasas_pthru_frame *) cmd->frame;
+                       mfi_sgl = &pthru->sgl;
+                       sgcount = pthru->sge_count;
+                       printk(KERN_ERR "megasas[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x, lun : 0x%x, cdb_len : 0x%x, data xfer len : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n",instance->host->host_no,cmd->frame_count,pthru->cmd,pthru->target_id,pthru->lun,pthru->cdb_len , pthru->data_xfer_len,pthru->sense_buf_phys_addr_lo,sgcount);
+               }
+       if(megasas_dbg_lvl & MEGASAS_DBG_LVL){
+               for (n = 0; n < sgcount; n++){
+                       if (IS_DMA64)
+                               printk(KERN_ERR "megasas: sgl len : 0x%x, sgl addr : 0x%08lx ",mfi_sgl->sge64[n].length , (unsigned long)mfi_sgl->sge64[n].phys_addr) ;
+                       else
+                               printk(KERN_ERR "megasas: sgl len : 0x%x, sgl addr : 0x%x ",mfi_sgl->sge32[n].length , mfi_sgl->sge32[n].phys_addr) ;
+                       }
+               }
+               printk(KERN_ERR "\n");
+       } /*for max_cmd*/
+       printk(KERN_ERR "\nmegasas[%d]: Pending Internal cmds in FW : \n",instance->host->host_no);
+       for (i = 0; i < max_cmd; i++) {
+
+               cmd = instance->cmd_list[i];
+
+               if(cmd->sync_cmd == 1){
+                       printk(KERN_ERR "0x%08lx : ", (unsigned long)cmd->frame_phys_addr);
+               }
+       }
+       printk(KERN_ERR "megasas[%d]: Dumping Done.\n\n",instance->host->host_no);
+}
+
 /**
  * megasas_queue_command -     Queue entry point
  * @scmd:                      SCSI command to be queued
@@ -655,12 +801,16 @@ static int
 megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
 {
        u32 frame_count;
-       unsigned long flags;
        struct megasas_cmd *cmd;
        struct megasas_instance *instance;
 
        instance = (struct megasas_instance *)
            scmd->device->host->hostdata;
+
+       /* Don't process if we have already declared adapter dead */
+       if (instance->hw_crit_error)
+               return SCSI_MLQUEUE_HOST_BUSY;
+
        scmd->scsi_done = done;
        scmd->result = 0;
 
@@ -670,6 +820,18 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
                goto out_done;
        }
 
+       switch (scmd->cmnd[0]) {
+       case SYNCHRONIZE_CACHE:
+               /*
+                * FW takes care of flush cache on its own
+                * No need to send it down
+                */
+               scmd->result = DID_OK << 16;
+               goto out_done;
+       default:
+               break;
+       }
+
        cmd = megasas_get_cmd(instance);
        if (!cmd)
                return SCSI_MLQUEUE_HOST_BUSY;
@@ -687,14 +849,11 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
 
        cmd->scmd = scmd;
        scmd->SCp.ptr = (char *)cmd;
-       scmd->SCp.sent_command = jiffies;
 
        /*
         * Issue the command to the FW
         */
-       spin_lock_irqsave(&instance->instance_lock, flags);
-       instance->fw_outstanding++;
-       spin_unlock_irqrestore(&instance->instance_lock, flags);
+       atomic_inc(&instance->fw_outstanding);
 
        instance->instancet->fire_cmd(cmd->frame_phys_addr ,cmd->frame_count-1,instance->reg_set);
 
@@ -707,6 +866,26 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
        return 0;
 }
 
+static int megasas_slave_configure(struct scsi_device *sdev)
+{
+       /*
+        * Don't export physical disk devices to the disk driver.
+        *
+        * FIXME: Currently we don't export them to the midlayer at all.
+        *        That will be fixed once LSI engineers have audited the
+        *        firmware for possible issues.
+        */
+       if (sdev->channel < MEGASAS_MAX_PD_CHANNELS && sdev->type == TYPE_DISK)
+               return -ENXIO;
+
+       /*
+        * The RAID firmware may require extended timeouts.
+        */
+       if (sdev->channel >= MEGASAS_MAX_PD_CHANNELS)
+               sdev->timeout = MEGASAS_DEFAULT_CMD_TIMEOUT * HZ;
+       return 0;
+}
+
 /**
  * megasas_wait_for_outstanding -      Wait for all outstanding cmds
  * @instance:                          Adapter soft state
@@ -722,19 +901,27 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance)
 
        for (i = 0; i < wait_time; i++) {
 
-               if (!instance->fw_outstanding)
+               int outstanding = atomic_read(&instance->fw_outstanding);
+
+               if (!outstanding)
                        break;
 
                if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) {
                        printk(KERN_NOTICE "megasas: [%2d]waiting for %d "
-                              "commands to complete\n", i,
-                              instance->fw_outstanding);
+                              "commands to complete\n",i,outstanding);
                }
 
                msleep(1000);
        }
 
-       if (instance->fw_outstanding) {
+       if (atomic_read(&instance->fw_outstanding)) {
+               /*
+               * Send signal to FW to stop processing any pending cmds.
+               * The controller will be taken offline by the OS now.
+               */
+               writel(MFI_STOP_ADP,
+                               &instance->reg_set->inbound_doorbell);
+               megasas_dump_pending_frames(instance);
                instance->hw_crit_error = 1;
                return FAILED;
        }
@@ -757,8 +944,8 @@ static int megasas_generic_reset(struct scsi_cmnd *scmd)
 
        instance = (struct megasas_instance *)scmd->device->host->hostdata;
 
-       scmd_printk(KERN_NOTICE, scmd, "megasas: RESET -%ld cmd=%x\n",
-              scmd->serial_number, scmd->cmnd[0]);
+       scmd_printk(KERN_NOTICE, scmd, "megasas: RESET -%ld cmd=%x retries=%x\n",
+                scmd->serial_number, scmd->cmnd[0], scmd->retries);
 
        if (instance->hw_crit_error) {
                printk(KERN_ERR "megasas: cannot recover from previous reset "
@@ -775,21 +962,37 @@ static int megasas_generic_reset(struct scsi_cmnd *scmd)
        return ret_val;
 }
 
-static enum scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd)
+/**
+ * megasas_reset_timer - quiesce the adapter if required
+ * @scmd:              scsi cmnd
+ *
+ * Sets the FW busy flag and reduces the host->can_queue if the
+ * cmd has not been completed within the timeout period.
+ */
+static enum
+scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd)
 {
-       unsigned long seconds;
-
-       if (scmd->SCp.ptr) {
-               seconds = (jiffies - scmd->SCp.sent_command) / HZ;
+       struct megasas_cmd *cmd = (struct megasas_cmd *)scmd->SCp.ptr;
+       struct megasas_instance *instance;
+       unsigned long flags;
 
-               if (seconds < 90) {
-                       return EH_RESET_TIMER;
-               } else {
-                       return EH_NOT_HANDLED;
-               }
+       if (time_after(jiffies, scmd->jiffies_at_alloc +
+                               (MEGASAS_DEFAULT_CMD_TIMEOUT * 2) * HZ)) {
+               return EH_NOT_HANDLED;
        }
 
-       return EH_HANDLED;
+       instance = cmd->instance;
+       if (!(instance->flag & MEGASAS_FW_BUSY)) {
+               /* FW is busy, throttle IO */
+               spin_lock_irqsave(instance->host->host_lock, flags);
+
+               instance->host->can_queue = 16;
+               instance->last_time = jiffies;
+               instance->flag |= MEGASAS_FW_BUSY;
+
+               spin_unlock_irqrestore(instance->host->host_lock, flags);
+       }
+       return EH_RESET_TIMER;
 }
 
 /**
@@ -815,7 +1018,7 @@ static int megasas_reset_bus_host(struct scsi_cmnd *scmd)
        int ret;
 
        /*
-        * Frist wait for all commands to complete
+        * First wait for all commands to complete
         */
        ret = megasas_generic_reset(scmd);
 
@@ -823,6 +1026,49 @@ static int megasas_reset_bus_host(struct scsi_cmnd *scmd)
 }
 
 /**
+ * megasas_bios_param - Returns disk geometry for a disk
+ * @sdev:              device handle
+ * @bdev:              block device
+ * @capacity:          drive capacity
+ * @geom:              geometry parameters
+ */
+static int
+megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev,
+                sector_t capacity, int geom[])
+{
+       int heads;
+       int sectors;
+       sector_t cylinders;
+       unsigned long tmp;
+       /* Default heads (64) & sectors (32) */
+       heads = 64;
+       sectors = 32;
+
+       tmp = heads * sectors;
+       cylinders = capacity;
+
+       sector_div(cylinders, tmp);
+
+       /*
+        * Handle extended translation size for logical drives > 1Gb
+        */
+
+       if (capacity >= 0x200000) {
+               heads = 255;
+               sectors = 63;
+               tmp = heads*sectors;
+               cylinders = capacity;
+               sector_div(cylinders, tmp);
+       }
+
+       geom[0] = heads;
+       geom[1] = sectors;
+       geom[2] = cylinders;
+
+       return 0;
+}
+
+/**
  * megasas_service_aen -       Processes an event notification
  * @instance:                  Adapter soft state
  * @cmd:                       AEN command completed by the ISR
@@ -857,12 +1103,15 @@ static struct scsi_host_template megasas_template = {
        .module = THIS_MODULE,
        .name = "LSI Logic SAS based MegaRAID driver",
        .proc_name = "megaraid_sas",
+       .slave_configure = megasas_slave_configure,
        .queuecommand = megasas_queue_command,
        .eh_device_reset_handler = megasas_reset_device,
        .eh_bus_reset_handler = megasas_reset_bus_host,
        .eh_host_reset_handler = megasas_reset_bus_host,
        .eh_timed_out = megasas_reset_timer,
+       .bios_param = megasas_bios_param,
        .use_clustering = ENABLE_CLUSTERING,
+       .use_sg_chaining = ENABLE_SG_CHAINING,
 };
 
 /**
@@ -909,45 +1158,6 @@ megasas_complete_abort(struct megasas_instance *instance,
 }
 
 /**
- * megasas_unmap_sgbuf -       Unmap SG buffers
- * @instance:                  Adapter soft state
- * @cmd:                       Completed command
- */
-static void
-megasas_unmap_sgbuf(struct megasas_instance *instance, struct megasas_cmd *cmd)
-{
-       dma_addr_t buf_h;
-       u8 opcode;
-
-       if (cmd->scmd->use_sg) {
-               pci_unmap_sg(instance->pdev, cmd->scmd->request_buffer,
-                            cmd->scmd->use_sg, cmd->scmd->sc_data_direction);
-               return;
-       }
-
-       if (!cmd->scmd->request_bufflen)
-               return;
-
-       opcode = cmd->frame->hdr.cmd;
-
-       if ((opcode == MFI_CMD_LD_READ) || (opcode == MFI_CMD_LD_WRITE)) {
-               if (IS_DMA64)
-                       buf_h = cmd->frame->io.sgl.sge64[0].phys_addr;
-               else
-                       buf_h = cmd->frame->io.sgl.sge32[0].phys_addr;
-       } else {
-               if (IS_DMA64)
-                       buf_h = cmd->frame->pthru.sgl.sge64[0].phys_addr;
-               else
-                       buf_h = cmd->frame->pthru.sgl.sge32[0].phys_addr;
-       }
-
-       pci_unmap_single(instance->pdev, buf_h, cmd->scmd->request_bufflen,
-                        cmd->scmd->sc_data_direction);
-       return;
-}
-
-/**
  * megasas_complete_cmd -      Completes a command
  * @instance:                  Adapter soft state
  * @cmd:                       Command to be completed
@@ -963,11 +1173,9 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
 {
        int exception = 0;
        struct megasas_header *hdr = &cmd->frame->hdr;
-       unsigned long flags;
 
-       if (cmd->scmd) {
-               cmd->scmd->SCp.ptr = (char *)0;
-       }
+       if (cmd->scmd)
+               cmd->scmd->SCp.ptr = NULL;
 
        switch (hdr->cmd) {
 
@@ -985,20 +1193,6 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
                        break;
                }
 
-               /*
-                * Don't export physical disk devices to mid-layer.
-                */
-               if (!MEGASAS_IS_LOGICAL(cmd->scmd) &&
-                   (hdr->cmd_status == MFI_STAT_OK) &&
-                   (cmd->scmd->cmnd[0] == INQUIRY)) {
-
-                       if (((*(u8 *) cmd->scmd->request_buffer) & 0x1F) ==
-                           TYPE_DISK) {
-                               cmd->scmd->result = DID_BAD_TARGET << 16;
-                               exception = 1;
-                       }
-               }
-
        case MFI_CMD_LD_READ:
        case MFI_CMD_LD_WRITE:
 
@@ -1009,11 +1203,9 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
 
                if (exception) {
 
-                       spin_lock_irqsave(&instance->instance_lock, flags);
-                       instance->fw_outstanding--;
-                       spin_unlock_irqrestore(&instance->instance_lock, flags);
+                       atomic_dec(&instance->fw_outstanding);
 
-                       megasas_unmap_sgbuf(instance, cmd);
+                       scsi_dma_unmap(cmd->scmd);
                        cmd->scmd->scsi_done(cmd->scmd);
                        megasas_return_cmd(instance, cmd);
 
@@ -1059,11 +1251,9 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
                        break;
                }
 
-               spin_lock_irqsave(&instance->instance_lock, flags);
-               instance->fw_outstanding--;
-               spin_unlock_irqrestore(&instance->instance_lock, flags);
+               atomic_dec(&instance->fw_outstanding);
 
-               megasas_unmap_sgbuf(instance, cmd);
+               scsi_dma_unmap(cmd->scmd);
                cmd->scmd->scsi_done(cmd->scmd);
                megasas_return_cmd(instance, cmd);
 
@@ -1107,11 +1297,6 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
 static int
 megasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status)
 {
-       u32 producer;
-       u32 consumer;
-       u32 context;
-       struct megasas_cmd *cmd;
-
        /*
         * Check if it is our interrupt
         * Clear the interrupt 
@@ -1119,31 +1304,20 @@ megasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status)
        if(instance->instancet->clear_intr(instance->reg_set))
                return IRQ_NONE;
 
-       producer = *instance->producer;
-       consumer = *instance->consumer;
-
-       while (consumer != producer) {
-               context = instance->reply_queue[consumer];
-
-               cmd = instance->cmd_list[context];
-
-               megasas_complete_cmd(instance, cmd, alt_status);
-
-               consumer++;
-               if (consumer == (instance->max_fw_cmds + 1)) {
-                       consumer = 0;
-               }
-       }
-
-       *instance->consumer = producer;
-
+       if (instance->hw_crit_error)
+               goto out_done;
+        /*
+        * Schedule the tasklet for cmd completion
+        */
+       tasklet_schedule(&instance->isr_tasklet);
+out_done:
        return IRQ_HANDLED;
 }
 
 /**
  * megasas_isr - isr entry point
  */
-static irqreturn_t megasas_isr(int irq, void *devp, struct pt_regs *regs)
+static irqreturn_t megasas_isr(int irq, void *devp)
 {
        return megasas_deplete_reply_queue((struct megasas_instance *)devp,
                                           DID_OK);
@@ -1168,10 +1342,12 @@ megasas_transition_to_ready(struct megasas_instance* instance)
 
        fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK;
 
+       if (fw_state != MFI_STATE_READY)
+               printk(KERN_INFO "megasas: Waiting for FW to come to ready"
+                      " state\n");
+
        while (fw_state != MFI_STATE_READY) {
 
-               printk(KERN_INFO "megasas: Waiting for FW to come to ready"
-                      " state\n");
                switch (fw_state) {
 
                case MFI_STATE_FAULT:
@@ -1183,19 +1359,27 @@ megasas_transition_to_ready(struct megasas_instance* instance)
                        /*
                         * Set the CLR bit in inbound doorbell
                         */
-                       writel(MFI_INIT_CLEAR_HANDSHAKE,
+                       writel(MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG,
                                &instance->reg_set->inbound_doorbell);
 
                        max_wait = 2;
                        cur_state = MFI_STATE_WAIT_HANDSHAKE;
                        break;
 
+               case MFI_STATE_BOOT_MESSAGE_PENDING:
+                       writel(MFI_INIT_HOTPLUG,
+                               &instance->reg_set->inbound_doorbell);
+
+                       max_wait = 10;
+                       cur_state = MFI_STATE_BOOT_MESSAGE_PENDING;
+                       break;
+
                case MFI_STATE_OPERATIONAL:
                        /*
-                        * Bring it to READY state; assuming max wait 2 secs
+                        * Bring it to READY state; assuming max wait 10 secs
                         */
-                       megasas_disable_intr(instance->reg_set);
-                       writel(MFI_INIT_READY, &instance->reg_set->inbound_doorbell);
+                       instance->instancet->disable_intr(instance->reg_set);
+                       writel(MFI_RESET_FLAGS, &instance->reg_set->inbound_doorbell);
 
                        max_wait = 10;
                        cur_state = MFI_STATE_OPERATIONAL;
@@ -1262,6 +1446,7 @@ megasas_transition_to_ready(struct megasas_instance* instance)
                        return -ENODEV;
                }
        };
+       printk(KERN_INFO "megasas: FW now in Ready state\n");
 
        return 0;
 }
@@ -1291,7 +1476,7 @@ static void megasas_teardown_frame_pool(struct megasas_instance *instance)
                                      cmd->frame_phys_addr);
 
                if (cmd->sense)
-                       pci_pool_free(instance->sense_dma_pool, cmd->frame,
+                       pci_pool_free(instance->sense_dma_pool, cmd->sense,
                                      cmd->sense_phys_addr);
        }
 
@@ -1453,15 +1638,13 @@ static int megasas_alloc_cmds(struct megasas_instance *instance)
         * Allocate the dynamic array first and then allocate individual
         * commands.
         */
-       instance->cmd_list = kmalloc(sizeof(struct megasas_cmd *) * max_cmd,
-                                    GFP_KERNEL);
+       instance->cmd_list = kcalloc(max_cmd, sizeof(struct megasas_cmd*), GFP_KERNEL);
 
        if (!instance->cmd_list) {
                printk(KERN_DEBUG "megasas: out of memory\n");
                return -ENOMEM;
        }
 
-       memset(instance->cmd_list, 0, sizeof(struct megasas_cmd *) * max_cmd);
 
        for (i = 0; i < max_cmd; i++) {
                instance->cmd_list[i] = kmalloc(sizeof(struct megasas_cmd),
@@ -1567,21 +1750,71 @@ megasas_get_ctrl_info(struct megasas_instance *instance,
 }
 
 /**
- * megasas_init_mfi -  Initializes the FW
- * @instance:          Adapter soft state
+ * megasas_complete_cmd_dpc     -      Returns FW's controller structure
+ * @instance_addr:                     Address of adapter soft state
  *
- * This is the main function for initializing MFI firmware.
+ * Tasklet to complete cmds
  */
-static int megasas_init_mfi(struct megasas_instance *instance)
+static void megasas_complete_cmd_dpc(unsigned long instance_addr)
 {
-       u32 context_sz;
-       u32 reply_q_sz;
-       u32 max_sectors_1;
-       u32 max_sectors_2;
-       struct megasas_register_set __iomem *reg_set;
+       u32 producer;
+       u32 consumer;
+       u32 context;
+       struct megasas_cmd *cmd;
+       struct megasas_instance *instance = (struct megasas_instance *)instance_addr;
+       unsigned long flags;
+
+       /* If we have already declared adapter dead, donot complete cmds */
+       if (instance->hw_crit_error)
+               return;
+
+       producer = *instance->producer;
+       consumer = *instance->consumer;
+
+       while (consumer != producer) {
+               context = instance->reply_queue[consumer];
+
+               cmd = instance->cmd_list[context];
+
+               megasas_complete_cmd(instance, cmd, DID_OK);
+
+               consumer++;
+               if (consumer == (instance->max_fw_cmds + 1)) {
+                       consumer = 0;
+               }
+       }
+
+       *instance->consumer = producer;
+
+       /*
+        * Check if we can restore can_queue
+        */
+       if (instance->flag & MEGASAS_FW_BUSY
+               && time_after(jiffies, instance->last_time + 5 * HZ)
+               && atomic_read(&instance->fw_outstanding) < 17) {
+
+               spin_lock_irqsave(instance->host->host_lock, flags);
+               instance->flag &= ~MEGASAS_FW_BUSY;
+               instance->host->can_queue =
+                               instance->max_fw_cmds - MEGASAS_INT_CMDS;
+
+               spin_unlock_irqrestore(instance->host->host_lock, flags);
+       }
+
+}
+
+/**
+ * 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_ctrl_info *ctrl_info;
 
        struct megasas_init_frame *init_frame;
        struct megasas_init_queue_info *initq_info;
@@ -1589,6 +1822,78 @@ static int megasas_init_mfi(struct megasas_instance *instance)
        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
+ *
+ * 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);
@@ -1607,7 +1912,17 @@ static int megasas_init_mfi(struct megasas_instance *instance)
 
        reg_set = instance->reg_set;
 
-       instance->instancet = &megasas_instance_template_xscale;
+       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
@@ -1619,6 +1934,12 @@ static int megasas_init_mfi(struct megasas_instance *instance)
         * 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;
        /*
@@ -1648,47 +1969,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);
-
-       /*
-        * 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);
 
@@ -1701,24 +1983,32 @@ static int megasas_init_mfi(struct megasas_instance *instance)
         * 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;
 
-               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;
+               tmp_sectors = min_t(u32, 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:
-       megasas_return_cmd(instance, cmd);
 
        pci_free_consistent(instance->pdev, reply_q_sz,
                            instance->reply_queue, instance->reply_queue_h);
@@ -1983,6 +2273,7 @@ static int megasas_io_attach(struct megasas_instance *instance)
        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
@@ -1999,6 +2290,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
@@ -2032,19 +2345,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));
@@ -2087,13 +2389,14 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
         */
        INIT_LIST_HEAD(&instance->cmd_pool);
 
+       atomic_set(&instance->fw_outstanding,0);
+
        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);
+       mutex_init(&instance->aen_mutex);
        sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS);
 
        /*
@@ -2104,6 +2407,10 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        instance->unique_id = pdev->bus->number << 8 | pdev->devfn;
        instance->init_id = MEGASAS_DEFAULT_INIT_ID;
 
+       megasas_dbg_lvl = 0;
+       instance->flag = 0;
+       instance->last_time = 0;
+
        /*
         * Initialize MFI Firmware
         */
@@ -2113,7 +2420,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        /*
         * Register IRQ
         */
-       if (request_irq(pdev->irq, megasas_isr, SA_SHIRQ, "megasas", instance)) {
+       if (request_irq(pdev->irq, megasas_isr, IRQF_SHARED, "megasas", instance)) {
                printk(KERN_DEBUG "megasas: Failed to register IRQ\n");
                goto fail_irq;
        }
@@ -2156,7 +2463,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        megasas_mgmt_info.max_index--;
 
        pci_set_drvdata(pdev, NULL);
-       megasas_disable_intr(instance->reg_set);
+       instance->instancet->disable_intr(instance->reg_set);
        free_irq(instance->pdev->irq, instance);
 
        megasas_release_mfi(instance);
@@ -2221,8 +2528,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;
@@ -2245,7 +2554,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);
 
@@ -2255,6 +2564,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
  */
@@ -2269,7 +2703,8 @@ 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);
 
        /*
         * Take the instance off the instance array. Note that we will not
@@ -2286,7 +2721,7 @@ static void megasas_detach_one(struct pci_dev *pdev)
 
        pci_set_drvdata(instance->pdev, NULL);
 
-       megasas_disable_intr(instance->reg_set);
+       instance->instancet->disable_intr(instance->reg_set);
 
        free_irq(instance->pdev->irq, instance);
 
@@ -2390,6 +2825,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
        void *sense = NULL;
        dma_addr_t sense_handle;
        u32 *sense_ptr;
+       unsigned long *sense_buff;
 
        memset(kbuff_arr, 0, sizeof(kbuff_arr));
 
@@ -2429,9 +2865,9 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
         * For each user buffer, create a mirror buffer and copy in
         */
        for (i = 0; i < ioc->sge_count; i++) {
-               kbuff_arr[i] = pci_alloc_consistent(instance->pdev,
+               kbuff_arr[i] = dma_alloc_coherent(&instance->pdev->dev,
                                                    ioc->sgl[i].iov_len,
-                                                   &buf_handle);
+                                                   &buf_handle, GFP_KERNEL);
                if (!kbuff_arr[i]) {
                        printk(KERN_DEBUG "megasas: Failed to alloc "
                               "kernel SGL buffer for IOCTL \n");
@@ -2458,8 +2894,8 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
        }
 
        if (ioc->sense_len) {
-               sense = pci_alloc_consistent(instance->pdev, ioc->sense_len,
-                                            &sense_handle);
+               sense = dma_alloc_coherent(&instance->pdev->dev, ioc->sense_len,
+                                            &sense_handle, GFP_KERNEL);
                if (!sense) {
                        error = -ENOMEM;
                        goto out;
@@ -2494,14 +2930,16 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
         */
        if (ioc->sense_len) {
                /*
-                * sense_ptr points to the location that has the user
+                * sense_buff points to the location that has the user
                 * sense buffer address
                 */
-               sense_ptr = (u32 *) ((unsigned long)ioc->frame.raw +
-                                    ioc->sense_off);
+               sense_buff = (unsigned long *) ((unsigned long)ioc->frame.raw +
+                                                               ioc->sense_off);
 
-               if (copy_to_user((void __user *)((unsigned long)(*sense_ptr)),
-                                sense, ioc->sense_len)) {
+               if (copy_to_user((void __user *)(unsigned long)(*sense_buff),
+                               sense, ioc->sense_len)) {
+                       printk(KERN_ERR "megasas: Failed to copy out to user "
+                                       "sense data\n");
                        error = -EFAULT;
                        goto out;
                }
@@ -2518,12 +2956,12 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
 
       out:
        if (sense) {
-               pci_free_consistent(instance->pdev, ioc->sense_len,
+               dma_free_coherent(&instance->pdev->dev, ioc->sense_len,
                                    sense, sense_handle);
        }
 
        for (i = 0; i < ioc->sge_count && kbuff_arr[i]; i++) {
-               pci_free_consistent(instance->pdev,
+               dma_free_coherent(&instance->pdev->dev,
                                    kern_sge32[i].length,
                                    kbuff_arr[i], kern_sge32[i].phys_addr);
        }
@@ -2604,10 +3042,10 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg)
        if (!instance)
                return -ENODEV;
 
-       down(&instance->aen_mutex);
+       mutex_lock(&instance->aen_mutex);
        error = megasas_register_aen(instance, aen.seq_num,
                                     aen.class_locale_word);
-       up(&instance->aen_mutex);
+       mutex_unlock(&instance->aen_mutex);
        return error;
 }
 
@@ -2638,7 +3076,8 @@ static int megasas_mgmt_compat_ioctl_fw(struct file *file, unsigned long arg)
        int i;
        int error = 0;
 
-       clear_user(ioc, sizeof(*ioc));
+       if (clear_user(ioc, sizeof(*ioc)))
+               return -EFAULT;
 
        if (copy_in_user(&ioc->host_no, &cioc->host_no, sizeof(u16)) ||
            copy_in_user(&ioc->sgl_off, &cioc->sgl_off, sizeof(u32)) ||
@@ -2686,7 +3125,7 @@ megasas_mgmt_compat_ioctl(struct file *file, unsigned int cmd,
 /*
  * File operations structure for management interface
  */
-static struct file_operations megasas_mgmt_fops = {
+static const struct file_operations megasas_mgmt_fops = {
        .owner = THIS_MODULE,
        .open = megasas_mgmt_open,
        .release = megasas_mgmt_release,
@@ -2706,6 +3145,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,
 };
 
@@ -2730,6 +3171,26 @@ megasas_sysfs_show_release_date(struct device_driver *dd, char *buf)
 static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date,
                   NULL);
 
+static ssize_t
+megasas_sysfs_show_dbg_lvl(struct device_driver *dd, char *buf)
+{
+       return sprintf(buf,"%u",megasas_dbg_lvl);
+}
+
+static ssize_t
+megasas_sysfs_set_dbg_lvl(struct device_driver *dd, const char *buf, size_t count)
+{
+       int retval = count;
+       if(sscanf(buf,"%u",&megasas_dbg_lvl)<1){
+               printk(KERN_ERR "megasas: could not set dbg_lvl\n");
+               retval = -EINVAL;
+       }
+       return retval;
+}
+
+static DRIVER_ATTR(dbg_lvl, S_IRUGO|S_IWUGO, megasas_sysfs_show_dbg_lvl,
+                  megasas_sysfs_set_dbg_lvl);
+
 /**
  * megasas_init - Driver load entry point
  */
@@ -2760,18 +3221,37 @@ static int __init megasas_init(void)
        /*
         * Register ourselves as PCI hotplug module
         */
-       rval = pci_module_init(&megasas_pci_driver);
+       rval = pci_register_driver(&megasas_pci_driver);
 
        if (rval) {
                printk(KERN_DEBUG "megasas: PCI hotplug regisration failed \n");
-               unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl");
-       }
-
-       driver_create_file(&megasas_pci_driver.driver, &driver_attr_version);
-       driver_create_file(&megasas_pci_driver.driver,
-                          &driver_attr_release_date);
+               goto err_pcidrv;
+       }
+
+       rval = driver_create_file(&megasas_pci_driver.driver,
+                                 &driver_attr_version);
+       if (rval)
+               goto err_dcf_attr_ver;
+       rval = driver_create_file(&megasas_pci_driver.driver,
+                                 &driver_attr_release_date);
+       if (rval)
+               goto err_dcf_rel_date;
+       rval = driver_create_file(&megasas_pci_driver.driver,
+                                 &driver_attr_dbg_lvl);
+       if (rval)
+               goto err_dcf_dbg_lvl;
 
        return rval;
+err_dcf_dbg_lvl:
+       driver_remove_file(&megasas_pci_driver.driver,
+                          &driver_attr_release_date);
+err_dcf_rel_date:
+       driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
+err_dcf_attr_ver:
+       pci_unregister_driver(&megasas_pci_driver);
+err_pcidrv:
+       unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl");
+       return rval;
 }
 
 /**
@@ -2779,9 +3259,11 @@ static int __init megasas_init(void)
  */
 static void __exit megasas_exit(void)
 {
-       driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
+       driver_remove_file(&megasas_pci_driver.driver,
+                          &driver_attr_dbg_lvl);
        driver_remove_file(&megasas_pci_driver.driver,
                           &driver_attr_release_date);
+       driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
 
        pci_unregister_driver(&megasas_pci_driver);
        unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl");