aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/iwl-core.c
diff options
context:
space:
mode:
authorMohamed Abbas <mohamed.abbas@intel.com>2009-05-22 14:01:50 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-05-22 14:06:05 -0400
commitef850d7cb301bda9155c096269557a4586b58071 (patch)
treee9dc0190a2a575a9515511043805fbdb73ac02e6 /drivers/net/wireless/iwlwifi/iwl-core.c
parenta2b0f02e4795bfde5f11720a10af8923cb98b654 (diff)
iwlcore: support ICT interrupt
Add ICT interrupt handler support, ICT should improve CPU utilization since it does not require target read which is very expensive. This interrupt handler only added to 5000 cards and newer. Device will write interrupts to ICT shared table to inform driver about its interrupts. These patches will not touch 3945 and 4965 interrupt handlers and tasklet. Signed-off-by: Mohamed Abbas <mohamed.abbas@intel.com> Signed-off-by: Reinette Chatre <reinette.chatre@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-core.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c266
1 files changed, 264 insertions, 2 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index a15f7955845b..12f018392a44 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -39,6 +39,7 @@
39#include "iwl-rfkill.h" 39#include "iwl-rfkill.h"
40#include "iwl-power.h" 40#include "iwl-power.h"
41#include "iwl-sta.h" 41#include "iwl-sta.h"
42#include "iwl-helpers.h"
42 43
43 44
44MODULE_DESCRIPTION("iwl core"); 45MODULE_DESCRIPTION("iwl core");
@@ -59,6 +60,8 @@ MODULE_LICENSE("GPL");
59 IWL_RATE_##pp##M_INDEX, \ 60 IWL_RATE_##pp##M_INDEX, \
60 IWL_RATE_##np##M_INDEX } 61 IWL_RATE_##np##M_INDEX }
61 62
63static irqreturn_t iwl_isr(int irq, void *data);
64
62/* 65/*
63 * Parameter order: 66 * Parameter order:
64 * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate 67 * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate
@@ -1501,7 +1504,266 @@ void iwl_enable_interrupts(struct iwl_priv *priv)
1501} 1504}
1502EXPORT_SYMBOL(iwl_enable_interrupts); 1505EXPORT_SYMBOL(iwl_enable_interrupts);
1503 1506
1504irqreturn_t iwl_isr(int irq, void *data) 1507
1508#define ICT_COUNT (PAGE_SIZE/sizeof(u32))
1509
1510/* Free dram table */
1511void iwl_free_isr_ict(struct iwl_priv *priv)
1512{
1513 if (priv->ict_tbl_vir) {
1514 pci_free_consistent(priv->pci_dev, (sizeof(u32) * ICT_COUNT) +
1515 PAGE_SIZE, priv->ict_tbl_vir,
1516 priv->ict_tbl_dma);
1517 priv->ict_tbl_vir = NULL;
1518 }
1519}
1520EXPORT_SYMBOL(iwl_free_isr_ict);
1521
1522
1523/* allocate dram shared table it is a PAGE_SIZE aligned
1524 * also reset all data related to ICT table interrupt.
1525 */
1526int iwl_alloc_isr_ict(struct iwl_priv *priv)
1527{
1528
1529 if (priv->cfg->use_isr_legacy)
1530 return 0;
1531 /* allocate shrared data table */
1532 priv->ict_tbl_vir = pci_alloc_consistent(priv->pci_dev, (sizeof(u32) *
1533 ICT_COUNT) + PAGE_SIZE,
1534 &priv->ict_tbl_dma);
1535 if (!priv->ict_tbl_vir)
1536 return -ENOMEM;
1537
1538 /* align table to PAGE_SIZE boundry */
1539 priv->aligned_ict_tbl_dma = ALIGN(priv->ict_tbl_dma, PAGE_SIZE);
1540
1541 IWL_DEBUG_ISR(priv, "ict dma addr %Lx dma aligned %Lx diff %d\n",
1542 (unsigned long long)priv->ict_tbl_dma,
1543 (unsigned long long)priv->aligned_ict_tbl_dma,
1544 (int)(priv->aligned_ict_tbl_dma - priv->ict_tbl_dma));
1545
1546 priv->ict_tbl = priv->ict_tbl_vir +
1547 (priv->aligned_ict_tbl_dma - priv->ict_tbl_dma);
1548
1549 IWL_DEBUG_ISR(priv, "ict vir addr %p vir aligned %p diff %d\n",
1550 priv->ict_tbl, priv->ict_tbl_vir,
1551 (int)(priv->aligned_ict_tbl_dma - priv->ict_tbl_dma));
1552
1553 /* reset table and index to all 0 */
1554 memset(priv->ict_tbl_vir,0, (sizeof(u32) * ICT_COUNT) + PAGE_SIZE);
1555 priv->ict_index = 0;
1556
1557 return 0;
1558}
1559EXPORT_SYMBOL(iwl_alloc_isr_ict);
1560
1561/* Device is going up inform it about using ICT interrupt table,
1562 * also we need to tell the driver to start using ICT interrupt.
1563 */
1564int iwl_reset_ict(struct iwl_priv *priv)
1565{
1566 u32 val;
1567 unsigned long flags;
1568
1569 if (!priv->ict_tbl_vir)
1570 return 0;
1571
1572 spin_lock_irqsave(&priv->lock, flags);
1573 iwl_disable_interrupts(priv);
1574
1575 memset(&priv->ict_tbl[0],0, sizeof(u32) * ICT_COUNT);
1576
1577 val = priv->aligned_ict_tbl_dma >> PAGE_SHIFT;
1578
1579 val |= CSR_DRAM_INT_TBL_ENABLE;
1580 val |= CSR_DRAM_INIT_TBL_WRAP_CHECK;
1581
1582 IWL_DEBUG_ISR(priv, "CSR_DRAM_INT_TBL_REG =0x%X "
1583 "aligned dma address %Lx\n",
1584 val, (unsigned long long)priv->aligned_ict_tbl_dma);
1585
1586 iwl_write32(priv, CSR_DRAM_INT_TBL_REG, val);
1587 priv->use_ict = true;
1588 priv->ict_index = 0;
1589 iwl_write32(priv, CSR_INT, CSR_INI_SET_MASK);
1590 iwl_enable_interrupts(priv);
1591 spin_unlock_irqrestore(&priv->lock, flags);
1592
1593 return 0;
1594}
1595EXPORT_SYMBOL(iwl_reset_ict);
1596
1597/* Device is going down disable ict interrupt usage */
1598void iwl_disable_ict(struct iwl_priv *priv)
1599{
1600 unsigned long flags;
1601
1602 spin_lock_irqsave(&priv->lock, flags);
1603 priv->use_ict = false;
1604 spin_unlock_irqrestore(&priv->lock, flags);
1605}
1606EXPORT_SYMBOL(iwl_disable_ict);
1607
1608/* interrupt handler using ict table, with this interrupt driver will
1609 * stop using INTA register to get device's interrupt, reading this register
1610 * is expensive, device will write interrupts in ICT dram table, increment
1611 * index then will fire interrupt to driver, driver will OR all ICT table
1612 * entries from current index up to table entry with 0 value. the result is
1613 * the interrupt we need to service, driver will set the entries back to 0 and
1614 * set index.
1615 */
1616irqreturn_t iwl_isr_ict(int irq, void *data)
1617{
1618 struct iwl_priv *priv = data;
1619 u32 inta, inta_mask;
1620 u32 val = 0;
1621
1622 if (!priv)
1623 return IRQ_NONE;
1624
1625 /* dram interrupt table not set yet,
1626 * use legacy interrupt.
1627 */
1628 if (!priv->use_ict)
1629 return iwl_isr(irq, data);
1630
1631 spin_lock(&priv->lock);
1632
1633 /* Disable (but don't clear!) interrupts here to avoid
1634 * back-to-back ISRs and sporadic interrupts from our NIC.
1635 * If we have something to service, the tasklet will re-enable ints.
1636 * If we *don't* have something, we'll re-enable before leaving here.
1637 */
1638 inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */
1639 iwl_write32(priv, CSR_INT_MASK, 0x00000000);
1640
1641
1642 /* Ignore interrupt if there's nothing in NIC to service.
1643 * This may be due to IRQ shared with another device,
1644 * or due to sporadic interrupts thrown from our NIC. */
1645 if (!priv->ict_tbl[priv->ict_index]) {
1646 IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n");
1647 goto none;
1648 }
1649
1650 /* read all entries that not 0 start with ict_index */
1651 while (priv->ict_tbl[priv->ict_index]) {
1652
1653 val |= priv->ict_tbl[priv->ict_index];
1654 IWL_DEBUG_ISR(priv, "ICT index %d value 0x%08X\n",
1655 priv->ict_index,
1656 priv->ict_tbl[priv->ict_index]);
1657 priv->ict_tbl[priv->ict_index] = 0;
1658 priv->ict_index = iwl_queue_inc_wrap(priv->ict_index,
1659 ICT_COUNT);
1660
1661 }
1662
1663 /* We should not get this value, just ignore it. */
1664 if (val == 0xffffffff)
1665 val = 0;
1666
1667 inta = (0xff & val) | ((0xff00 & val) << 16);
1668 IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n",
1669 inta, inta_mask, val);
1670
1671 inta &= CSR_INI_SET_MASK;
1672 priv->inta |= inta;
1673
1674 /* iwl_irq_tasklet() will service interrupts and re-enable them */
1675 if (likely(inta))
1676 tasklet_schedule(&priv->irq_tasklet);
1677 else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta) {
1678 /* Allow interrupt if was disabled by this handler and
1679 * no tasklet was schedules, We should not enable interrupt,
1680 * tasklet will enable it.
1681 */
1682 iwl_enable_interrupts(priv);
1683 }
1684
1685 spin_unlock(&priv->lock);
1686 return IRQ_HANDLED;
1687
1688 none:
1689 /* re-enable interrupts here since we don't have anything to service.
1690 * only Re-enable if disabled by irq.
1691 */
1692 if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta)
1693 iwl_enable_interrupts(priv);
1694
1695 spin_unlock(&priv->lock);
1696 return IRQ_NONE;
1697}
1698EXPORT_SYMBOL(iwl_isr_ict);
1699
1700
1701static irqreturn_t iwl_isr(int irq, void *data)
1702{
1703 struct iwl_priv *priv = data;
1704 u32 inta, inta_mask;
1705 u32 inta_fh;
1706
1707 if (!priv)
1708 return IRQ_NONE;
1709
1710 spin_lock(&priv->lock);
1711
1712 /* Disable (but don't clear!) interrupts here to avoid
1713 * back-to-back ISRs and sporadic interrupts from our NIC.
1714 * If we have something to service, the tasklet will re-enable ints.
1715 * If we *don't* have something, we'll re-enable before leaving here. */
1716 inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */
1717 iwl_write32(priv, CSR_INT_MASK, 0x00000000);
1718
1719 /* Discover which interrupts are active/pending */
1720 inta = iwl_read32(priv, CSR_INT);
1721
1722 /* Ignore interrupt if there's nothing in NIC to service.
1723 * This may be due to IRQ shared with another device,
1724 * or due to sporadic interrupts thrown from our NIC. */
1725 if (!inta) {
1726 IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n");
1727 goto none;
1728 }
1729
1730 if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
1731 /* Hardware disappeared. It might have already raised
1732 * an interrupt */
1733 IWL_WARN(priv, "HARDWARE GONE?? INTA == 0x%08x\n", inta);
1734 goto unplugged;
1735 }
1736
1737#ifdef CONFIG_IWLWIFI_DEBUG
1738 if (priv->debug_level & (IWL_DL_ISR)) {
1739 inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS);
1740 IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, "
1741 "fh 0x%08x\n", inta, inta_mask, inta_fh);
1742 }
1743#endif
1744
1745 priv->inta |= inta;
1746 /* iwl_irq_tasklet() will service interrupts and re-enable them */
1747 if (likely(inta))
1748 tasklet_schedule(&priv->irq_tasklet);
1749 else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta)
1750 iwl_enable_interrupts(priv);
1751
1752 unplugged:
1753 spin_unlock(&priv->lock);
1754 return IRQ_HANDLED;
1755
1756 none:
1757 /* re-enable interrupts here since we don't have anything to service. */
1758 /* only Re-enable if diabled by irq and no schedules tasklet. */
1759 if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta)
1760 iwl_enable_interrupts(priv);
1761
1762 spin_unlock(&priv->lock);
1763 return IRQ_NONE;
1764}
1765
1766irqreturn_t iwl_isr_legacy(int irq, void *data)
1505{ 1767{
1506 struct iwl_priv *priv = data; 1768 struct iwl_priv *priv = data;
1507 u32 inta, inta_mask; 1769 u32 inta, inta_mask;
@@ -1558,7 +1820,7 @@ irqreturn_t iwl_isr(int irq, void *data)
1558 spin_unlock(&priv->lock); 1820 spin_unlock(&priv->lock);
1559 return IRQ_NONE; 1821 return IRQ_NONE;
1560} 1822}
1561EXPORT_SYMBOL(iwl_isr); 1823EXPORT_SYMBOL(iwl_isr_legacy);
1562 1824
1563int iwl_send_bt_config(struct iwl_priv *priv) 1825int iwl_send_bt_config(struct iwl_priv *priv)
1564{ 1826{