[SCSI] bnx2fc: Broadcom FCoE offload driver
[linux-2.6.git] / drivers / scsi / hpsa.c
index 44f81a0..c30591f 100644 (file)
@@ -31,7 +31,6 @@
 #include <linux/seq_file.h>
 #include <linux/init.h>
 #include <linux/spinlock.h>
-#include <linux/smp_lock.h>
 #include <linux/compat.h>
 #include <linux/blktrace_api.h>
 #include <linux/uaccess.h>
@@ -75,6 +74,10 @@ static int hpsa_allow_any;
 module_param(hpsa_allow_any, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(hpsa_allow_any,
                "Allow hpsa driver to access unknown HP Smart Array hardware");
+static int hpsa_simple_mode;
+module_param(hpsa_simple_mode, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(hpsa_simple_mode,
+       "Use 'simple mode' rather than 'performant mode'");
 
 /* define the PCI info for the cards we can control */
 static const struct pci_device_id hpsa_pci_device_id[] = {
@@ -91,11 +94,7 @@ static const struct pci_device_id hpsa_pci_device_id[] = {
        {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x3252},
        {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x3253},
        {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSE,     0x103C, 0x3254},
-#define PCI_DEVICE_ID_HP_CISSF 0x333f
-       {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSF,     0x103C, 0x333F},
-       {PCI_VENDOR_ID_HP,     PCI_ANY_ID,             PCI_ANY_ID, PCI_ANY_ID,
-               PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0},
-       {PCI_VENDOR_ID_COMPAQ,     PCI_ANY_ID,             PCI_ANY_ID, PCI_ANY_ID,
+       {PCI_VENDOR_ID_HP,     PCI_ANY_ID,      PCI_ANY_ID, PCI_ANY_ID,
                PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0},
        {0,}
 };
@@ -114,8 +113,6 @@ static struct board_type products[] = {
        {0x3249103C, "Smart Array P812", &SA5_access},
        {0x324a103C, "Smart Array P712m", &SA5_access},
        {0x324b103C, "Smart Array P711m", &SA5_access},
-       {0x3233103C, "StorageWorks P1210m", &SA5_access},
-       {0x333F103C, "StorageWorks P1210m", &SA5_access},
        {0x3250103C, "Smart Array", &SA5_access},
        {0x3250113C, "Smart Array", &SA5_access},
        {0x3250123C, "Smart Array", &SA5_access},
@@ -126,7 +123,8 @@ static struct board_type products[] = {
 
 static int number_of_controllers;
 
-static irqreturn_t do_hpsa_intr(int irq, void *dev_id);
+static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id);
+static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id);
 static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg);
 static void start_io(struct ctlr_info *h);
 
@@ -142,8 +140,7 @@ static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
        void *buff, size_t size, u8 page_code, unsigned char *scsi3addr,
        int cmd_type);
 
-static int hpsa_scsi_queue_command(struct scsi_cmnd *cmd,
-               void (*done)(struct scsi_cmnd *));
+static int hpsa_scsi_queue_command(struct Scsi_Host *h, struct scsi_cmnd *cmd);
 static void hpsa_scan_start(struct Scsi_Host *);
 static int hpsa_scan_finished(struct Scsi_Host *sh,
        unsigned long elapsed_time);
@@ -162,6 +159,10 @@ static ssize_t unique_id_show(struct device *dev,
        struct device_attribute *attr, char *buf);
 static ssize_t host_show_firmware_revision(struct device *dev,
             struct device_attribute *attr, char *buf);
+static ssize_t host_show_commands_outstanding(struct device *dev,
+            struct device_attribute *attr, char *buf);
+static ssize_t host_show_transport_mode(struct device *dev,
+       struct device_attribute *attr, char *buf);
 static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno);
 static ssize_t host_store_rescan(struct device *dev,
         struct device_attribute *attr, const char *buf, size_t count);
@@ -174,6 +175,16 @@ static void calc_bucket_map(int *bucket, int num_buckets,
        int nsgs, int *bucket_map);
 static __devinit void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h);
 static inline u32 next_command(struct ctlr_info *h);
+static int __devinit hpsa_find_cfg_addrs(struct pci_dev *pdev,
+       void __iomem *vaddr, u32 *cfg_base_addr, u64 *cfg_base_addr_index,
+       u64 *cfg_offset);
+static int __devinit hpsa_pci_find_memory_BAR(struct pci_dev *pdev,
+       unsigned long *memory_bar);
+static int __devinit hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id);
+static int __devinit hpsa_wait_for_board_state(struct pci_dev *pdev,
+       void __iomem *vaddr, int wait_for_ready);
+#define BOARD_NOT_READY 0
+#define BOARD_READY 1
 
 static DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL);
 static DEVICE_ATTR(lunid, S_IRUGO, lunid_show, NULL);
@@ -181,6 +192,10 @@ static DEVICE_ATTR(unique_id, S_IRUGO, unique_id_show, NULL);
 static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan);
 static DEVICE_ATTR(firmware_revision, S_IRUGO,
        host_show_firmware_revision, NULL);
