]> Pileus Git - ~andy/linux/blobdiff - drivers/scsi/sym53c8xx_2/sym_glue.c
[SCSI] sym53c8xx: Use pdev->revision
[~andy/linux] / drivers / scsi / sym53c8xx_2 / sym_glue.c
index 739d3ef46a407d14d1d3a309676a6c1588b182f0..3ed3ed86d35019e02282f0eec992fcb04b325601 100644 (file)
@@ -134,82 +134,26 @@ static struct scsi_transport_template *sym2_transport_template = NULL;
  *  Driver private area in the SCSI command structure.
  */
 struct sym_ucmd {              /* Override the SCSI pointer structure */
-       dma_addr_t      data_mapping;
-       unsigned char   data_mapped;
-       unsigned char   to_do;                  /* For error handling */
-       void (*old_done)(struct scsi_cmnd *);   /* For error handling */
-       struct completion *eh_done;             /* For error handling */
+       struct completion *eh_done;             /* SCSI error handling */
 };
 
 #define SYM_UCMD_PTR(cmd)  ((struct sym_ucmd *)(&(cmd)->SCp))
 #define SYM_SOFTC_PTR(cmd) sym_get_hcb(cmd->device->host)
 
-static void __unmap_scsi_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
-{
-       int dma_dir = cmd->sc_data_direction;
-
-       switch(SYM_UCMD_PTR(cmd)->data_mapped) {
-       case 2:
-               pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, dma_dir);
-               break;
-       case 1:
-               pci_unmap_single(pdev, SYM_UCMD_PTR(cmd)->data_mapping,
-                                cmd->request_bufflen, dma_dir);
-               break;
-       }
-       SYM_UCMD_PTR(cmd)->data_mapped = 0;
-}
-
-static dma_addr_t __map_scsi_single_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
-{
-       dma_addr_t mapping;
-       int dma_dir = cmd->sc_data_direction;
-
-       mapping = pci_map_single(pdev, cmd->request_buffer,
-                                cmd->request_bufflen, dma_dir);
-       if (mapping) {
-               SYM_UCMD_PTR(cmd)->data_mapped  = 1;
-               SYM_UCMD_PTR(cmd)->data_mapping = mapping;
-       }
-
-       return mapping;
-}
-
-static int __map_scsi_sg_data(struct pci_dev *pdev, struct scsi_cmnd *cmd)
-{
-       int use_sg;
-       int dma_dir = cmd->sc_data_direction;
-
-       use_sg = pci_map_sg(pdev, cmd->request_buffer, cmd->use_sg, dma_dir);
-       if (use_sg > 0) {
-               SYM_UCMD_PTR(cmd)->data_mapped  = 2;
-               SYM_UCMD_PTR(cmd)->data_mapping = use_sg;
-       }
-
-       return use_sg;
-}
-
-#define unmap_scsi_data(np, cmd)       \
-               __unmap_scsi_data(np->s.device, cmd)
-#define map_scsi_single_data(np, cmd)  \
-               __map_scsi_single_data(np->s.device, cmd)
-#define map_scsi_sg_data(np, cmd)      \
-               __map_scsi_sg_data(np->s.device, cmd)
 /*
  *  Complete a pending CAM CCB.
  */
 void sym_xpt_done(struct sym_hcb *np, struct scsi_cmnd *cmd)
 {
-       unmap_scsi_data(np, cmd);
-       cmd->scsi_done(cmd);
-}
+       struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd);
+       BUILD_BUG_ON(sizeof(struct scsi_pointer) < sizeof(struct sym_ucmd));
 
-static void sym_xpt_done2(struct sym_hcb *np, struct scsi_cmnd *cmd, int cam_status)
-{
-       sym_set_cam_status(cmd, cam_status);
-       sym_xpt_done(np, cmd);
-}
+       if (ucmd->eh_done)
+               complete(ucmd->eh_done);
 
+       scsi_dma_unmap(cmd);
+       cmd->scsi_done(cmd);
+}
 
 /*
  *  Tell the SCSI layer about a BUS RESET.
@@ -322,68 +266,33 @@ void sym_set_cam_result_error(struct sym_hcb *np, struct sym_ccb *cp, int resid)
                 */
                cam_status = sym_xerr_cam_status(DID_ERROR, cp->xerr_status);
        }
