diff options
author | Andrew Vasquez <andrew.vasquez@qlogic.com> | 2007-01-29 13:22:19 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2007-01-31 12:05:20 -0500 |
commit | a8488abefaa863a0c3a19888f03395adb3f1c6d2 (patch) | |
tree | d26c038c2a72231490db6c6e4ef9ea5d7f03862c /drivers/scsi/qla2xxx | |
parent | 3c6061236801a376d86ca75fd56d61f964611dd5 (diff) |
[SCSI] qla2xxx: Add MSI-X support.
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/qla2xxx')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_def.h | 24 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_gbl.h | 3 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_init.c | 2 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_isr.c | 215 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_os.c | 14 |
5 files changed, 247 insertions, 11 deletions
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index c4fc40f8e8ca..8d7e8cb1e703 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h | |||
@@ -2046,6 +2046,27 @@ struct isp_operations { | |||
2046 | uint32_t); | 2046 | uint32_t); |
2047 | }; | 2047 | }; |
2048 | 2048 | ||
2049 | /* MSI-X Support *************************************************************/ | ||
2050 | |||
2051 | #define QLA_MSIX_CHIP_REV_24XX 3 | ||
2052 | #define QLA_MSIX_FW_MODE(m) (((m) & (BIT_7|BIT_8|BIT_9)) >> 7) | ||
2053 | #define QLA_MSIX_FW_MODE_1(m) (QLA_MSIX_FW_MODE(m) == 1) | ||
2054 | |||
2055 | #define QLA_MSIX_DEFAULT 0x00 | ||
2056 | #define QLA_MSIX_RSP_Q 0x01 | ||
2057 | |||
2058 | #define QLA_MSIX_ENTRIES 2 | ||
2059 | #define QLA_MIDX_DEFAULT 0 | ||
2060 | #define QLA_MIDX_RSP_Q 1 | ||
2061 | |||
2062 | struct scsi_qla_host; | ||
2063 | |||
2064 | struct qla_msix_entry { | ||
2065 | int have_irq; | ||
2066 | uint16_t msix_vector; | ||
2067 | uint16_t msix_entry; | ||
2068 | }; | ||
2069 | |||
2049 | /* | 2070 | /* |
2050 | * Linux Host Adapter structure | 2071 | * Linux Host Adapter structure |
2051 | */ | 2072 | */ |
@@ -2356,6 +2377,7 @@ typedef struct scsi_qla_host { | |||
2356 | 2377 | ||
2357 | uint8_t host_str[16]; | 2378 | uint8_t host_str[16]; |
2358 | uint32_t pci_attr; | 2379 | uint32_t pci_attr; |
2380 | uint16_t chip_revision; | ||
2359 | 2381 | ||
2360 | uint16_t product_id[4]; | 2382 | uint16_t product_id[4]; |
2361 | 2383 | ||
@@ -2389,6 +2411,8 @@ typedef struct scsi_qla_host { | |||
2389 | uint16_t zio_mode; | 2411 | uint16_t zio_mode; |
2390 | uint16_t zio_timer; | 2412 | uint16_t zio_timer; |
2391 | struct fc_host_statistics fc_host_stat; | 2413 | struct fc_host_statistics fc_host_stat; |
2414 | |||
2415 | struct qla_msix_entry msix_entries[QLA_MSIX_ENTRIES]; | ||
2392 | } scsi_qla_host_t; | 2416 | } scsi_qla_host_t; |
2393 | 2417 | ||
2394 | 2418 | ||
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 32ebeec45ff0..e6ca2bc9a28d 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h | |||
@@ -225,6 +225,9 @@ extern irqreturn_t qla24xx_intr_handler(int, void *); | |||
225 | extern void qla2x00_process_response_queue(struct scsi_qla_host *); | 225 | extern void qla2x00_process_response_queue(struct scsi_qla_host *); |
226 | extern void qla24xx_process_response_queue(struct scsi_qla_host *); | 226 | extern void qla24xx_process_response_queue(struct scsi_qla_host *); |
227 | 227 | ||
228 | extern int qla2x00_request_irqs(scsi_qla_host_t *); | ||
229 | extern void qla2x00_free_irqs(scsi_qla_host_t *); | ||
230 | |||
228 | /* | 231 | /* |
229 | * Global Function Prototypes in qla_sup.c source file. | 232 | * Global Function Prototypes in qla_sup.c source file. |
230 | */ | 233 | */ |
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index a823f0bc519d..b6f0494e034c 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c | |||
@@ -293,6 +293,8 @@ qla24xx_pci_config(scsi_qla_host_t *ha) | |||
293 | d &= ~PCI_ROM_ADDRESS_ENABLE; | 293 | d &= ~PCI_ROM_ADDRESS_ENABLE; |
294 | pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); | 294 | pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); |
295 | 295 | ||
296 | pci_read_config_word(ha->pdev, PCI_REVISION_ID, &ha->chip_revision); | ||
297 | |||
296 | /* Get PCI bus information. */ | 298 | /* Get PCI bus information. */ |
297 | spin_lock_irqsave(&ha->hardware_lock, flags); | 299 | spin_lock_irqsave(&ha->hardware_lock, flags); |
298 | ha->pci_attr = RD_REG_DWORD(®->ctrl_status); | 300 | ha->pci_attr = RD_REG_DWORD(®->ctrl_status); |
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index b95fcb2c065e..e883b5fc3b08 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c | |||
@@ -1529,3 +1529,218 @@ qla24xx_ms_entry(scsi_qla_host_t *ha, struct ct_entry_24xx *pkt) | |||
1529 | qla2x00_sp_compl(ha, sp); | 1529 | qla2x00_sp_compl(ha, sp); |
1530 | } | 1530 | } |
1531 | 1531 | ||
1532 | static irqreturn_t | ||
1533 | qla24xx_msix_rsp_q(int irq, void *dev_id) | ||
1534 | { | ||
1535 | scsi_qla_host_t *ha; | ||
1536 | struct device_reg_24xx __iomem *reg; | ||
1537 | unsigned long flags; | ||
1538 | |||
1539 | ha = dev_id; | ||
1540 | reg = &ha->iobase->isp24; | ||
1541 | |||
1542 | spin_lock_irqsave(&ha->hardware_lock, flags); | ||
1543 | |||
1544 | qla24xx_process_response_queue(ha); | ||
1545 | |||
1546 | WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); | ||
1547 | RD_REG_DWORD_RELAXED(®->hccr); | ||
1548 | |||
1549 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
1550 | |||
1551 | return IRQ_HANDLED; | ||
1552 | } | ||
1553 | |||
1554 | static irqreturn_t | ||
1555 | qla24xx_msix_default(int irq, void *dev_id) | ||
1556 | { | ||
1557 | scsi_qla_host_t *ha; | ||
1558 | struct device_reg_24xx __iomem *reg; | ||
1559 | int status; | ||
1560 | unsigned long flags; | ||
1561 | unsigned long iter; | ||
1562 | uint32_t stat; | ||
1563 | uint32_t hccr; | ||
1564 | uint16_t mb[4]; | ||
1565 | |||
1566 | ha = dev_id; | ||
1567 | reg = &ha->iobase->isp24; | ||
1568 | status = 0; | ||
1569 | |||
1570 | spin_lock_irqsave(&ha->hardware_lock, flags); | ||
1571 | for (iter = 50; iter--; ) { | ||
1572 | stat = RD_REG_DWORD(®->host_status); | ||
1573 | if (stat & HSRX_RISC_PAUSED) { | ||
1574 | hccr = RD_REG_DWORD(®->hccr); | ||
1575 | |||
1576 | qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " | ||
1577 | "Dumping firmware!\n", hccr); | ||
1578 | ha->isp_ops.fw_dump(ha, 1); | ||
1579 | set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); | ||
1580 | break; | ||
1581 | } else if ((stat & HSRX_RISC_INT) == 0) | ||
1582 | break; | ||
1583 | |||
1584 | switch (stat & 0xff) { | ||
1585 | case 0x1: | ||
1586 | case 0x2: | ||
1587 | case 0x10: | ||
1588 | case 0x11: | ||
1589 | qla24xx_mbx_completion(ha, MSW(stat)); | ||
1590 | status |= MBX_INTERRUPT; | ||
1591 | |||
1592 | break; | ||
1593 | case 0x12: | ||
1594 | mb[0] = MSW(stat); | ||
1595 | mb[1] = RD_REG_WORD(®->mailbox1); | ||
1596 | mb[2] = RD_REG_WORD(®->mailbox2); | ||
1597 | mb[3] = RD_REG_WORD(®->mailbox3); | ||
1598 | qla2x00_async_event(ha, mb); | ||
1599 | break; | ||
1600 | case 0x13: | ||
1601 | qla24xx_process_response_queue(ha); | ||
1602 | break; | ||
1603 | default: | ||
1604 | DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " | ||
1605 | "(%d).\n", | ||
1606 | ha->host_no, stat & 0xff)); | ||
1607 | break; | ||
1608 | } | ||
1609 | WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); | ||
1610 | RD_REG_DWORD_RELAXED(®->hccr); | ||
1611 | } | ||
1612 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
1613 | |||
1614 | if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && | ||
1615 | (status & MBX_INTERRUPT) && ha->flags.mbox_int) { | ||
1616 | spin_lock_irqsave(&ha->mbx_reg_lock, flags); | ||
1617 | |||
1618 | set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); | ||
1619 | up(&ha->mbx_intr_sem); | ||
1620 | |||
1621 | spin_unlock_irqrestore(&ha->mbx_reg_lock, flags); | ||
1622 | } | ||
1623 | |||
1624 | return IRQ_HANDLED; | ||
1625 | } | ||
1626 | |||
1627 | /* Interrupt handling helpers. */ | ||
1628 | |||
1629 | struct qla_init_msix_entry { | ||
1630 | uint16_t entry; | ||
1631 | uint16_t index; | ||
1632 | const char *name; | ||
1633 | irqreturn_t (*handler)(int, void *); | ||
1634 | }; | ||
1635 | |||
1636 | static struct qla_init_msix_entry imsix_entries[QLA_MSIX_ENTRIES] = { | ||
1637 | { QLA_MSIX_DEFAULT, QLA_MIDX_DEFAULT, | ||
1638 | "qla2xxx (default)", qla24xx_msix_default }, | ||
1639 | |||
1640 | { QLA_MSIX_RSP_Q, QLA_MIDX_RSP_Q, | ||
1641 | "qla2xxx (rsp_q)", qla24xx_msix_rsp_q }, | ||
1642 | }; | ||
1643 | |||
1644 | static void | ||
1645 | qla24xx_disable_msix(scsi_qla_host_t *ha) | ||
1646 | { | ||
1647 | int i; | ||
1648 | struct qla_msix_entry *qentry; | ||
1649 | |||
1650 | for (i = 0; i < QLA_MSIX_ENTRIES; i++) { | ||
1651 | qentry = &ha->msix_entries[imsix_entries[i].index]; | ||
1652 | if (qentry->have_irq) | ||
1653 | free_irq(qentry->msix_vector, ha); | ||
1654 | } | ||
1655 | pci_disable_msix(ha->pdev); | ||
1656 | } | ||
1657 | |||
1658 | static int | ||
1659 | qla24xx_enable_msix(scsi_qla_host_t *ha) | ||
1660 | { | ||
1661 | int i, ret; | ||
1662 | struct msix_entry entries[QLA_MSIX_ENTRIES]; | ||
1663 | struct qla_msix_entry *qentry; | ||
1664 | |||
1665 | for (i = 0; i < QLA_MSIX_ENTRIES; i++) | ||
1666 | entries[i].entry = imsix_entries[i].entry; | ||
1667 | |||
1668 | ret = pci_enable_msix(ha->pdev, entries, ARRAY_SIZE(entries)); | ||
1669 | if (ret) { | ||
1670 | qla_printk(KERN_WARNING, ha, | ||
1671 | "MSI-X: Failed to enable support -- %d/%d\n", | ||
1672 | QLA_MSIX_ENTRIES, ret); | ||
1673 | goto msix_out; | ||
1674 | } | ||
1675 | ha->flags.msix_enabled = 1; | ||
1676 | |||
1677 | for (i = 0; i < QLA_MSIX_ENTRIES; i++) { | ||
1678 | qentry = &ha->msix_entries[imsix_entries[i].index]; | ||
1679 | qentry->msix_vector = entries[i].vector; | ||
1680 | qentry->msix_entry = entries[i].entry; | ||
1681 | qentry->have_irq = 0; | ||
1682 | ret = request_irq(qentry->msix_vector, | ||
1683 | imsix_entries[i].handler, 0, imsix_entries[i].name, ha); | ||
1684 | if (ret) { | ||
1685 | qla_printk(KERN_WARNING, ha, | ||
1686 | "MSI-X: Unable to register handler -- %x/%d.\n", | ||
1687 | imsix_entries[i].index, ret); | ||
1688 | qla24xx_disable_msix(ha); | ||
1689 | goto msix_out; | ||
1690 | } | ||
1691 | qentry->have_irq = 1; | ||
1692 | } | ||
1693 | |||
1694 | msix_out: | ||
1695 | return ret; | ||
1696 | } | ||
1697 | |||
1698 | int | ||
1699 | qla2x00_request_irqs(scsi_qla_host_t *ha) | ||
1700 | { | ||
1701 | int ret; | ||
1702 | |||
1703 | /* If possible, enable MSI-X. */ | ||
1704 | if (!IS_QLA2432(ha)) | ||
1705 | goto skip_msix; | ||
1706 | |||
1707 | if (ha->chip_revision < QLA_MSIX_CHIP_REV_24XX || | ||
1708 | !QLA_MSIX_FW_MODE_1(ha->fw_attributes)) { | ||
1709 | DEBUG2(qla_printk(KERN_WARNING, ha, | ||
1710 | "MSI-X: Unsupported ISP2432 (0x%X, 0x%X).\n", | ||
1711 | ha->chip_revision, ha->fw_attributes)); | ||
1712 | |||
1713 | goto skip_msix; | ||
1714 | } | ||
1715 | |||
1716 | ret = qla24xx_enable_msix(ha); | ||
1717 | if (!ret) { | ||
1718 | DEBUG2(qla_printk(KERN_INFO, ha, | ||
1719 | "MSI-X: Enabled (0x%X, 0x%X).\n", ha->chip_revision, | ||
1720 | ha->fw_attributes)); | ||
1721 | return ret; | ||
1722 | } | ||
1723 | qla_printk(KERN_WARNING, ha, | ||
1724 | "MSI-X: Falling back-to INTa mode -- %d.\n", ret); | ||
1725 | skip_msix: | ||
1726 | ret = request_irq(ha->pdev->irq, ha->isp_ops.intr_handler, | ||
1727 | IRQF_DISABLED|IRQF_SHARED, QLA2XXX_DRIVER_NAME, ha); | ||
1728 | if (ret) { | ||
1729 | qla_printk(KERN_WARNING, ha, | ||
1730 | "Failed to reserve interrupt %d already in use.\n", | ||
1731 | ha->pdev->irq); | ||
1732 | } | ||
1733 | ha->host->irq = ha->pdev->irq; | ||
1734 | |||
1735 | return ret; | ||
1736 | } | ||
1737 | |||
1738 | void | ||
1739 | qla2x00_free_irqs(scsi_qla_host_t *ha) | ||
1740 | { | ||
1741 | |||
1742 | if (ha->flags.msix_enabled) | ||
1743 | qla24xx_disable_msix(ha); | ||
1744 | else if (ha->host->irq) | ||
1745 | free_irq(ha->host->irq, ha); | ||
1746 | } | ||
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index d03523d3bf38..2bd70bb82aab 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c | |||
@@ -1612,15 +1612,9 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) | |||
1612 | host->max_lun = MAX_LUNS; | 1612 | host->max_lun = MAX_LUNS; |
1613 | host->transportt = qla2xxx_transport_template; | 1613 | host->transportt = qla2xxx_transport_template; |
1614 | 1614 | ||
1615 | ret = request_irq(pdev->irq, ha->isp_ops.intr_handler, | 1615 | ret = qla2x00_request_irqs(ha); |
1616 | IRQF_DISABLED|IRQF_SHARED, QLA2XXX_DRIVER_NAME, ha); | 1616 | if (ret) |
1617 | if (ret) { | ||
1618 | qla_printk(KERN_WARNING, ha, | ||
1619 | "Failed to reserve interrupt %d already in use.\n", | ||
1620 | pdev->irq); | ||
1621 | goto probe_failed; | 1617 | goto probe_failed; |
1622 | } | ||
1623 | host->irq = pdev->irq; | ||
1624 | 1618 | ||
1625 | /* Initialized the timer */ | 1619 | /* Initialized the timer */ |
1626 | qla2x00_start_timer(ha, qla2x00_timer, WATCH_INTERVAL); | 1620 | qla2x00_start_timer(ha, qla2x00_timer, WATCH_INTERVAL); |
@@ -1750,9 +1744,7 @@ qla2x00_free_device(scsi_qla_host_t *ha) | |||
1750 | 1744 | ||
1751 | qla2x00_mem_free(ha); | 1745 | qla2x00_mem_free(ha); |
1752 | 1746 | ||
1753 | /* Detach interrupts */ | 1747 | qla2x00_free_irqs(ha); |
1754 | if (ha->host->irq) | ||
1755 | free_irq(ha->host->irq, ha); | ||
1756 | 1748 | ||
1757 | /* release io space registers */ | 1749 | /* release io space registers */ |
1758 | if (ha->iobase) | 1750 | if (ha->iobase) |