+static DEVICE_ATTR(commands_outstanding, S_IRUGO,
+       host_show_commands_outstanding, NULL);
+static DEVICE_ATTR(transport_mode, S_IRUGO,
+       host_show_transport_mode, NULL);
 
 static struct device_attribute *hpsa_sdev_attrs[] = {
        &dev_attr_raid_level,
@@ -192,6 +207,8 @@ static struct device_attribute *hpsa_sdev_attrs[] = {
 static struct device_attribute *hpsa_shost_attrs[] = {
        &dev_attr_rescan,
        &dev_attr_firmware_revision,
+       &dev_attr_commands_outstanding,
+       &dev_attr_transport_mode,
        NULL,
 };
 
@@ -292,17 +309,38 @@ static ssize_t host_show_firmware_revision(struct device *dev,
                fwrev[0], fwrev[1], fwrev[2], fwrev[3]);
 }
 
+static ssize_t host_show_commands_outstanding(struct device *dev,
+            struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(dev);
+       struct ctlr_info *h = shost_to_hba(shost);
+
+       return snprintf(buf, 20, "%d\n", h->commands_outstanding);
+}
+
+static ssize_t host_show_transport_mode(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct ctlr_info *h;
+       struct Scsi_Host *shost = class_to_shost(dev);
+
+       h = shost_to_hba(shost);
+       return snprintf(buf, 20, "%s\n",
+               h->transMethod & CFGTBL_Trans_Performant ?
+                       "performant" : "simple");
+}
+
 /* Enqueuing and dequeuing functions for cmdlists. */
-static inline void addQ(struct hlist_head *list, struct CommandList *c)
+static inline void addQ(struct list_head *list, struct CommandList *c)
 {
-       hlist_add_head(&c->list, list);
+       list_add_tail(&c->list, list);
 }
 
 static inline u32 next_command(struct ctlr_info *h)
 {
        u32 a;
 
-       if (unlikely(h->transMethod != CFGTBL_Trans_Performant))
+       if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant)))
                return h->access.command_completed(h);
 
        if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) {
@@ -326,7 +364,7 @@ static inline u32 next_command(struct ctlr_info *h)
  */
 static void set_performant_mode(struct ctlr_info *h, struct CommandList *c)
 {
-       if (likely(h->transMethod == CFGTBL_Trans_Performant))
+       if (likely(h->transMethod & CFGTBL_Trans_Performant))
                c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1);
 }
 
@@ -345,9 +383,9 @@ static void enqueue_cmd_and_start_io(struct ctlr_info *h,
 
 static inline void removeQ(struct CommandList *c)
 {
-       if (WARN_ON(hlist_unhashed(&c->list)))
+       if (WARN_ON(list_empty(&c->list)))
                return;
-       hlist_del_init(&c->list);
+       list_del_init(&c->list);
 }
 
 static inline int is_hba_lunid(unsigned char scsi3addr[])
@@ -642,11 +680,6 @@ static void fixup_botched_add(struct ctlr_info *h,
 static inline int device_is_the_same(struct hpsa_scsi_dev_t *dev1,
        struct hpsa_scsi_dev_t *dev2)
 {
-       if ((is_logical_dev_addr_mode(dev1->scsi3addr) ||
-               (dev1->lun != -1 && dev2->lun != -1)) &&
-               dev1->devtype != 0x0C)
-               return (memcmp(dev1, dev2, sizeof(*dev1)) == 0);
-
        /* we compare everything except lun and target as these
         * are not yet assigned.  Compare parts likely
         * to differ first
@@ -661,12 +694,8 @@ static inline int device_is_the_same(struct hpsa_scsi_dev_t *dev1,
                return 0;
        if (memcmp(dev1->vendor, dev2->vendor, sizeof(dev1->vendor)) != 0)
                return 0;
-       if (memcmp(dev1->revision, dev2->revision, sizeof(dev1->revision)) != 0)
-               return 0;
        if (dev1->devtype != dev2->devtype)
                return 0;
-       if (dev1->raid_level != dev2->raid_level)
-               return 0;
        if (dev1->bus != dev2->bus)
                return 0;
        return 1;
@@ -1140,6 +1169,10 @@ static void complete_scsi_command(struct CommandList *cp,
                cmd->result = DID_TIME_OUT << 16;
                dev_warn(&h->pdev->dev, "cp %p timedout\n", cp);
                break;
+       case CMD_UNABORTABLE:
+               cmd->result = DID_ERROR << 16;
+               dev_warn(&h->pdev->dev, "Command unabortable\n");
+               break;
        default:
                cmd->result = DID_ERROR << 16;
                dev_warn(&h->pdev->dev, "cp %p returned unknown status %x\n",
@@ -1170,7 +1203,7 @@ static int hpsa_scsi_detect(struct ctlr_info *h)
        sh->sg_tablesize = h->maxsgentries;
        h->scsi_host = sh;
        sh->hostdata[0] = (unsigned long) h;
-       sh->irq = h->intr[PERF_MODE_INT];
+       sh->irq = h->intr[h->intr_mode];
        sh->unique_id = sh->irq;
        error = scsi_add_host(sh, &h->pdev->dev);
        if (error)
@@ -1305,6 +1338,9 @@ static void hpsa_scsi_interpret_error(struct CommandList *cp)
        case CMD_TIMEOUT:
                dev_warn(d, "cp %p timed out\n", cp);
                break;
+       case CMD_UNABORTABLE:
+               dev_warn(d, "Command unabortable\n");
+               break;
        default:
                dev_warn(d, "cp %p returned unknown status %x\n", cp,
                                ei->CommandStatus);
@@ -1478,8 +1514,6 @@ static int hpsa_update_device_info(struct ctlr_info *h,
                sizeof(this_device->vendor));
        memcpy(this_device->model, &inq_buff[16],
                sizeof(this_device->model));
-       memcpy(this_device->revision, &inq_buff[32],
-               sizeof(this_device->revision));
        memset(this_device->device_id, 0,
                sizeof(this_device->device_id));
        hpsa_get_device_id(h, scsi3addr, this_device->device_id,
@@ -1607,6 +1641,8 @@ static int add_msa2xxx_enclosure_device(struct ctlr_info *h,
        if (lun == 0) /* if lun is 0, then obviously we have a lun 0. */
                return 0;
 
+       memset(scsi3addr, 0, 8);
+       scsi3addr[3] = target;
        if (is_hba_lunid(scsi3addr))
                return 0; /* Don't add the RAID controller here. */
 
@@ -1621,8 +1657,6 @@ static int add_msa2xxx_enclosure_device(struct ctlr_info *h,
                return 0;
        }
 
-       memset(scsi3addr, 0, 8);
-       scsi3addr[3] = target;
        if (hpsa_update_device_info(h, scsi3addr, this_device))
                return 0;
        (*nmsa2xxx_enclosures)++;
@@ -1919,7 +1953,7 @@ sglist_finished:
 }
 
 
-static int hpsa_scsi_queue_command(struct scsi_cmnd *cmd,
+static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd,
        void (*done)(struct scsi_cmnd *))
 {
        struct ctlr_info *h;
@@ -2013,6 +2047,8 @@ static int hpsa_scsi_queue_command(struct scsi_cmnd *cmd,
        return 0;
 }
 
+static DEF_SCSI_QCMD(hpsa_scsi_queue_command)
+
 static void hpsa_scan_start(struct Scsi_Host *sh)
 {
        struct ctlr_info *h = shost_to_hba(sh);
@@ -2209,7 +2245,7 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h)
 
        c->cmdindex = i;
 
-       INIT_HLIST_NODE(&c->list);
+       INIT_LIST_HEAD(&c->list);
        c->busaddr = (u32) cmd_dma_handle;
        temp64.val = (u64) err_dma_handle;
        c->ErrDesc.Addr.lower = temp64.val32.lower;
@@ -2247,7 +2283,7 @@ static struct CommandList *cmd_special_alloc(struct ctlr_info *h)
        }
        memset(c->err_info, 0, sizeof(*c->err_info));
 
-       INIT_HLIST_NODE(&c->list);
+       INIT_LIST_HEAD(&c->list);
        c->busaddr = (u32) cmd_dma_handle;
        temp64.val = (u64) err_dma_handle;
        c->ErrDesc.Addr.lower = temp64.val32.lower;
@@ -2277,7 +2313,7 @@ static void cmd_special_free(struct ctlr_info *h, struct CommandList *c)
        pci_free_consistent(h->pdev, sizeof(*c->err_info),
                            c->err_info, (dma_addr_t) temp64.val);
        pci_free_consistent(h->pdev, sizeof(*c),
-                           c, (dma_addr_t) c->busaddr);
+                           c, (dma_addr_t) (c->busaddr & DIRECT_LOOKUP_MASK));
 }
 
 #ifdef CONFIG_COMPAT
