Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[pandora-kernel.git] / drivers / scsi / qlogicpti.c
index 1fd5fc6..5b2f074 100644 (file)
@@ -1,6 +1,6 @@
 /* qlogicpti.c: Performance Technologies QlogicISP sbus card driver.
  *
- * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu)
+ * Copyright (C) 1996, 2006 David S. Miller (davem@davemloft.net)
  *
  * A lot of this driver was directly stolen from Erik H. Moe's PCI
  * Qlogic ISP driver.  Mucho kudos to him for this code.
@@ -24,6 +24,7 @@
 #include <linux/spinlock.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/jiffies.h>
 
 #include <asm/byteorder.h>
 
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_eh.h>
-#include <scsi/scsi_request.h>
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsi_host.h>
 
-
-
 #define MAX_TARGETS    16
 #define MAX_LUNS       8       /* 32 for 1.31 F/W */
 
@@ -57,7 +55,6 @@
 
 static struct qlogicpti *qptichain = NULL;
 static DEFINE_SPINLOCK(qptichain_lock);
-static int qptis_running = 0;
 
 #define PACKB(a, b)                    (((a)<<4)|(b))
 
@@ -131,7 +128,7 @@ static const u_char mbox_param[] = {
        PACKB(0, 0)     /* 0x0042 */
 };
 
-#define MAX_MBOX_COMMAND       (sizeof(mbox_param)/sizeof(u_short))
+#define MAX_MBOX_COMMAND       ARRAY_SIZE(mbox_param)
 
 /* queue length's _must_ be power of two: */
 #define QUEUE_DEPTH(in, out, ql)       ((in - out) & (ql))
@@ -721,10 +718,10 @@ static int __init qpti_register_irq(struct qlogicpti *qpti)
         * sanely maintain.
         */
        if (request_irq(qpti->irq, qpti_intr,
-                       SA_SHIRQ, "Qlogic/PTI", qpti))
+                       IRQF_SHARED, "Qlogic/PTI", qpti))
                goto fail;
 
-       printk("qpti%d: IRQ %s ", qpti->qpti_id, __irq_itoa(qpti->irq));
+       printk("qpti%d: IRQ %d ", qpti->qpti_id, qpti->irq);
 
        return 0;
 
@@ -815,180 +812,13 @@ static int __init qpti_map_queues(struct qlogicpti *qpti)
        return 0;
 }
 