-       cmd->resid = resid;
+       scsi_set_resid(cmd, resid);
        cmd->result = (drv_status << 24) + (cam_status << 16) + scsi_status;
 }
 
-
-/*
- *  Build the scatter/gather array for an I/O.
- */
-
-static int sym_scatter_no_sglist(struct sym_hcb *np, struct sym_ccb *cp, struct scsi_cmnd *cmd)
-{
-       struct sym_tblmove *data = &cp->phys.data[SYM_CONF_MAX_SG-1];
-       int segment;
-       unsigned int len = cmd->request_bufflen;
-
-       if (len) {
-               dma_addr_t baddr = map_scsi_single_data(np, cmd);
-               if (baddr) {
-                       if (len & 1) {
-                               struct sym_tcb *tp = &np->target[cp->target];
-                               if (tp->head.wval & EWS) {
-                                       len++;
-                                       cp->odd_byte_adjustment++;
-                               }
-                       }
-                       cp->data_len = len;
-                       sym_build_sge(np, data, baddr, len);
-                       segment = 1;
-               } else {
-                       segment = -2;
-               }
-       } else {
-               segment = 0;
-       }
-
-       return segment;
-}
-
 static int sym_scatter(struct sym_hcb *np, struct sym_ccb *cp, struct scsi_cmnd *cmd)
 {
        int segment;
-       int use_sg = (int) cmd->use_sg;
+       int use_sg;
 
        cp->data_len = 0;
 
-       if (!use_sg)
-               segment = sym_scatter_no_sglist(np, cp, cmd);
-       else if ((use_sg = map_scsi_sg_data(np, cmd)) > 0) {
-               struct scatterlist *scatter = (struct scatterlist *)cmd->request_buffer;
+       use_sg = scsi_dma_map(cmd);
+       if (use_sg > 0) {
+               struct scatterlist *sg;
                struct sym_tcb *tp = &np->target[cp->target];
                struct sym_tblmove *data;
 
                if (use_sg > SYM_CONF_MAX_SG) {
-                       unmap_scsi_data(np, cmd);
+                       scsi_dma_unmap(cmd);
                        return -1;
                }
 
                data = &cp->phys.data[SYM_CONF_MAX_SG - use_sg];
 
-               for (segment = 0; segment < use_sg; segment++) {
-                       dma_addr_t baddr = sg_dma_address(&scatter[segment]);
-                       unsigned int len = sg_dma_len(&scatter[segment]);
+               scsi_for_each_sg(cmd, sg, use_sg, segment) {
+                       dma_addr_t baddr = sg_dma_address(sg);
+                       unsigned int len = sg_dma_len(sg);
 
                        if ((len & 1) && (tp->head.wval & EWS)) {
                                len++;
@@ -411,15 +320,6 @@ static int sym_queue_command(struct sym_hcb *np, struct scsi_cmnd *cmd)
        struct sym_ccb *cp;
        int     order;
 
-       /*
-        *  Minimal checkings, so that we will not 
-        *  go outside our tables.
-        */
-       if (sdev->id == np->myaddr) {
-               sym_xpt_done2(np, cmd, DID_NO_CONNECT);
-               return 0;
-       }
-
        /*
         *  Retrieve the target descriptor.
         */
@@ -625,7 +525,7 @@ static int sym53c8xx_queue_command(struct scsi_cmnd *cmd,
        struct sym_ucmd *ucp = SYM_UCMD_PTR(cmd);
        int sts = 0;
 
-       cmd->scsi_done     = done;
+       cmd->scsi_done = done;
        memset(ucp, 0, sizeof(*ucp));
 
        /*
@@ -652,16 +552,19 @@ static int sym53c8xx_queue_command(struct scsi_cmnd *cmd,
 /*
  *  Linux entry point of the interrupt handler.
  */
-static irqreturn_t sym53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
+static irqreturn_t sym53c8xx_intr(int irq, void *dev_id)
 {
-       unsigned long flags;
-       struct sym_hcb *np = (struct sym_hcb *)dev_id;
+       struct sym_hcb *np = dev_id;
+
+       /* Avoid spinloop trying to handle interrupts on frozen device */
+       if (pci_channel_offline(np->s.device))
+               return IRQ_NONE;
 
        if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("[");
 
-       spin_lock_irqsave(np->s.host->host_lock, flags);
+       spin_lock(np->s.host->host_lock);
        sym_interrupt(np);
-       spin_unlock_irqrestore(np->s.host->host_lock, flags);
+       spin_unlock(np->s.host->host_lock);
 
        if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("]\n");
 
@@ -690,26 +593,6 @@ static void sym53c8xx_timer(unsigned long npref)
 #define SYM_EH_BUS_RESET       2
 #define SYM_EH_HOST_RESET      3
 
-/*
- *  What we will do regarding the involved SCSI command.
- */
-#define SYM_EH_DO_IGNORE       0
-#define SYM_EH_DO_WAIT         2
-
-/*
- *  scsi_done() alias when error recovery is in progress.
- */
-static void sym_eh_done(struct scsi_cmnd *cmd)
-{
-       struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd);
-       BUILD_BUG_ON(sizeof(struct scsi_pointer) < sizeof(struct sym_ucmd));
-
-       cmd->scsi_done = ucmd->old_done;
-
-       if (ucmd->to_do == SYM_EH_DO_WAIT)
-               complete(ucmd->eh_done);
-}
-
 /*
  *  Generic method for our eh processing.
  *  The 'op' argument tells what we have to do.
@@ -719,31 +602,56 @@ static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd)
        struct sym_hcb *np = SYM_SOFTC_PTR(cmd);
        struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd);
        struct Scsi_Host *host = cmd->device->host;
+       struct pci_dev *pdev = np->s.device;
        SYM_QUEHEAD *qp;
-       int to_do = SYM_EH_DO_IGNORE;
+       int cmd_queued = 0;
        int sts = -1;
        struct completion eh_done;
 
        dev_warn(&cmd->device->sdev_gendev, "%s operation started.\n", opname);
 
+       /* We may be in an error condition because the PCI bus
+        * went down. In this case, we need to wait until the
+        * PCI bus is reset, the card is reset, and only then
+        * proceed with the scsi error recovery.  There's no
+        * point in hurrying; take a leisurely wait.
+        */
+#define WAIT_FOR_PCI_RECOVERY  35
+       if (pci_channel_offline(pdev)) {
+               struct host_data *hostdata = shost_priv(host);
+               struct completion *io_reset;
+               int finished_reset = 0;
+               init_completion(&eh_done);
+               spin_lock_irq(host->host_lock);
+               /* Make sure we didn't race */
+               if (pci_channel_offline(pdev)) {
+                       if (!hostdata->io_reset)
+                               hostdata->io_reset = &eh_done;
+                       io_reset = hostdata->io_reset;
+               } else {
+                       io_reset = NULL;
+               }
+
+               if (!pci_channel_offline(pdev))
+                       finished_reset = 1;
+               spin_unlock_irq(host->host_lock);
+               if (!finished_reset)
+                       finished_reset = wait_for_completion_timeout(io_reset,
+                                               WAIT_FOR_PCI_RECOVERY*HZ);
+               if (!finished_reset)
+                       return SCSI_FAILED;
+       }
+
        spin_lock_irq(host->host_lock);
        /* This one is queued in some place -> to wait for completion */
        FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
                struct sym_ccb *cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
                if (cp->cmd == cmd) {
-                       to_do = SYM_EH_DO_WAIT;
+                       cmd_queued = 1;
                        break;
                }
        }
 
-       if (to_do == SYM_EH_DO_WAIT) {
-               init_completion(&eh_done);
-               ucmd->old_done = cmd->scsi_done;
-               ucmd->eh_done = &eh_done;
-               wmb();
-               cmd->scsi_done = sym_eh_done;
-       }
-
        /* Try to proceed the operation we have been asked for */
        sts = -1;
        switch(op) {
@@ -759,7 +667,7 @@ static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd)
                break;
        case SYM_EH_HOST_RESET:
                sym_reset_scsi_bus(np, 0);
-               sym_start_up (np, 1);
+               sym_start_up(np, 1);
                sts = 0;
                break;
        default:
@@ -767,21 +675,21 @@ static int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd)
        }
 
        /* On error, restore everything and cross fingers :) */
-       if (sts) {
-               cmd->scsi_done = ucmd->old_done;
-               to_do = SYM_EH_DO_IGNORE;
-       }
-
-       ucmd->to_do = to_do;
-       spin_unlock_irq(host->host_lock);
+       if (sts)
+               cmd_queued = 0;
 
-       if (to_do == SYM_EH_DO_WAIT) {
+       if (cmd_queued) {
+               init_completion(&eh_done);
+               ucmd->eh_done = &eh_done;
+               spin_unlock_irq(host->host_lock);
                if (!wait_for_completion_timeout(&eh_done, 5*HZ)) {
-                       ucmd->to_do = SYM_EH_DO_IGNORE;
-                       wmb();
+                       ucmd->eh_done = NULL;
                        sts = -2;
                }
+       } else {
+               spin_unlock_irq(host->host_lock);
        }
+
        dev_warn(&cmd->device->sdev_gendev, "%s operation %s.\n", opname,
                        sts==0 ? "complete" :sts==-2 ? "timed-out" : "failed");
        return sts ? SCSI_FAILED : SCSI_SUCCESS;
@@ -1346,10 +1254,10 @@ static int sym_host_info(struct sym_hcb *np, char *ptr, off_t offset, int len)
        info.pos        = 0;
 
        copy_info(&info, "Chip " NAME53C "%s, device id 0x%x, "
-                        "revision id 0x%x\n",
-                        np->s.chip_name, np->device_id, np->revision_id);
+                        "revision id 0x%x\n", np->s.chip_name,
+                        np->device_id, np->s.device->revision);
        copy_info(&info, "At PCI address %s, IRQ " IRQ_FMT "\n",
-               pci_name(np->s.device), IRQ_PRM(np->s.irq));
+               pci_name(np->s.device), IRQ_PRM(np->s.device->irq));
        copy_info(&info, "Min. period factor %d, %s SCSI BUS%s\n",
                         (int) (np->minsync_dt ? np->minsync_dt : np->minsync),
                         np->maxwide ? "Wide" : "Narrow",
@@ -1402,8 +1310,8 @@ static void sym_free_resources(struct sym_hcb *np, struct pci_dev *pdev)
        /*
         *  Free O/S specific resources.
         */
-       if (np->s.irq)
-               free_irq(np->s.irq, np);
+       if (pdev->irq)
+               free_irq(pdev->irq, np);
        if (np->s.ioaddr)
                pci_iounmap(pdev, np->s.ioaddr);
        if (np->s.ramaddr)
@@ -1460,10 +1368,9 @@ static struct Scsi_Host * __devinit sym_attach(struct scsi_host_template *tpnt,
        unsigned long flags;
        struct sym_fw *fw;
 
-       printk(KERN_INFO
-               "sym%d: <%s> rev 0x%x at pci %s irq " IRQ_FMT "\n",
-               unit, dev->chip.name, dev->chip.revision_id,
-               pci_name(pdev), IRQ_PRM(pdev->irq));
+       printk(KERN_INFO "sym%d: <%s> rev 0x%x at pci %s irq " IRQ_FMT "\n",
+               unit, dev->chip.name, pdev->revision, pci_name(pdev),
+               IRQ_PRM(pdev->irq));
 
        /*
         *  Get the firmware for this chip.
@@ -1504,7 +1411,6 @@ static struct Scsi_Host * __devinit sym_attach(struct scsi_host_template *tpnt,
        np->s.device    = pdev;
        np->s.unit      = unit;
        np->device_id   = dev->chip.device_id;
-       np->revision_id = dev->chip.revision_id;
        np->features    = dev->chip.features;
        np->clock_divn  = dev->chip.nr_divisor;
        np->maxoffs     = dev->chip.offset_max;
@@ -1552,7 +1458,6 @@ static struct Scsi_Host * __devinit sym_attach(struct scsi_host_template *tpnt,
                        sym_name(np), pdev->irq);
                goto attach_failed;
        }
-       np->s.irq = pdev->irq;
 
        /*
         *  After SCSI devices have been opened, we cannot
@@ -1565,7 +1470,7 @@ static struct Scsi_Host * __devinit sym_attach(struct scsi_host_template *tpnt,
        /*
         *  Start the SCRIPTS.
         */
-       sym_start_up (np, 1);
+       sym_start_up(np, 1);
 
        /*
         *  Start the timer daemon
@@ -1592,6 +1497,10 @@ static struct Scsi_Host * __devinit sym_attach(struct scsi_host_template *tpnt,
        BUG_ON(sym2_transport_template == NULL);
        instance->transportt    = sym2_transport_template;
 
+       /* 53c896 rev 1 errata: DMA may not cross 16MB boundary */
+       if (pdev->device == PCI_DEVICE_ID_NCR_53C896 && pdev->revision < 2)
+               instance->dma_boundary = 0xFFFFFF;
+
        spin_unlock_irqrestore(instance->host_lock, flags);
 
        return instance;
@@ -1634,7 +1543,6 @@ static int __devinit sym_check_supported(struct sym_device *device)
 {
        struct sym_chip *chip;
        struct pci_dev *pdev = device->pdev;
-       u_char revision;
        unsigned long io_port = pci_resource_start(pdev, 0);
        int i;
 
@@ -1654,14 +1562,12 @@ static int __devinit sym_check_supported(struct sym_device *device)
         * to our device structure so we can make it match the actual device
         * and options.
         */
-       pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision);
-       chip = sym_lookup_chip_table(pdev->device, revision);
+       chip = sym_lookup_chip_table(pdev->device, pdev->revision);
        if (!chip) {
                dev_info(&pdev->dev, "device not supported\n");
                return -ENODEV;
        }
        memcpy(&device->chip, chip, sizeof(device->chip));
-       device->chip.revision_id = revision;
 
        return 0;
 }
@@ -1702,7 +1608,7 @@ static int __devinit sym_set_workarounds(struct sym_device *device)
         *  We must ensure the chip will use WRITE AND INVALIDATE.
         *  The revision number limit is for now arbitrary.
         */
-       if (pdev->device == PCI_DEVICE_ID_NCR_53C896 && chip->revision_id < 0x4) {
+       if (pdev->device == PCI_DEVICE_ID_NCR_53C896 && pdev->revision < 0x4) {
                chip->features  |= (FE_WRIE | FE_CLSE);
        }
 
@@ -1869,6 +1775,7 @@ static struct scsi_host_template sym2_template = {
        .eh_host_reset_handler  = sym53c8xx_eh_host_reset_handler,
        .this_id                = 7,
        .use_clustering         = ENABLE_CLUSTERING,
+       .use_sg_chaining        = ENABLE_SG_CHAINING,
        .max_sectors            = 0xFFFF,
 #ifdef SYM_LINUX_PROC_INFO_SUPPORT
        .proc_info              = sym53c8xx_proc_info,
@@ -1948,6 +1855,132 @@ static void __devexit sym2_remove(struct pci_dev *pdev)
        attach_count--;
 }
 
+/**
+ * sym2_io_error_detected() - called when PCI error is detected
+ * @pdev: pointer to PCI device
+ * @state: current state of the PCI slot
+ */
+static pci_ers_result_t sym2_io_error_detected(struct pci_dev *pdev,
+                                         enum pci_channel_state state)
+{
+       /* If slot is permanently frozen, turn everything off */
+       if (state == pci_channel_io_perm_failure) {
+               sym2_remove(pdev);
+               return PCI_ERS_RESULT_DISCONNECT;
+       }
+
+       disable_irq(pdev->irq);
+       pci_disable_device(pdev);
+
+       /* Request that MMIO be enabled, so register dump can be taken. */
+       return PCI_ERS_RESULT_CAN_RECOVER;
+}
+
+/**
+ * sym2_io_slot_dump - Enable MMIO and dump debug registers
+ * @pdev: pointer to PCI device
+ */
+static pci_ers_result_t sym2_io_slot_dump(struct pci_dev *pdev)
+{
+       struct sym_hcb *np = pci_get_drvdata(pdev);
+
+       sym_dump_registers(np);
+
+       /* Request a slot reset. */
+       return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * sym2_reset_workarounds - hardware-specific work-arounds
+ *
+ * This routine is similar to sym_set_workarounds(), except
+ * that, at this point, we already know that the device was
+ * succesfully intialized at least once before, and so most
+ * of the steps taken there are un-needed here.
+ */
+static void sym2_reset_workarounds(struct pci_dev *pdev)
+{
+       u_short status_reg;
+       struct sym_chip *chip;
+
+       chip = sym_lookup_chip_table(pdev->device, pdev->revision);
+
+       /* Work around for errant bit in 895A, in a fashion
+        * similar to what is done in sym_set_workarounds().
+        */
+       pci_read_config_word(pdev, PCI_STATUS, &status_reg);
+       if (!(chip->features & FE_66MHZ) && (status_reg & PCI_STATUS_66MHZ)) {
+               status_reg = PCI_STATUS_66MHZ;
+               pci_write_config_word(pdev, PCI_STATUS, status_reg);
+               pci_read_config_word(pdev, PCI_STATUS, &status_reg);
+       }
+}
+
+/**
+ * sym2_io_slot_reset() - called when the pci bus has been reset.
+ * @pdev: pointer to PCI device
+ *
+ * Restart the card from scratch.
+ */
+static pci_ers_result_t sym2_io_slot_reset(struct pci_dev *pdev)
+{
+       struct sym_hcb *np = pci_get_drvdata(pdev);
+
+       printk(KERN_INFO "%s: recovering from a PCI slot reset\n",
+                 sym_name(np));
+
+       if (pci_enable_device(pdev)) {
+               printk(KERN_ERR "%s: Unable to enable after PCI reset\n",
+                       sym_name(np));
+               return PCI_ERS_RESULT_DISCONNECT;
+       }
+
+       pci_set_master(pdev);
+       enable_irq(pdev->irq);
+
+       /* If the chip can do Memory Write Invalidate, enable it */
+       if (np->features & FE_WRIE) {
+               if (pci_set_mwi(pdev))
+                       return PCI_ERS_RESULT_DISCONNECT;
+       }
+
+       /* Perform work-arounds, analogous to sym_set_workarounds() */
+       sym2_reset_workarounds(pdev);
+
+       /* Perform host reset only on one instance of the card */
+       if (PCI_FUNC(pdev->devfn) == 0) {
+               if (sym_reset_scsi_bus(np, 0)) {
+                       printk(KERN_ERR "%s: Unable to reset scsi host\n",
+                               sym_name(np));
+                       return PCI_ERS_RESULT_DISCONNECT;
+               }
+               sym_start_up(np, 1);
+       }
+
+       return PCI_ERS_RESULT_RECOVERED;
+}
+
+/**
+ * sym2_io_resume() - resume normal ops after PCI reset
+ * @pdev: pointer to PCI device
+ *
+ * Called when the error recovery driver tells us that its
+ * OK to resume normal operation. Use completion to allow
+ * halted scsi ops to resume.
+ */
+static void sym2_io_resume(struct pci_dev *pdev)
+{
+       struct sym_hcb *np = pci_get_drvdata(pdev);
+       struct Scsi_Host *shost = np->s.host;
+       struct host_data *hostdata = shost_priv(shost);
+
+       spin_lock_irq(shost->host_lock);
+       if (hostdata->io_reset)
+               complete_all(hostdata->io_reset);
+       hostdata->io_reset = NULL;
+       spin_unlock_irq(shost->host_lock);
+}
+
 static void sym2_get_signalling(struct Scsi_Host *shost)
 {
        struct sym_hcb *np = sym_get_hcb(shost);
@@ -2094,7 +2127,7 @@ static struct pci_device_id sym2_id_table[] __devinitdata = {
        { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C875,
          PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
        { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C1510,
-         PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, /* new */
+         PCI_ANY_ID, PCI_ANY_ID,  PCI_CLASS_STORAGE_SCSI<<8,  0xffff00, 0UL }, /* new */
        { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C895A,
          PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
        { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C875A,
@@ -2110,11 +2143,19 @@ static struct pci_device_id sym2_id_table[] __devinitdata = {
 
 MODULE_DEVICE_TABLE(pci, sym2_id_table);
 
+static struct pci_error_handlers sym2_err_handler = {
+       .error_detected = sym2_io_error_detected,
+       .mmio_enabled   = sym2_io_slot_dump,
+       .slot_reset     = sym2_io_slot_reset,
+       .resume         = sym2_io_resume,
+};
+
 static struct pci_driver sym2_driver = {
        .name           = NAME53C8XX,
        .id_table       = sym2_id_table,
        .probe          = sym2_probe,
        .remove         = __devexit_p(sym2_remove),
+       .err_handler    = &sym2_err_handler,
 };
 
 static int __init sym2_init(void)