@@ -2291,6 +2327,7 @@ static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, void *arg)
        int err;
        u32 cp;
 
+       memset(&arg64, 0, sizeof(arg64));
        err = 0;
        err |= copy_from_user(&arg64.LUN_info, &arg32->LUN_info,
                           sizeof(arg64.LUN_info));
@@ -2327,6 +2364,7 @@ static int hpsa_ioctl32_big_passthru(struct scsi_device *dev,
        int err;
        u32 cp;
 
+       memset(&arg64, 0, sizeof(arg64));
        err = 0;
        err |= copy_from_user(&arg64.LUN_info, &arg32->LUN_info,
                           sizeof(arg64.LUN_info));
@@ -2443,15 +2481,17 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
                buff = kmalloc(iocommand.buf_size, GFP_KERNEL);
                if (buff == NULL)
                        return -EFAULT;
-       }
-       if (iocommand.Request.Type.Direction == XFER_WRITE) {
-               /* Copy the data into the buffer we created */
-               if (copy_from_user(buff, iocommand.buf, iocommand.buf_size)) {
-                       kfree(buff);
-                       return -EFAULT;
+               if (iocommand.Request.Type.Direction == XFER_WRITE) {
+                       /* Copy the data into the buffer we created */
+                       if (copy_from_user(buff, iocommand.buf,
+                               iocommand.buf_size)) {
+                               kfree(buff);
+                               return -EFAULT;
+                       }
+               } else {
+                       memset(buff, 0, iocommand.buf_size);
                }
-       } else
-               memset(buff, 0, iocommand.buf_size);
+       }
        c = cmd_special_alloc(h);
        if (c == NULL) {
                kfree(buff);
@@ -2497,8 +2537,8 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
                cmd_special_free(h, c);
                return -EFAULT;
        }
-
-       if (iocommand.Request.Type.Direction == XFER_READ) {
+       if (iocommand.Request.Type.Direction == XFER_READ &&
+               iocommand.buf_size > 0) {
                /* Copy the data out of the buffer we created */
                if (copy_to_user(iocommand.buf, buff, iocommand.buf_size)) {
                        kfree(buff);
@@ -2591,14 +2631,7 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
        }
        c->cmd_type = CMD_IOCTL_PEND;
        c->Header.ReplyQueue = 0;
-
-       if (ioc->buf_size > 0) {
-               c->Header.SGList = sg_used;
-               c->Header.SGTotal = sg_used;
-       } else {
-               c->Header.SGList = 0;
-               c->Header.SGTotal = 0;
-       }
+       c->Header.SGList = c->Header.SGTotal = sg_used;
        memcpy(&c->Header.LUN, &ioc->LUN_info, sizeof(c->Header.LUN));
        c->Header.Tag.lower = c->busaddr;
        memcpy(&c->Request, &ioc->Request, sizeof(c->Request));
@@ -2615,7 +2648,8 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
                }
        }
        hpsa_scsi_do_simple_cmd_core(h, c);
-       hpsa_pci_unmap(h->pdev, c, sg_used, PCI_DMA_BIDIRECTIONAL);
+       if (sg_used)
+               hpsa_pci_unmap(h->pdev, c, sg_used, PCI_DMA_BIDIRECTIONAL);
        check_ioctl_unit_attention(h, c);
        /* Copy the error information out */
        memcpy(&ioc->error_info, c->err_info, sizeof(ioc->error_info));