-/* Detect all PTI Qlogic ISP's in the machine. */
-static int __init qlogicpti_detect(struct scsi_host_template *tpnt)
-{
-       struct qlogicpti *qpti;
-       struct Scsi_Host *qpti_host;
-       struct sbus_bus *sbus;
-       struct sbus_dev *sdev;
-       int nqptis = 0, nqptis_in_use = 0;
-
-       tpnt->proc_name = "qlogicpti";
-       for_each_sbus(sbus) {
-               for_each_sbusdev(sdev, sbus) {
-                       /* Is this a red snapper? */
-                       if (strcmp(sdev->prom_name, "ptisp") &&
-                           strcmp(sdev->prom_name, "PTI,ptisp") &&
-                           strcmp(sdev->prom_name, "QLGC,isp") &&
-                           strcmp(sdev->prom_name, "SUNW,isp"))
-                               continue;
-
-                       /* Sometimes Antares cards come up not completely
-                        * setup, and we get a report of a zero IRQ.
-                        * Skip over them in such cases so we survive.
-                        */
-                       if (sdev->irqs[0] == 0) {
-                               printk("qpti%d: Adapter reports no interrupt, "
-                                      "skipping over this card.", nqptis);
-                               continue;
-                       }
-
-                       /* Yep, register and allocate software state. */
-                       qpti_host = scsi_register(tpnt, sizeof(struct qlogicpti));
-                       if (!qpti_host) {
-                               printk("QPTI: Cannot register PTI Qlogic ISP SCSI host");
-                               continue;
-                       }
-                       qpti = (struct qlogicpti *) qpti_host->hostdata;
-
-                       /* We are wide capable, 16 targets. */
-                       qpti_host->max_id = MAX_TARGETS;
-
-                       /* Setup back pointers and misc. state. */
-                       qpti->qhost = qpti_host;
-                       qpti->sdev = sdev;
-                       qpti->qpti_id = nqptis++;
-                       qpti->prom_node = sdev->prom_node;
-                       prom_getstring(qpti->prom_node, "name",
-                                      qpti->prom_name,
-                                      sizeof(qpti->prom_name));
-
-                       /* This is not correct, actually. There's a switch
-                        * on the PTI cards that put them into "emulation"
-                        * mode- i.e., report themselves as QLGC,isp
-                        * instead of PTI,ptisp. The only real substantive
-                        * difference between non-pti and pti cards is
-                        * the tmon register. Which is possibly even
-                        * there for Qlogic cards, but non-functional.
-                        */
-                       qpti->is_pti = (strcmp (qpti->prom_name, "QLGC,isp") != 0);
-
-                       qpti_chain_add(qpti);
-                       if (qpti_map_regs(qpti) < 0)
-                               goto fail_unlink;
-
-                       if (qpti_register_irq(qpti) < 0)
-                               goto fail_unmap_regs;
-
-                       qpti_get_scsi_id(qpti);
-                       qpti_get_bursts(qpti);
-                       qpti_get_clock(qpti);
-
-                       /* Clear out scsi_cmnd array. */
-                       memset(qpti->cmd_slots, 0, sizeof(qpti->cmd_slots));
-
-                       if (qpti_map_queues(qpti) < 0)
-                               goto fail_free_irq;
-
-                       /* Load the firmware. */
-                       if (qlogicpti_load_firmware(qpti))
-                               goto fail_unmap_queues;
-                       if (qpti->is_pti) {
-                               /* Check the PTI status reg. */
-                               if (qlogicpti_verify_tmon(qpti))
-                                       goto fail_unmap_queues;
-                       }
-
-                       /* Reset the ISP and init res/req queues. */
-                       if (qlogicpti_reset_hardware(qpti_host))
-                               goto fail_unmap_queues;
-
-                       printk("(Firmware v%d.%d.%d)", qpti->fware_majrev,
-                           qpti->fware_minrev, qpti->fware_micrev);
-                       {
-                               char buffer[60];
-                               
-                               prom_getstring (qpti->prom_node,
-                                               "isp-fcode", buffer, 60);
-                               if (buffer[0])
-                                       printk("(Firmware %s)", buffer);
-                               if (prom_getbool(qpti->prom_node, "differential"))
-                                       qpti->differential = 1;
-                       }
-                       
-                       printk (" [%s Wide, using %s interface]\n",
-                              (qpti->ultra ? "Ultra" : "Fast"),
-                              (qpti->differential ? "differential" : "single ended"));
-
-                       nqptis_in_use++;
-                       continue;
-
-               fail_unmap_queues:
-#define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN)
-                       sbus_free_consistent(qpti->sdev,
-                                            QSIZE(RES_QUEUE_LEN),
-                                            qpti->res_cpu, qpti->res_dvma);
-                       sbus_free_consistent(qpti->sdev,
-                                            QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
-                                            qpti->req_cpu, qpti->req_dvma);
-#undef QSIZE
-               fail_free_irq:
-                       free_irq(qpti->irq, qpti);
-
-               fail_unmap_regs:
-                       sbus_iounmap(qpti->qregs,
-                                    qpti->sdev->reg_addrs[0].reg_size);
-                       if (qpti->is_pti)
-                               sbus_iounmap(qpti->sreg, sizeof(unsigned char));
-               fail_unlink:
-                       qpti_chain_del(qpti);
-                       scsi_unregister(qpti->qhost);
-               }
-       }
-       if (nqptis)
-               printk("QPTI: Total of %d PTI Qlogic/ISP hosts found, %d actually in use.\n",
-                      nqptis, nqptis_in_use);
-       qptis_running = nqptis_in_use;
-       return nqptis;
-}
-
-static int qlogicpti_release(struct Scsi_Host *host)
-{
-       struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
-
-       /* Remove visibility from IRQ handlers. */
-       qpti_chain_del(qpti);
-
-       /* Shut up the card. */
-       sbus_writew(0, qpti->qregs + SBUS_CTRL);
-
-       /* Free IRQ handler and unmap Qlogic,ISP and PTI status regs. */
-       free_irq(qpti->irq, qpti);
-
-#define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN)
-       sbus_free_consistent(qpti->sdev,
-                            QSIZE(RES_QUEUE_LEN),
-                            qpti->res_cpu, qpti->res_dvma);
-       sbus_free_consistent(qpti->sdev,
-                            QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
-                            qpti->req_cpu, qpti->req_dvma);
-#undef QSIZE
-
-       sbus_iounmap(qpti->qregs, qpti->sdev->reg_addrs[0].reg_size);
-       if (qpti->is_pti)
-               sbus_iounmap(qpti->sreg, sizeof(unsigned char));
-
-       return 0;
-}
-
 const char *qlogicpti_info(struct Scsi_Host *host)
 {
        static char buf[80];
        struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
 
-       sprintf(buf, "PTI Qlogic,ISP SBUS SCSI irq %s regs at %p",
-               __irq_itoa(qpti->qhost->irq), qpti->qregs);
+       sprintf(buf, "PTI Qlogic,ISP SBUS SCSI irq %d regs at %p",
+               qpti->qhost->irq, qpti->qregs);
        return buf;
 }
 
