X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fscsi%2Fqlogicpti.c;h=c7e78dcf09df237734d944587aa6f0e664fe223c;hb=9464c7cf61b9433057924c36e6e02f303a00e768;hp=5b2f0741a55b841f06ad6dc93e9eba81dd809e27;hpb=41689045f6a3cbe0550e1d34e9cc20d2e8c432ba;p=linux-2.6.git diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index 5b2f0741a..c7e78dcf0 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, 2006 David S. Miller (davem@davemloft.net) + * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu) * * A lot of this driver was directly stolen from Erik H. Moe's PCI * Qlogic ISP driver. Mucho kudos to him for this code. @@ -43,9 +43,12 @@ #include #include #include +#include #include #include + + #define MAX_TARGETS 16 #define MAX_LUNS 8 /* 32 for 1.31 F/W */ @@ -55,6 +58,7 @@ static struct qlogicpti *qptichain = NULL; static DEFINE_SPINLOCK(qptichain_lock); +static int qptis_running = 0; #define PACKB(a, b) (((a)<<4)|(b)) @@ -128,7 +132,7 @@ static const u_char mbox_param[] = { PACKB(0, 0) /* 0x0042 */ }; -#define MAX_MBOX_COMMAND ARRAY_SIZE(mbox_param) +#define MAX_MBOX_COMMAND (sizeof(mbox_param)/sizeof(u_short)) /* queue length's _must_ be power of two: */ #define QUEUE_DEPTH(in, out, ql) ((in - out) & (ql)) @@ -718,10 +722,10 @@ static int __init qpti_register_irq(struct qlogicpti *qpti) * sanely maintain. */ if (request_irq(qpti->irq, qpti_intr, - IRQF_SHARED, "Qlogic/PTI", qpti)) + SA_SHIRQ, "Qlogic/PTI", qpti)) goto fail; - printk("qpti%d: IRQ %d ", qpti->qpti_id, qpti->irq); + printk("qpti%d: IRQ %s ", qpti->qpti_id, __irq_itoa(qpti->irq)); return 0; @@ -812,13 +816,180 @@ 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 %d regs at %p", - qpti->qhost->irq, qpti->qregs); + sprintf(buf, "PTI Qlogic,ISP SBUS SCSI irq %s regs at %p", + __irq_itoa(qpti->qhost->irq), qpti->qregs); return buf; } @@ -874,7 +1045,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->request_buffer; + sg = (struct scatterlist *) Cmnd->buffer; sg_count = sbus_map_sg(qpti->sdev, sg, Cmnd->use_sg, Cmnd->sc_data_direction); ds = cmd->dataseg; @@ -1278,7 +1449,7 @@ static struct scsi_cmnd *qlogicpti_intr_handler(struct qlogicpti *qpti) if (Cmnd->use_sg) { sbus_unmap_sg(qpti->sdev, - (struct scatterlist *)Cmnd->request_buffer, + (struct scatterlist *)Cmnd->buffer, Cmnd->use_sg, Cmnd->sc_data_direction); } else { @@ -1381,9 +1552,9 @@ static int qlogicpti_reset(struct scsi_cmnd *Cmnd) return return_status; } -static struct scsi_host_template qpti_template = { - .module = THIS_MODULE, - .name = "qlogicpti", +static struct scsi_host_template driver_template = { + .detect = qlogicpti_detect, + .release = qlogicpti_release, .info = qlogicpti_info, .queuecommand = qlogicpti_queuecommand_slow, .eh_abort_handler = qlogicpti_abort, @@ -1395,189 +1566,8 @@ static struct scsi_host_template qpti_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), -}; -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); -} +#include "scsi_module.c" -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);