diff options
| -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); | ||