@@ -1017,7 +847,7 @@ static inline void cmd_frob(struct Command_Entry *cmd, struct scsi_cmnd *Cmnd,
        if (Cmnd->device->tagged_supported) {
                if (qpti->cmd_count[Cmnd->device->id] == 0)
                        qpti->tag_ages[Cmnd->device->id] = jiffies;
-               if ((jiffies - qpti->tag_ages[Cmnd->device->id]) > (5*HZ)) {
+               if (time_after(jiffies, qpti->tag_ages[Cmnd->device->id] + (5*HZ))) {
                        cmd->control_flags = CFLAG_ORDERED_TAG;
                        qpti->tag_ages[Cmnd->device->id] = jiffies;
                } else
@@ -1044,7 +874,7 @@ static inline int load_cmd(struct scsi_cmnd *Cmnd, struct Command_Entry *cmd,
        if (Cmnd->use_sg) {
                int sg_count;
 
-               sg = (struct scatterlist *) Cmnd->buffer;
+               sg = (struct scatterlist *) Cmnd->request_buffer;
                sg_count = sbus_map_sg(qpti->sdev, sg, Cmnd->use_sg, Cmnd->sc_data_direction);
 
                ds = cmd->dataseg;
@@ -1448,7 +1278,7 @@ static struct scsi_cmnd *qlogicpti_intr_handler(struct qlogicpti *qpti)
 
                if (Cmnd->use_sg) {
                        sbus_unmap_sg(qpti->sdev,
-                                     (struct scatterlist *)Cmnd->buffer,
+                                     (struct scatterlist *)Cmnd->request_buffer,
                                      Cmnd->use_sg,
                                      Cmnd->sc_data_direction);
                } else {
@@ -1551,9 +1381,9 @@ static int qlogicpti_reset(struct scsi_cmnd *Cmnd)
        return return_status;
 }
 
-static struct scsi_host_template driver_template = {
-       .detect                 = qlogicpti_detect,
-       .release                = qlogicpti_release,
+static struct scsi_host_template qpti_template = {
+       .module                 = THIS_MODULE,
+       .name                   = "qlogicpti",
        .info                   = qlogicpti_info,
        .queuecommand           = qlogicpti_queuecommand_slow,
        .eh_abort_handler       = qlogicpti_abort,
@@ -1565,8 +1395,189 @@ static struct scsi_host_template driver_template = {
        .use_clustering         = ENABLE_CLUSTERING,
 };
 
+static int __devinit qpti_sbus_probe(struct of_device *dev, const struct of_device_id *match)
+{
+       static int nqptis;
+       struct sbus_dev *sdev = to_sbus_device(&dev->dev);
+       struct device_node *dp = dev->node;
+       struct scsi_host_template *tpnt = match->data;
+       struct Scsi_Host *host;
+       struct qlogicpti *qpti;
+       char *fcode;
+
+       /* Sometimes Antares cards come up not completely
+        * setup, and we get a report of a zero IRQ.
+        */
+       if (sdev->irqs[0] == 0)
+               return -ENODEV;
+
+       host = scsi_host_alloc(tpnt, sizeof(struct qlogicpti));
+       if (!host)
+               return -ENOMEM;
+
+       qpti = (struct qlogicpti *) host->hostdata;
+
+       host->max_id = MAX_TARGETS;
+       qpti->qhost = host;
+       qpti->sdev = sdev;
+       qpti->qpti_id = nqptis;
+       qpti->prom_node = sdev->prom_node;
+       strcpy(qpti->prom_name, sdev->ofdev.node->name);
+       qpti->is_pti = strcmp(qpti->prom_name, "QLGC,isp");
+
+       if (qpti_map_regs(qpti) < 0)
+               goto fail_unlink;
+
+       if (qpti_register_irq(qpti) < 0)
+               goto fail_unmap_regs;
+
+       qpti_get_scsi_id(qpti);
+       qpti_get_bursts(qpti);
+       qpti_get_clock(qpti);
+
+       /* Clear out scsi_cmnd array. */
+       memset(qpti->cmd_slots, 0, sizeof(qpti->cmd_slots));
+
+       if (qpti_map_queues(qpti) < 0)
+               goto fail_free_irq;
+
+       /* Load the firmware. */
+       if (qlogicpti_load_firmware(qpti))
+               goto fail_unmap_queues;
+       if (qpti->is_pti) {
+               /* Check the PTI status reg. */
+               if (qlogicpti_verify_tmon(qpti))
+                       goto fail_unmap_queues;
+       }
+
+       /* Reset the ISP and init res/req queues. */
+       if (qlogicpti_reset_hardware(host))
+               goto fail_unmap_queues;
+
+       if (scsi_add_host(host, &dev->dev))
+               goto fail_unmap_queues;
+
+       printk("(Firmware v%d.%d.%d)", qpti->fware_majrev,
+              qpti->fware_minrev, qpti->fware_micrev);
+
+       fcode = of_get_property(dp, "isp-fcode", NULL);
+       if (fcode && fcode[0])
+               printk("(Firmware %s)", fcode);
+       if (of_find_property(dp, "differential", NULL) != NULL)
+               qpti->differential = 1;
+                       
+       printk (" [%s Wide, using %s interface]\n",
+               (qpti->ultra ? "Ultra" : "Fast"),
+               (qpti->differential ? "differential" : "single ended"));
+
+       dev_set_drvdata(&sdev->ofdev.dev, qpti);
+
+       qpti_chain_add(qpti);
+
+       scsi_scan_host(host);
+       nqptis++;
+
+       return 0;
+
+fail_unmap_queues:
+#define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN)
+       sbus_free_consistent(qpti->sdev,
+                            QSIZE(RES_QUEUE_LEN),
+                            qpti->res_cpu, qpti->res_dvma);
+       sbus_free_consistent(qpti->sdev,
+                            QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
+                            qpti->req_cpu, qpti->req_dvma);
+#undef QSIZE
+
+fail_unmap_regs:
+       sbus_iounmap(qpti->qregs,
+                    qpti->sdev->reg_addrs[0].reg_size);
+       if (qpti->is_pti)
+               sbus_iounmap(qpti->sreg, sizeof(unsigned char));
+
+fail_free_irq:
+       free_irq(qpti->irq, qpti);
+
+fail_unlink:
+       scsi_host_put(host);
+
+       return -ENODEV;
+}
+
+static int __devexit qpti_sbus_remove(struct of_device *dev)
+{
+       struct qlogicpti *qpti = dev_get_drvdata(&dev->dev);
+
+       qpti_chain_del(qpti);
+
+       scsi_remove_host(qpti->qhost);
+
+       /* Shut up the card. */
+       sbus_writew(0, qpti->qregs + SBUS_CTRL);
+
+       /* Free IRQ handler and unmap Qlogic,ISP and PTI status regs. */
+       free_irq(qpti->irq, qpti);
+
+#define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN)
+       sbus_free_consistent(qpti->sdev,
+                            QSIZE(RES_QUEUE_LEN),
+                            qpti->res_cpu, qpti->res_dvma);
+       sbus_free_consistent(qpti->sdev,
+                            QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
+                            qpti->req_cpu, qpti->req_dvma);
+#undef QSIZE
+
+       sbus_iounmap(qpti->qregs, qpti->sdev->reg_addrs[0].reg_size);
+       if (qpti->is_pti)
+               sbus_iounmap(qpti->sreg, sizeof(unsigned char));
+
+       scsi_host_put(qpti->qhost);
+
+       return 0;
+}
+
+static struct of_device_id qpti_match[] = {
+       {
+               .name = "ptisp",
+               .data = &qpti_template,
+       },
+       {
+               .name = "PTI,ptisp",
+               .data = &qpti_template,
+       },
+       {
+               .name = "QLGC,isp",
+               .data = &qpti_template,
+       },
+       {
+               .name = "SUNW,isp",
+               .data = &qpti_template,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, qpti_match);
+
+static struct of_platform_driver qpti_sbus_driver = {
+       .name           = "qpti",
+       .match_table    = qpti_match,
+       .probe          = qpti_sbus_probe,
+       .remove         = __devexit_p(qpti_sbus_remove),
+};
 
-#include "scsi_module.c"
+static int __init qpti_init(void)
+{
+       return of_register_driver(&qpti_sbus_driver, &sbus_bus_type);
+}
+
+static void __exit qpti_exit(void)
+{
+       of_unregister_driver(&qpti_sbus_driver);
+}
 
+MODULE_DESCRIPTION("QlogicISP SBUS driver");
+MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
 MODULE_LICENSE("GPL");
+MODULE_VERSION("2.0");
 
+module_init(qpti_init);
+module_exit(qpti_exit);