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

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2006-06-23 22:00:34 -07:00
parent 7bd5ed5dd4
commit 3d4253d9af

View File

@ -1,6 +1,6 @@
/* qlogicpti.c: Performance Technologies QlogicISP sbus card driver. /* 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 * A lot of this driver was directly stolen from Erik H. Moe's PCI
* Qlogic ISP driver. Mucho kudos to him for this code. * Qlogic ISP driver. Mucho kudos to him for this code.
@ -46,8 +46,6 @@
#include <scsi/scsi_tcq.h> #include <scsi/scsi_tcq.h>
#include <scsi/scsi_host.h> #include <scsi/scsi_host.h>
#define MAX_TARGETS 16 #define MAX_TARGETS 16
#define MAX_LUNS 8 /* 32 for 1.31 F/W */ #define MAX_LUNS 8 /* 32 for 1.31 F/W */
@ -57,7 +55,6 @@
static struct qlogicpti *qptichain = NULL; static struct qlogicpti *qptichain = NULL;
static DEFINE_SPINLOCK(qptichain_lock); static DEFINE_SPINLOCK(qptichain_lock);
static int qptis_running = 0;
#define PACKB(a, b) (((a)<<4)|(b)) #define PACKB(a, b) (((a)<<4)|(b))
@ -815,173 +812,6 @@ static int __init qpti_map_queues(struct qlogicpti *qpti)
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) const char *qlogicpti_info(struct Scsi_Host *host)
{ {
static char buf[80]; static char buf[80];
@ -1551,9 +1381,9 @@ static int qlogicpti_reset(struct scsi_cmnd *Cmnd)
return return_status; return return_status;
} }
static struct scsi_host_template driver_template = { static struct scsi_host_template qpti_template = {
.detect = qlogicpti_detect, .module = THIS_MODULE,
.release = qlogicpti_release, .name = "qlogicpti",
.info = qlogicpti_info, .info = qlogicpti_info,
.queuecommand = qlogicpti_queuecommand_slow, .queuecommand = qlogicpti_queuecommand_slow,
.eh_abort_handler = qlogicpti_abort, .eh_abort_handler = qlogicpti_abort,
@ -1565,8 +1395,189 @@ static struct scsi_host_template driver_template = {
.use_clustering = ENABLE_CLUSTERING, .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_LICENSE("GPL");
MODULE_VERSION("2.0");
module_init(qpti_init);
module_exit(qpti_exit);