/* 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.
#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 */
static struct qlogicpti *qptichain = NULL;
static DEFINE_SPINLOCK(qptichain_lock);
+static int qptis_running = 0;
#define PACKB(a, b) (((a)<<4)|(b))
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))
* 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;
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;
}
if (Cmnd->device->tagged_supported) {
if (qpti->cmd_count[Cmnd->device->id] == 0)
qpti->tag_ages[Cmnd->device->id] = jiffies;
- if (time_after(jiffies, qpti->tag_ages[Cmnd->device->id] + (5*HZ))) {
+ if ((jiffies - qpti->tag_ages[Cmnd->device->id]) > (5*HZ)) {
cmd->control_flags = CFLAG_ORDERED_TAG;
qpti->tag_ages[Cmnd->device->id] = jiffies;
} else
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;
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 {
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,
.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);