@@ -2624,7 +2658,7 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
                status = -EFAULT;
                goto cleanup1;
        }
-       if (ioc->Request.Type.Direction == XFER_READ) {
+       if (ioc->Request.Type.Direction == XFER_READ && ioc->buf_size > 0) {
                /* Copy the data out of the buffer we created */
                BYTE __user *ptr = ioc->buf;
                for (i = 0; i < sg_used; i++) {
@@ -2820,8 +2854,8 @@ static void start_io(struct ctlr_info *h)
 {
        struct CommandList *c;
 
-       while (!hlist_empty(&h->reqQ)) {
-               c = hlist_entry(h->reqQ.first, struct CommandList, list);
+       while (!list_empty(&h->reqQ)) {
+               c = list_entry(h->reqQ.next, struct CommandList, list);
                /* can't do anything if fifo is full */
                if ((h->access.fifo_full(h))) {
                        dev_warn(&h->pdev->dev, "fifo full\n");
@@ -2852,9 +2886,8 @@ static inline bool interrupt_pending(struct ctlr_info *h)
 
 static inline long interrupt_not_for_us(struct ctlr_info *h)
 {
-       return !(h->msi_vector || h->msix_vector) &&
-               ((h->access.intr_pending(h) == 0) ||
-               (h->interrupts_enabled == 0));
+       return (h->access.intr_pending(h) == 0) ||
+               (h->interrupts_enabled == 0);
 }
 
 static inline int bad_tag(struct ctlr_info *h, u32 tag_index,
@@ -2878,20 +2911,22 @@ static inline void finish_cmd(struct CommandList *c, u32 raw_tag)
 
 static inline u32 hpsa_tag_contains_index(u32 tag)
 {
-#define DIRECT_LOOKUP_BIT 0x10
        return tag & DIRECT_LOOKUP_BIT;
 }
 
 static inline u32 hpsa_tag_to_index(u32 tag)
 {
-#define DIRECT_LOOKUP_SHIFT 5
        return tag >> DIRECT_LOOKUP_SHIFT;
 }
 
-static inline u32 hpsa_tag_discard_error_bits(u32 tag)
+
+static inline u32 hpsa_tag_discard_error_bits(struct ctlr_info *h, u32 tag)
 {
-#define HPSA_ERROR_BITS 0x03
-       return tag & ~HPSA_ERROR_BITS;
+#define HPSA_PERF_ERROR_BITS ((1 << DIRECT_LOOKUP_SHIFT) - 1)
+#define HPSA_SIMPLE_ERROR_BITS 0x03
+       if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant)))
+               return tag & ~HPSA_SIMPLE_ERROR_BITS;
+       return tag & ~HPSA_PERF_ERROR_BITS;
 }
 
 /* process completion of an indexed ("direct lookup") command */
@@ -2915,10 +2950,9 @@ static inline u32 process_nonindexed_cmd(struct ctlr_info *h,
 {
        u32 tag;
        struct CommandList *c = NULL;
-       struct hlist_node *tmp;
 
-       tag = hpsa_tag_discard_error_bits(raw_tag);
-       hlist_for_each_entry(c, tmp, &h->cmpQ, list) {
+       tag = hpsa_tag_discard_error_bits(h, raw_tag);
+       list_for_each_entry(c, &h->cmpQ, list) {
                if ((c->busaddr & 0xFFFFFFE0) == (tag & 0xFFFFFFE0)) {
                        finish_cmd(c, raw_tag);
                        return next_command(h);
@@ -2928,7 +2962,7 @@ static inline u32 process_nonindexed_cmd(struct ctlr_info *h,
        return next_command(h);
 }
 
-static irqreturn_t do_hpsa_intr(int irq, void *dev_id)
+static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id)
 {
        struct ctlr_info *h = dev_id;
        unsigned long flags;
@@ -2937,6 +2971,26 @@ static irqreturn_t do_hpsa_intr(int irq, void *dev_id)
        if (interrupt_not_for_us(h))
                return IRQ_NONE;
        spin_lock_irqsave(&h->lock, flags);
+       while (interrupt_pending(h)) {
+               raw_tag = get_next_completion(h);
+               while (raw_tag != FIFO_EMPTY) {
+                       if (hpsa_tag_contains_index(raw_tag))
+                               raw_tag = process_indexed_cmd(h, raw_tag);
+                       else
+                               raw_tag = process_nonindexed_cmd(h, raw_tag);
+               }
+       }
+       spin_unlock_irqrestore(&h->lock, flags);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id)
+{
+       struct ctlr_info *h = dev_id;
+       unsigned long flags;
+       u32 raw_tag;
+
+       spin_lock_irqsave(&h->lock, flags);
        raw_tag = get_next_completion(h);
        while (raw_tag != FIFO_EMPTY) {
                if (hpsa_tag_contains_index(raw_tag))
@@ -2948,7 +3002,10 @@ static irqreturn_t do_hpsa_intr(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-/* Send a message CDB to the firmware. */
+/* Send a message CDB to the firmware. Careful, this only works
+ * in simple mode, not performant mode due to the tag lookup.
+ * We only ever use this immediately after a controller reset.
+ */
 static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
                                                unsigned char type)
 {
@@ -3014,7 +3071,7 @@ static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
 
        for (i = 0; i < HPSA_MSG_SEND_RETRY_LIMIT; i++) {
                tag = readl(vaddr + SA5_REPLY_PORT_OFFSET);
-               if (hpsa_tag_discard_error_bits(tag) == paddr32)
+               if ((tag & ~HPSA_SIMPLE_ERROR_BITS) == paddr32)
                        break;
                msleep(HPSA_MSG_SEND_RETRY_INTERVAL_MSECS);
        }
@@ -3046,114 +3103,182 @@ static __devinit int hpsa_message(struct pci_dev *pdev, unsigned char opcode,
 #define hpsa_soft_reset_controller(p) hpsa_message(p, 1, 0)
 #define hpsa_noop(p) hpsa_message(p, 3, 0)
 
-static __devinit int hpsa_reset_msi(struct pci_dev *pdev)
+static int hpsa_controller_hard_reset(struct pci_dev *pdev,
+       void * __iomem vaddr, bool use_doorbell)
 {
-/* the #defines are stolen from drivers/pci/msi.h. */
-#define msi_control_reg(base)          (base + PCI_MSI_FLAGS)
-#define PCI_MSIX_FLAGS_ENABLE          (1 << 15)
-
+       u16 pmcsr;
        int pos;
-       u16 control = 0;
-
-       pos = pci_find_capability(pdev, PCI_CAP_ID_MSI);
-       if (pos) {
-               pci_read_config_word(pdev, msi_control_reg(pos), &control);
-               if (control & PCI_MSI_FLAGS_ENABLE) {
-                       dev_info(&pdev->dev, "resetting MSI\n");
-                       pci_write_config_word(pdev, msi_control_reg(pos),
-                                       control & ~PCI_MSI_FLAGS_ENABLE);
-               }
-       }
 
-       pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
-       if (pos) {
-               pci_read_config_word(pdev, msi_control_reg(pos), &control);
-               if (control & PCI_MSIX_FLAGS_ENABLE) {
-                       dev_info(&pdev->dev, "resetting MSI-X\n");
-                       pci_write_config_word(pdev, msi_control_reg(pos),
-                                       control & ~PCI_MSIX_FLAGS_ENABLE);
+       if (use_doorbell) {
+               /* For everything after the P600, the PCI power state method
+                * of resetting the controller doesn't work, so we have this
+                * other way using the doorbell register.
+                */
+               dev_info(&pdev->dev, "using doorbell to reset controller\n");
+               writel(DOORBELL_CTLR_RESET, vaddr + SA5_DOORBELL);
+               msleep(1000);
+       } else { /* Try to do it the PCI power state way */
+
+               /* Quoting from the Open CISS Specification: "The Power
+                * Management Control/Status Register (CSR) controls the power
+                * state of the device.  The normal operating state is D0,
+                * CSR=00h.  The software off state is D3, CSR=03h.  To reset
+                * the controller, place the interface device in D3 then to D0,
+                * this causes a secondary PCI reset which will reset the
+                * controller." */
+
+               pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
+               if (pos == 0) {
+                       dev_err(&pdev->dev,
+                               "hpsa_reset_controller: "
+                               "PCI PM not supported\n");
+                       return -ENODEV;
                }
-       }
+               dev_info(&pdev->dev, "using PCI PM to reset controller\n");
+               /* enter the D3hot power management state */
+               pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr);
+               pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+               pmcsr |= PCI_D3hot;
+               pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+
+               msleep(500);
+
+               /* enter the D0 power management state */
+               pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+               pmcsr |= PCI_D0;
+               pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
 
+               msleep(500);
+       }
        return 0;
 }
 
 /* This does a hard reset of the controller using PCI power management
- * states.
+ * states or the using the doorbell register.
  */
-static __devinit int hpsa_hard_reset_controller(struct pci_dev *pdev)
+static __devinit int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
 {
-       u16 pmcsr, saved_config_space[32];
-       int i, pos;
-
-       dev_info(&pdev->dev, "using PCI PM to reset controller\n");
+       u64 cfg_offset;
+       u32 cfg_base_addr;
+       u64 cfg_base_addr_index;
+       void __iomem *vaddr;
+       unsigned long paddr;
+       u32 misc_fw_support, active_transport;
+       int rc;
+       struct CfgTable __iomem *cfgtable;
+       bool use_doorbell;
+       u32 board_id;
+       u16 command_register;
 
-       /* This is very nearly the same thing as
+       /* For controllers as old as the P600, this is very nearly
+        * the same thing as
         *
         * pci_save_state(pci_dev);
         * pci_set_power_state(pci_dev, PCI_D3hot);
         * pci_set_power_state(pci_dev, PCI_D0);
         * pci_restore_state(pci_dev);
         *
-        * but we can't use these nice canned kernel routines on
-        * kexec, because they also check the MSI/MSI-X state in PCI
-        * configuration space and do the wrong thing when it is
-        * set/cleared.  Also, the pci_save/restore_state functions
-        * violate the ordering requirements for restoring the
-        * configuration space from the CCISS document (see the
-        * comment below).  So we roll our own ....
+        * For controllers newer than the P600, the pci power state
+        * method of resetting doesn't work so we have another way
+        * using the doorbell register.
         */
 
-       for (i = 0; i < 32; i++)
-               pci_read_config_word(pdev, 2*i, &saved_config_space[i]);
-
-       pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
-       if (pos == 0) {
-               dev_err(&pdev->dev,
-                       "hpsa_reset_controller: PCI PM not supported\n");
+       /* Exclude 640x boards.  These are two pci devices in one slot
+        * which share a battery backed cache module.  One controls the
+        * cache, the other accesses the cache through the one that controls
+        * it.  If we reset the one controlling the cache, the other will
+        * likely not be happy.  Just forbid resetting this conjoined mess.
+        * The 640x isn't really supported by hpsa anyway.
+        */
+       rc = hpsa_lookup_board_id(pdev, &board_id);
+       if (rc < 0) {
+               dev_warn(&pdev->dev, "Not resetting device.\n");
                return -ENODEV;
        }
+       if (board_id == 0x409C0E11 || board_id == 0x409D0E11)
+               return -ENOTSUPP;
 
-       /* Quoting from the Open CISS Specification: "The Power
-        * Management Control/Status Register (CSR) controls the power
-        * state of the device.  The normal operating state is D0,
-        * CSR=00h.  The software off state is D3, CSR=03h.  To reset
-        * the controller, place the interface device in D3 then to
-        * D0, this causes a secondary PCI reset which will reset the
-        * controller."
+       /* Save the PCI command register */
+       pci_read_config_word(pdev, 4, &command_register);
+       /* Turn the board off.  This is so that later pci_restore_state()
+        * won't turn the board on before the rest of config space is ready.
         */
+       pci_disable_device(pdev);
+       pci_save_state(pdev);
 
-       /* enter the D3hot power management state */
-       pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr);
-       pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
-       pmcsr |= PCI_D3hot;
-       pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+       /* find the first memory BAR, so we can find the cfg table */
+       rc = hpsa_pci_find_memory_BAR(pdev, &paddr);
+       if (rc)
+               return rc;
+       vaddr = remap_pci_mem(paddr, 0x250);
+       if (!vaddr)
+               return -ENOMEM;
 
-       msleep(500);
+       /* find cfgtable in order to check if reset via doorbell is supported */
+       rc = hpsa_find_cfg_addrs(pdev, vaddr, &cfg_base_addr,
+                                       &cfg_base_addr_index, &cfg_offset);
+       if (rc)
+               goto unmap_vaddr;
+       cfgtable = remap_pci_mem(pci_resource_start(pdev,
+                      cfg_base_addr_index) + cfg_offset, sizeof(*cfgtable));
+       if (!cfgtable) {
+               rc = -ENOMEM;
+               goto unmap_vaddr;
+       }
 
-       /* enter the D0 power management state */
-       pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
-       pmcsr |= PCI_D0;
-       pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+       /* If reset via doorbell register is supported, use that. */
+       misc_fw_support = readl(&cfgtable->misc_fw_support);
+       use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET;
 
-       msleep(500);
+       rc = hpsa_controller_hard_reset(pdev, vaddr, use_doorbell);
+       if (rc)
+               goto unmap_cfgtable;
 
-       /* Restore the PCI configuration space.  The Open CISS
-        * Specification says, "Restore the PCI Configuration
-        * Registers, offsets 00h through 60h. It is important to
-        * restore the command register, 16-bits at offset 04h,
-        * last. Do not restore the configuration status register,
-        * 16-bits at offset 06h."  Note that the offset is 2*i.
+       pci_restore_state(pdev);
+       rc = pci_enable_device(pdev);
+       if (rc) {
+               dev_warn(&pdev->dev, "failed to enable device.\n");
+               goto unmap_cfgtable;
+       }
+       pci_write_config_word(pdev, 4, command_register);
+
+       /* Some devices (notably the HP Smart Array 5i Controller)
+          need a little pause here */
+       msleep(HPSA_POST_RESET_PAUSE_MSECS);
+
+       /* Wait for board to become not ready, then ready. */
+       dev_info(&pdev->dev, "Waiting for board to become ready.\n");
+       rc = hpsa_wait_for_board_state(pdev, vaddr, BOARD_NOT_READY);
+       if (rc)
+               dev_warn(&pdev->dev,
+                       "failed waiting for board to become not ready\n");
+       rc = hpsa_wait_for_board_state(pdev, vaddr, BOARD_READY);
+       if (rc) {
+               dev_warn(&pdev->dev,
+                       "failed waiting for board to become ready\n");
+               goto unmap_cfgtable;
+       }
+       dev_info(&pdev->dev, "board ready.\n");
+
+       /* Controller should be in simple mode at this point.  If it's not,
+        * It means we're on one of those controllers which doesn't support
+        * the doorbell reset method and on which the PCI power management reset
+        * method doesn't work (P800, for example.)
+        * In those cases, don't try to proceed, as it generally doesn't work.
         */
-       for (i = 0; i < 32; i++) {
-               if (i == 2 || i == 3)
-                       continue;
-               pci_write_config_word(pdev, 2*i, saved_config_space[i]);
+       active_transport = readl(&cfgtable->TransportActive);
+       if (active_transport & PERFORMANT_MODE) {
+               dev_warn(&pdev->dev, "Unable to successfully reset controller,"
+                       " Ignoring controller.\n");
+               rc = -ENODEV;
        }
-       wmb();
-       pci_write_config_word(pdev, 4, saved_config_space[2]);
 
-       return 0;
+unmap_cfgtable:
+       iounmap(cfgtable);
+
+unmap_vaddr:
+       iounmap(vaddr);
+       return rc;
 }
 
 /*
@@ -3278,7 +3403,7 @@ static void __devinit hpsa_interrupt_mode(struct ctlr_info *h)
 default_int_mode:
 #endif                         /* CONFIG_PCI_MSI */
        /* if we get here we're going to use the default interrupt mode */
-       h->intr[PERF_MODE_INT] = h->pdev->irq;
+       h->intr[h->intr_mode] = h->pdev->irq;
 }
 
 static int __devinit hpsa_lookup_board_id(struct pci_dev *pdev, u32 *board_id)
@@ -3330,18 +3455,28 @@ static int __devinit hpsa_pci_find_memory_BAR(struct pci_dev *pdev,
        return -ENODEV;
 }
 
-static int __devinit hpsa_wait_for_board_ready(struct ctlr_info *h)
+static int __devinit hpsa_wait_for_board_state(struct pci_dev *pdev,
+       void __iomem *vaddr, int wait_for_ready)
 {
-       int i;
+       int i, iterations;
        u32 scratchpad;
+       if (wait_for_ready)
+               iterations = HPSA_BOARD_READY_ITERATIONS;
+       else
+               iterations = HPSA_BOARD_NOT_READY_ITERATIONS;
 
-       for (i = 0; i < HPSA_BOARD_READY_ITERATIONS; i++) {
-               scratchpad = readl(h->vaddr + SA5_SCRATCHPAD_OFFSET);
-               if (scratchpad == HPSA_FIRMWARE_READY)
-                       return 0;
+       for (i = 0; i < iterations; i++) {
+               scratchpad = readl(vaddr + SA5_SCRATCHPAD_OFFSET);
+               if (wait_for_ready) {
+                       if (scratchpad == HPSA_FIRMWARE_READY)
+                               return 0;
+               } else {
+                       if (scratchpad != HPSA_FIRMWARE_READY)
+                               return 0;
+               }
                msleep(HPSA_BOARD_READY_POLL_INTERVAL_MSECS);
        }
-       dev_warn(&h->pdev->dev, "board not ready, timed out.\n");
+       dev_warn(&pdev->dev, "board not ready, timed out.\n");
        return -ENODEV;
 }
 
@@ -3386,13 +3521,30 @@ static int __devinit hpsa_find_cfgtables(struct ctlr_info *h)
        return 0;
 }
 
+static void __devinit hpsa_get_max_perf_mode_cmds(struct ctlr_info *h)
+{
+       h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands));
+
+       /* Limit commands in memory limited kdump scenario. */
+       if (reset_devices && h->max_commands > 32)
+               h->max_commands = 32;
+
+       if (h->max_commands < 16) {
+               dev_warn(&h->pdev->dev, "Controller reports "
+                       "max supported commands of %d, an obvious lie. "
+                       "Using 16.  Ensure that firmware is up to date.\n",
+                       h->max_commands);
+               h->max_commands = 16;
+       }
+}
+
 /* Interrogate the hardware for some limits:
  * max commands, max SG elements without chaining, and with chaining,
  * SG chain block size, etc.
  */
 static void __devinit hpsa_find_board_params(struct ctlr_info *h)
 {
-       h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands));
+       hpsa_get_max_perf_mode_cmds(h);
        h->nr_cmds = h->max_commands - 4; /* Allow room for some ioctls */
        h->maxsgentries = readl(&(h->cfgtable->MaxScatterGatherElements));
        /*
@@ -3451,16 +3603,21 @@ static inline void hpsa_p600_dma_prefetch_quirk(struct ctlr_info *h)
 static void __devinit hpsa_wait_for_mode_change_ack(struct ctlr_info *h)
 {
        int i;
+       u32 doorbell_value;
+       unsigned long flags;
 
        /* under certain very rare conditions, this can take awhile.
         * (e.g.: hot replace a failed 144GB drive in a RAID 5 set right
         * as we enter this code.)
         */
        for (i = 0; i < MAX_CONFIG_WAIT; i++) {
-               if (!(readl(h->vaddr + SA5_DOORBELL) & CFGTBL_ChangeReq))
+               spin_lock_irqsave(&h->lock, flags);
+               doorbell_value = readl(h->vaddr + SA5_DOORBELL);
+               spin_unlock_irqrestore(&h->lock, flags);
+               if (!(doorbell_value & CFGTBL_ChangeReq))
                        break;
                /* delay and try again */
-               msleep(10);
+               usleep_range(10000, 20000);
        }
 }
 
@@ -3483,6 +3640,7 @@ static int __devinit hpsa_enter_simple_mode(struct ctlr_info *h)
                        "unable to get board into simple mode\n");
                return -ENODEV;
        }
+       h->transMethod = CFGTBL_Trans_Simple;
        return 0;
 }
 
@@ -3521,7 +3679,7 @@ static int __devinit hpsa_pci_init(struct ctlr_info *h)
                err = -ENOMEM;
                goto err_out_free_res;
        }
-       err = hpsa_wait_for_board_ready(h);
+       err = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY);
        if (err)
                goto err_out_free_res;
        err = hpsa_find_cfgtables(h);
@@ -3571,33 +3729,49 @@ static void __devinit hpsa_hba_inquiry(struct ctlr_info *h)
        }
 }
 
+static __devinit int hpsa_init_reset_devices(struct pci_dev *pdev)
+{
+       int rc, i;
+
+       if (!reset_devices)
+               return 0;
+
+       /* Reset the controller with a PCI power-cycle or via doorbell */
+       rc = hpsa_kdump_hard_reset_controller(pdev);
+
+       /* -ENOTSUPP here means we cannot reset the controller
+        * but it's already (and still) up and running in
+        * "performant mode".  Or, it might be 640x, which can't reset
+        * due to concerns about shared bbwc between 6402/6404 pair.
+        */
+       if (rc == -ENOTSUPP)
+               return 0; /* just try to do the kdump anyhow. */
+       if (rc)
+               return -ENODEV;
+
+       /* Now try to get the controller to respond to a no-op */
+       for (i = 0; i < HPSA_POST_RESET_NOOP_RETRIES; i++) {
+               if (hpsa_noop(pdev) == 0)
+                       break;
+               else
+                       dev_warn(&pdev->dev, "no-op failed%s\n",
+                                       (i < 11 ? "; re-trying" : ""));
+       }
+       return 0;
+}
+
 static int __devinit hpsa_init_one(struct pci_dev *pdev,
                                    const struct pci_device_id *ent)
 {
-       int i, rc;
-       int dac;
+       int dac, rc;
        struct ctlr_info *h;
 
        if (number_of_controllers == 0)
                printk(KERN_INFO DRIVER_NAME "\n");
-       if (reset_devices) {
-               /* Reset the controller with a PCI power-cycle */
-               if (hpsa_hard_reset_controller(pdev) || hpsa_reset_msi(pdev))
-                       return -ENODEV;
-
-               /* Some devices (notably the HP Smart Array 5i Controller)
-                  need a little pause here */
-               msleep(HPSA_POST_RESET_PAUSE_MSECS);
 
-               /* Now try to get the controller to respond to a no-op */
-               for (i = 0; i < HPSA_POST_RESET_NOOP_RETRIES; i++) {
-                       if (hpsa_noop(pdev) == 0)
-                               break;
-                       else
-                               dev_warn(&pdev->dev, "no-op failed%s\n",
-                                               (i < 11 ? "; re-trying" : ""));
-               }
-       }
+       rc = hpsa_init_reset_devices(pdev);
+       if (rc)
+               return rc;
 
        /* Command structures must be aligned on a 32-byte boundary because
         * the 5 lower bits of the address are used by the hardware. and by
@@ -3611,8 +3785,11 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
 
        h->pdev = pdev;
        h->busy_initializing = 1;
-       INIT_HLIST_HEAD(&h->cmpQ);
-       INIT_HLIST_HEAD(&h->reqQ);
+       h->intr_mode = hpsa_simple_mode ? SIMPLE_MODE_INT : PERF_MODE_INT;
+       INIT_LIST_HEAD(&h->cmpQ);
+       INIT_LIST_HEAD(&h->reqQ);
+       spin_lock_init(&h->lock);
+       spin_lock_init(&h->scan_lock);
        rc = hpsa_pci_init(h);
        if (rc != 0)
                goto clean1;
@@ -3637,17 +3814,22 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
 
        /* make sure the board interrupts are off */
        h->access.set_intr_mask(h, HPSA_INTR_OFF);
-       rc = request_irq(h->intr[PERF_MODE_INT], do_hpsa_intr,
-                       IRQF_DISABLED, h->devname, h);
+
+       if (h->msix_vector || h->msi_vector)
+               rc = request_irq(h->intr[h->intr_mode], do_hpsa_intr_msi,
+                               IRQF_DISABLED, h->devname, h);
+       else
+               rc = request_irq(h->intr[h->intr_mode], do_hpsa_intr_intx,
+                               IRQF_DISABLED, h->devname, h);
        if (rc) {
                dev_err(&pdev->dev, "unable to get irq %d for %s\n",
-                      h->intr[PERF_MODE_INT], h->devname);
+                      h->intr[h->intr_mode], h->devname);
                goto clean2;
        }
 
        dev_info(&pdev->dev, "%s: <0x%x> at IRQ %d%s using DAC\n",
               h->devname, pdev->device,
-              h->intr[PERF_MODE_INT], dac ? "" : " not");
+              h->intr[h->intr_mode], dac ? "" : " not");
 
        h->cmd_pool_bits =
            kmalloc(((h->nr_cmds + BITS_PER_LONG -
@@ -3667,8 +3849,6 @@ static int __devinit hpsa_init_one(struct pci_dev *pdev,
        }
        if (hpsa_allocate_sg_chain_blocks(h))
                goto clean4;
-       spin_lock_init(&h->lock);
-       spin_lock_init(&h->scan_lock);
        init_waitqueue_head(&h->scan_wait_queue);
        h->scan_finished = 1; /* no scan currently in progress */
 
@@ -3700,7 +3880,7 @@ clean4:
                            h->nr_cmds * sizeof(struct ErrorInfo),
                            h->errinfo_pool,
                            h->errinfo_pool_dhandle);
-       free_irq(h->intr[PERF_MODE_INT], h);
+       free_irq(h->intr[h->intr_mode], h);
 clean2:
 clean1:
        h->busy_initializing = 0;
@@ -3744,7 +3924,7 @@ static void hpsa_shutdown(struct pci_dev *pdev)
         */
        hpsa_flush_cache(h);
        h->access.set_intr_mask(h, HPSA_INTR_OFF);
-       free_irq(h->intr[PERF_MODE_INT], h);
+       free_irq(h->intr[h->intr_mode], h);
 #ifdef CONFIG_PCI_MSI
        if (h->msix_vector)
                pci_disable_msix(h->pdev);
@@ -3846,7 +4026,8 @@ static void  calc_bucket_map(int bucket[], int num_buckets,
        }
 }
 
-static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h)
+static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h,
+       u32 use_short_tags)
 {
        int i;
        unsigned long register_value;
@@ -3894,7 +4075,7 @@ static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h)
        writel(0, &h->transtable->RepQCtrAddrHigh32);
        writel(h->reply_pool_dhandle, &h->transtable->RepQAddr0Low32);
        writel(0, &h->transtable->RepQAddr0High32);
