[SCSI] qlogicpti: Convert to new SBUS device framework.

Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index 2203103..329ead2 100644
--- a/drivers/scsi/qlogicpti.c
+++ b/drivers/scsi/qlogicpti.c
@@ -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.
@@ -46,8 +46,6 @@
 #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))
 
@@ -815,173 +812,6 @@
 	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];
@@ -1551,9 +1381,9 @@
 	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 @@
 	.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;
 
-#include "scsi_module.c"
+	/* 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),
+};
+
+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);