Loading drivers/scsi/qlogicpti.c +186 −175 Original line number Diff line number Diff line /* 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. Loading Loading @@ -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 */ Loading @@ -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)) Loading Loading @@ -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]; Loading Loading @@ -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, Loading @@ -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); Loading
drivers/scsi/qlogicpti.c +186 −175 Original line number Diff line number Diff line /* 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. Loading Loading @@ -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 */ Loading @@ -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)) Loading Loading @@ -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]; Loading Loading @@ -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, Loading @@ -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);