-       writel(CFGTBL_Trans_Performant,
+       writel(CFGTBL_Trans_Performant | use_short_tags,
                &(h->cfgtable->HostWrite.TransportRequest));
        writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
        hpsa_wait_for_mode_change_ack(h);
@@ -3904,17 +4085,23 @@ static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h)
                                        " performant mode\n");
                return;
        }
+       /* Change the access methods to the performant access methods */
+       h->access = SA5_performant_access;
+       h->transMethod = CFGTBL_Trans_Performant;
 }
 
 static __devinit void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
 {
        u32 trans_support;
 
+       if (hpsa_simple_mode)
+               return;
+
        trans_support = readl(&(h->cfgtable->TransportSupport));
        if (!(trans_support & PERFORMANT_MODE))
                return;
 
-       h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands));
+       hpsa_get_max_perf_mode_cmds(h);
        h->max_sg_entries = 32;
        /* Performant mode ring buffer and supporting data structures */
        h->reply_pool_size = h->max_commands * sizeof(u64);
@@ -3929,11 +4116,8 @@ static __devinit void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
                || (h->blockFetchTable == NULL))
                goto clean_up;
 
-       hpsa_enter_performant_mode(h);
-
-       /* Change the access methods to the performant access methods */
-       h->access = SA5_performant_access;
-       h->transMethod = CFGTBL_Trans_Performant;
+       hpsa_enter_performant_mode(h,
+               trans_support & CFGTBL_Trans_use_short_tags);
 
        return;