diff options
-rw-r--r-- | drivers/scsi/qlogicpti.c | 361 |
1 files changed, 186 insertions, 175 deletions
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 @@ 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]; @@ -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); |