diff options
Diffstat (limited to 'drivers')
-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 2203103adced..329ead263714 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* qlogicpti.c: Performance Technologies QlogicISP sbus card driver. | 1 | /* qlogicpti.c: Performance Technologies QlogicISP sbus card driver. |
2 | * | 2 | * |
3 | * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu) | 3 | * Copyright (C) 1996, 2006 David S. Miller (davem@davemloft.net) |
4 | * | 4 | * |
5 | * A lot of this driver was directly stolen from Erik H. Moe's PCI | 5 | * A lot of this driver was directly stolen from Erik H. Moe's PCI |
6 | * Qlogic ISP driver. Mucho kudos to him for this code. | 6 | * Qlogic ISP driver. Mucho kudos to him for this code. |
@@ -46,8 +46,6 @@ | |||
46 | #include <scsi/scsi_tcq.h> | 46 | #include <scsi/scsi_tcq.h> |
47 | #include <scsi/scsi_host.h> | 47 | #include <scsi/scsi_host.h> |
48 | 48 | ||
49 | |||
50 | |||
51 | #define MAX_TARGETS 16 | 49 | #define MAX_TARGETS 16 |
52 | #define MAX_LUNS 8 /* 32 for 1.31 F/W */ | 50 | #define MAX_LUNS 8 /* 32 for 1.31 F/W */ |
53 | 51 | ||
@@ -57,7 +55,6 @@ | |||
57 | 55 | ||
58 | static struct qlogicpti *qptichain = NULL; | 56 | static struct qlogicpti *qptichain = NULL; |
59 | static DEFINE_SPINLOCK(qptichain_lock); | 57 | static DEFINE_SPINLOCK(qptichain_lock); |
60 | static int qptis_running = 0; | ||
61 | 58 | ||
62 | #define PACKB(a, b) (((a)<<4)|(b)) | 59 | #define PACKB(a, b) (((a)<<4)|(b)) |
63 | 60 | ||
@@ -815,173 +812,6 @@ static int __init qpti_map_queues(struct qlogicpti *qpti) | |||
815 | return 0; | 812 | return 0; |
816 | } | 813 | } |
817 | 814 | ||
818 | /* Detect all PTI Qlogic ISP's in the machine. */ | ||
819 | static int __init qlogicpti_detect(struct scsi_host_template *tpnt) | ||
820 | { | ||
821 | struct qlogicpti *qpti; | ||
822 | struct Scsi_Host *qpti_host; | ||
823 | struct sbus_bus *sbus; | ||
824 | struct sbus_dev *sdev; | ||
825 | int nqptis = 0, nqptis_in_use = 0; | ||
826 | |||
827 | tpnt->proc_name = "qlogicpti"; | ||
828 | for_each_sbus(sbus) { | ||
829 | for_each_sbusdev(sdev, sbus) { | ||
830 | /* Is this a red snapper? */ | ||
831 | if (strcmp(sdev->prom_name, "ptisp") && | ||
832 | strcmp(sdev->prom_name, "PTI,ptisp") && | ||
833 | strcmp(sdev->prom_name, "QLGC,isp") && | ||
834 | strcmp(sdev->prom_name, "SUNW,isp")) | ||
835 | continue; | ||
836 | |||
837 | /* Sometimes Antares cards come up not completely | ||
838 | * setup, and we get a report of a zero IRQ. | ||
839 | * Skip over them in such cases so we survive. | ||
840 | */ | ||
841 | if (sdev->irqs[0] == 0) { | ||
842 | printk("qpti%d: Adapter reports no interrupt, " | ||
843 | "skipping over this card.", nqptis); | ||
844 | continue; | ||
845 | } | ||
846 | |||
847 | /* Yep, register and allocate software state. */ | ||
848 | qpti_host = scsi_register(tpnt, sizeof(struct qlogicpti)); | ||
849 | if (!qpti_host) { | ||
850 | printk("QPTI: Cannot register PTI Qlogic ISP SCSI host"); | ||
851 | continue; | ||
852 | } | ||
853 | qpti = (struct qlogicpti *) qpti_host->hostdata; | ||
854 | |||
855 | /* We are wide capable, 16 targets. */ | ||
856 | qpti_host->max_id = MAX_TARGETS; | ||
857 | |||
858 | /* Setup back pointers and misc. state. */ | ||
859 | qpti->qhost = qpti_host; | ||
860 | qpti->sdev = sdev; | ||
861 | qpti->qpti_id = nqptis++; | ||
862 | qpti->prom_node = sdev->prom_node; | ||
863 | prom_getstring(qpti->prom_node, "name", | ||
864 | qpti->prom_name, | ||
865 | sizeof(qpti->prom_name)); | ||
866 | |||
867 | /* This is not correct, actually. There's a switch | ||
868 | * on the PTI cards that put them into "emulation" | ||
869 | * mode- i.e., report themselves as QLGC,isp | ||
870 | * instead of PTI,ptisp. The only real substantive | ||
871 | * difference between non-pti and pti cards is | ||
872 | * the tmon register. Which is possibly even | ||
873 | * there for Qlogic cards, but non-functional. | ||
874 | */ | ||
875 | qpti->is_pti = (strcmp (qpti->prom_name, "QLGC,isp") != 0); | ||
876 | |||
877 | qpti_chain_add(qpti); | ||
878 | if (qpti_map_regs(qpti) < 0) | ||
879 | goto fail_unlink; | ||
880 | |||
881 | if (qpti_register_irq(qpti) < 0) | ||
882 | goto fail_unmap_regs; | ||
883 | |||
884 | qpti_get_scsi_id(qpti); | ||
885 | qpti_get_bursts(qpti); | ||
886 | qpti_get_clock(qpti); | ||
887 | |||
888 | /* Clear out scsi_cmnd array. */ | ||
889 | memset(qpti->cmd_slots, 0, sizeof(qpti->cmd_slots)); | ||
890 | |||
891 | if (qpti_map_queues(qpti) < 0) | ||
892 | goto fail_free_irq; | ||
893 | |||
894 | /* Load the firmware. */ | ||
895 | if (qlogicpti_load_firmware(qpti)) | ||
896 | goto fail_unmap_queues; | ||
897 | if (qpti->is_pti) { | ||
898 | /* Check the PTI status reg. */ | ||
899 | if (qlogicpti_verify_tmon(qpti)) | ||
900 | goto fail_unmap_queues; | ||
901 | } | ||
902 | |||
903 | /* Reset the ISP and init res/req queues. */ | ||
904 | if (qlogicpti_reset_hardware(qpti_host)) | ||
905 | goto fail_unmap_queues; | ||
906 | |||
907 | printk("(Firmware v%d.%d.%d)", qpti->fware_majrev, | ||
908 | qpti->fware_minrev, qpti->fware_micrev); | ||
909 | { | ||
910 | char buffer[60]; | ||
911 | |||
912 | prom_getstring (qpti->prom_node, | ||
913 | "isp-fcode", buffer, 60); | ||
914 | if (buffer[0]) | ||
915 | printk("(Firmware %s)", buffer); | ||
916 | if (prom_getbool(qpti->prom_node, "differential")) | ||
917 | qpti->differential = 1; | ||
918 | } | ||
919 | |||
920 | printk (" [%s Wide, using %s interface]\n", | ||
921 | (qpti->ultra ? "Ultra" : "Fast"), | ||
922 | (qpti->differential ? "differential" : "single ended")); | ||
923 | |||
924 | nqptis_in_use++; | ||
925 | continue; | ||
926 | |||
927 | fail_unmap_queues: | ||
928 | #define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN) | ||
929 | sbus_free_consistent(qpti->sdev, | ||
930 | QSIZE(RES_QUEUE_LEN), | ||
931 | qpti->res_cpu, qpti->res_dvma); | ||
932 | sbus_free_consistent(qpti->sdev, | ||
933 | QSIZE(QLOGICPTI_REQ_QUEUE_LEN), | ||
934 | qpti->req_cpu, qpti->req_dvma); | ||
935 | #undef QSIZE | ||
936 | fail_free_irq: | ||
937 | free_irq(qpti->irq, qpti); | ||
938 | |||
939 | fail_unmap_regs: | ||
940 | sbus_iounmap(qpti->qregs, | ||
941 | qpti->sdev->reg_addrs[0].reg_size); | ||
942 | if (qpti->is_pti) | ||
943 | sbus_iounmap(qpti->sreg, sizeof(unsigned char)); | ||
944 | fail_unlink: | ||
945 | qpti_chain_del(qpti); | ||
946 | scsi_unregister(qpti->qhost); | ||
947 | } | ||
948 | } | ||
949 | if (nqptis) | ||
950 | printk("QPTI: Total of %d PTI Qlogic/ISP hosts found, %d actually in use.\n", | ||
951 | nqptis, nqptis_in_use); | ||
952 | qptis_running = nqptis_in_use; | ||
953 | return nqptis; | ||
954 | } | ||
955 | |||
956 | static int qlogicpti_release(struct Scsi_Host *host) | ||
957 | { | ||
958 | struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata; | ||
959 | |||
960 | /* Remove visibility from IRQ handlers. */ | ||
961 | qpti_chain_del(qpti); | ||
962 | |||
963 | /* Shut up the card. */ | ||
964 | sbus_writew(0, qpti->qregs + SBUS_CTRL); | ||
965 | |||
966 | /* Free IRQ handler and unmap Qlogic,ISP and PTI status regs. */ | ||
967 | free_irq(qpti->irq, qpti); | ||
968 | |||
969 | #define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN) | ||
970 | sbus_free_consistent(qpti->sdev, | ||
971 | QSIZE(RES_QUEUE_LEN), | ||
972 | qpti->res_cpu, qpti->res_dvma); | ||
973 | sbus_free_consistent(qpti->sdev, | ||
974 | QSIZE(QLOGICPTI_REQ_QUEUE_LEN), | ||
975 | qpti->req_cpu, qpti->req_dvma); | ||
976 | #undef QSIZE | ||
977 | |||
978 | sbus_iounmap(qpti->qregs, qpti->sdev->reg_addrs[0].reg_size); | ||
979 | if (qpti->is_pti) | ||
980 | sbus_iounmap(qpti->sreg, sizeof(unsigned char)); | ||
981 | |||
982 | return 0; | ||
983 | } | ||
984 | |||
985 | const char *qlogicpti_info(struct Scsi_Host *host) | 815 | const char *qlogicpti_info(struct Scsi_Host *host) |
986 | { | 816 | { |
987 | static char buf[80]; | 817 | static char buf[80]; |
@@ -1551,9 +1381,9 @@ static int qlogicpti_reset(struct scsi_cmnd *Cmnd) | |||
1551 | return return_status; | 1381 | return return_status; |
1552 | } | 1382 | } |
1553 | 1383 | ||
1554 | static struct scsi_host_template driver_template = { | 1384 | static struct scsi_host_template qpti_template = { |
1555 | .detect = qlogicpti_detect, | 1385 | .module = THIS_MODULE, |
1556 | .release = qlogicpti_release, | 1386 | .name = "qlogicpti", |
1557 | .info = qlogicpti_info, | 1387 | .info = qlogicpti_info, |
1558 | .queuecommand = qlogicpti_queuecommand_slow, | 1388 | .queuecommand = qlogicpti_queuecommand_slow, |
1559 | .eh_abort_handler = qlogicpti_abort, | 1389 | .eh_abort_handler = qlogicpti_abort, |
@@ -1565,8 +1395,189 @@ static struct scsi_host_template driver_template = { | |||
1565 | .use_clustering = ENABLE_CLUSTERING, | 1395 | .use_clustering = ENABLE_CLUSTERING, |
1566 | }; | 1396 | }; |
1567 | 1397 | ||
1398 | static int __devinit qpti_sbus_probe(struct of_device *dev, const struct of_device_id *match) | ||
1399 | { | ||
1400 | static int nqptis; | ||
1401 | struct sbus_dev *sdev = to_sbus_device(&dev->dev); | ||
1402 | struct device_node *dp = dev->node; | ||
1403 | struct scsi_host_template *tpnt = match->data; | ||
1404 | struct Scsi_Host *host; | ||
1405 | struct qlogicpti *qpti; | ||
1406 | char *fcode; | ||
1407 | |||
1408 | /* Sometimes Antares cards come up not completely | ||
1409 | * setup, and we get a report of a zero IRQ. | ||
1410 | */ | ||
1411 | if (sdev->irqs[0] == 0) | ||
1412 | return -ENODEV; | ||
1413 | |||
1414 | host = scsi_host_alloc(tpnt, sizeof(struct qlogicpti)); | ||
1415 | if (!host) | ||
1416 | return -ENOMEM; | ||
1417 | |||
1418 | qpti = (struct qlogicpti *) host->hostdata; | ||
1419 | |||
1420 | host->max_id = MAX_TARGETS; | ||
1421 | qpti->qhost = host; | ||
1422 | qpti->sdev = sdev; | ||
1423 | qpti->qpti_id = nqptis; | ||
1424 | qpti->prom_node = sdev->prom_node; | ||
1425 | strcpy(qpti->prom_name, sdev->ofdev.node->name); | ||
1426 | qpti->is_pti = strcmp(qpti->prom_name, "QLGC,isp"); | ||
1427 | |||
1428 | if (qpti_map_regs(qpti) < 0) | ||
1429 | goto fail_unlink; | ||
1430 | |||
1431 | if (qpti_register_irq(qpti) < 0) | ||
1432 | goto fail_unmap_regs; | ||
1433 | |||
1434 | qpti_get_scsi_id(qpti); | ||
1435 | qpti_get_bursts(qpti); | ||
1436 | qpti_get_clock(qpti); | ||
1437 | |||
1438 | /* Clear out scsi_cmnd array. */ | ||
1439 | memset(qpti->cmd_slots, 0, sizeof(qpti->cmd_slots)); | ||
1440 | |||
1441 | if (qpti_map_queues(qpti) < 0) | ||
1442 | goto fail_free_irq; | ||
1443 | |||
1444 | /* Load the firmware. */ | ||
1445 | if (qlogicpti_load_firmware(qpti)) | ||
1446 | goto fail_unmap_queues; | ||
1447 | if (qpti->is_pti) { | ||
1448 | /* Check the PTI status reg. */ | ||
1449 | if (qlogicpti_verify_tmon(qpti)) | ||
1450 | goto fail_unmap_queues; | ||
1451 | } | ||
1452 | |||
1453 | /* Reset the ISP and init res/req queues. */ | ||
1454 | if (qlogicpti_reset_hardware(host)) | ||
1455 | goto fail_unmap_queues; | ||
1456 | |||
1457 | if (scsi_add_host(host, &dev->dev)) | ||
1458 | goto fail_unmap_queues; | ||
1459 | |||
1460 | printk("(Firmware v%d.%d.%d)", qpti->fware_majrev, | ||
1461 | qpti->fware_minrev, qpti->fware_micrev); | ||
1462 | |||
1463 | fcode = of_get_property(dp, "isp-fcode", NULL); | ||
1464 | if (fcode && fcode[0]) | ||
1465 | printk("(Firmware %s)", fcode); | ||
1466 | if (of_find_property(dp, "differential", NULL) != NULL) | ||
1467 | qpti->differential = 1; | ||
1468 | |||
1469 | printk (" [%s Wide, using %s interface]\n", | ||
1470 | (qpti->ultra ? "Ultra" : "Fast"), | ||
1471 | (qpti->differential ? "differential" : "single ended")); | ||
1472 | |||
1473 | dev_set_drvdata(&sdev->ofdev.dev, qpti); | ||
1474 | |||
1475 | qpti_chain_add(qpti); | ||
1476 | |||
1477 | scsi_scan_host(host); | ||
1478 | nqptis++; | ||
1479 | |||
1480 | return 0; | ||
1481 | |||
1482 | fail_unmap_queues: | ||
1483 | #define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN) | ||
1484 | sbus_free_consistent(qpti->sdev, | ||
1485 | QSIZE(RES_QUEUE_LEN), | ||
1486 | qpti->res_cpu, qpti->res_dvma); | ||
1487 | sbus_free_consistent(qpti->sdev, | ||
1488 | QSIZE(QLOGICPTI_REQ_QUEUE_LEN), | ||
1489 | qpti->req_cpu, qpti->req_dvma); | ||
1490 | #undef QSIZE | ||
1491 | |||
1492 | fail_unmap_regs: | ||
1493 | sbus_iounmap(qpti->qregs, | ||
1494 | qpti->sdev->reg_addrs[0].reg_size); | ||
1495 | if (qpti->is_pti) | ||
1496 | sbus_iounmap(qpti->sreg, sizeof(unsigned char)); | ||
1497 | |||
1498 | fail_free_irq: | ||
1499 | free_irq(qpti->irq, qpti); | ||
1500 | |||
1501 | fail_unlink: | ||
1502 | scsi_host_put(host); | ||
1503 | |||
1504 | return -ENODEV; | ||
1505 | } | ||
1506 | |||
1507 | static int __devexit qpti_sbus_remove(struct of_device *dev) | ||
1508 | { | ||
1509 | struct qlogicpti *qpti = dev_get_drvdata(&dev->dev); | ||
1510 | |||
1511 | qpti_chain_del(qpti); | ||
1512 | |||
1513 | scsi_remove_host(qpti->qhost); | ||
1514 | |||
1515 | /* Shut up the card. */ | ||
1516 | sbus_writew(0, qpti->qregs + SBUS_CTRL); | ||
1517 | |||
1518 | /* Free IRQ handler and unmap Qlogic,ISP and PTI status regs. */ | ||
1519 | free_irq(qpti->irq, qpti); | ||
1520 | |||
1521 | #define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN) | ||
1522 | sbus_free_consistent(qpti->sdev, | ||
1523 | QSIZE(RES_QUEUE_LEN), | ||
1524 | qpti->res_cpu, qpti->res_dvma); | ||
1525 | sbus_free_consistent(qpti->sdev, | ||
1526 | QSIZE(QLOGICPTI_REQ_QUEUE_LEN), | ||
1527 | qpti->req_cpu, qpti->req_dvma); | ||
1528 | #undef QSIZE | ||
1529 | |||
1530 | sbus_iounmap(qpti->qregs, qpti->sdev->reg_addrs[0].reg_size); | ||
1531 | if (qpti->is_pti) | ||
1532 | sbus_iounmap(qpti->sreg, sizeof(unsigned char)); | ||
1533 | |||
1534 | scsi_host_put(qpti->qhost); | ||
1535 | |||
1536 | return 0; | ||
1537 | } | ||
1538 | |||
1539 | static struct of_device_id qpti_match[] = { | ||
1540 | { | ||
1541 | .name = "ptisp", | ||
1542 | .data = &qpti_template, | ||
1543 | }, | ||
1544 | { | ||
1545 | .name = "PTI,ptisp", | ||
1546 | .data = &qpti_template, | ||
1547 | }, | ||
1548 | { | ||
1549 | .name = "QLGC,isp", | ||
1550 | .data = &qpti_template, | ||
1551 | }, | ||
1552 | { | ||
1553 | .name = "SUNW,isp", | ||
1554 | .data = &qpti_template, | ||
1555 | }, | ||
1556 | {}, | ||
1557 | }; | ||
1558 | MODULE_DEVICE_TABLE(of, qpti_match); | ||
1559 | |||
1560 | static struct of_platform_driver qpti_sbus_driver = { | ||
1561 | .name = "qpti", | ||
1562 | .match_table = qpti_match, | ||
1563 | .probe = qpti_sbus_probe, | ||
1564 | .remove = __devexit_p(qpti_sbus_remove), | ||
1565 | }; | ||
1568 | 1566 | ||
1569 | #include "scsi_module.c" | 1567 | static int __init qpti_init(void) |
1568 | { | ||
1569 | return of_register_driver(&qpti_sbus_driver, &sbus_bus_type); | ||
1570 | } | ||
1571 | |||
1572 | static void __exit qpti_exit(void) | ||
1573 | { | ||
1574 | of_unregister_driver(&qpti_sbus_driver); | ||
1575 | } | ||
1570 | 1576 | ||
1577 | MODULE_DESCRIPTION("QlogicISP SBUS driver"); | ||
1578 | MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); | ||
1571 | MODULE_LICENSE("GPL"); | 1579 | MODULE_LICENSE("GPL"); |
1580 | MODULE_VERSION("2.0"); | ||
1572 | 1581 | ||
1582 | module_init(qpti_init); | ||
1583 | module_exit(qpti_exit); | ||