aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/mwl8k.c
diff options
context:
space:
mode:
authorLennert Buytenhek <buytenh@wantstofly.org>2010-01-08 12:32:01 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-01-12 14:02:10 -0500
commit1e9f9de3b17db3aa358f39d6932662324178350d (patch)
tree433f956956d7745fc996eacc4771999badd5de2c /drivers/net/wireless/mwl8k.c
parentefb7c49a68cf206f35793d7799608e1d69a209f9 (diff)
mwl8k: keep TX_DONE interrupt masked while transmit reclaim is running
By making use of the CLEAR_SEL feature of the mwl8k host interface interrupt controller, we can keep the TX_DONE interrupt source masked while the transmit reclaim tasklet is running (NAPI style) without having to touch the interrupt controller's interrupt mask register when entering or exiting polling mode, and without having to do any more register reads/writes than we do now. When CLEAR_SEL is enabled on the TX_DONE interrupt source, reading the interrupt status register will clear the TX_DONE status bit if it was set, allowing it to be set again if a new TX_DONE event arrives while we are running the TX reclaim tasklet, but such a new event will then not trigger another PCI interrupt until a zero is written to the TX_DONE interrupt status register bit. I.e., if we write a zero to the TX_DONE interrupt source bit in the interrupt status register when the TX reclaim tasklet thinks it's done, a PCI interrupt will be triggered if a new TX_DONE event arrived from the hardware between us deciding that there is no more work to do and re-enabling the TX_DONE interrupt source, thereby avoiding the classic NAPI poll mode exit race that would otherwise occur. Signed-off-by: Lennert Buytenhek <buytenh@marvell.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/mwl8k.c')
-rw-r--r--drivers/net/wireless/mwl8k.c77
1 files changed, 46 insertions, 31 deletions
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index 6598efcda5cc..3f55aa0c8db3 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -202,8 +202,8 @@ struct mwl8k_priv {
202 */ 202 */
203 struct work_struct finalize_join_worker; 203 struct work_struct finalize_join_worker;
204 204
205 /* Tasklet to reclaim TX descriptors and buffers after tx */ 205 /* Tasklet to perform TX reclaim. */
206 struct tasklet_struct tx_reclaim_task; 206 struct tasklet_struct poll_tx_task;
207}; 207};
208 208
209/* Per interface specific private data */ 209/* Per interface specific private data */
@@ -2963,13 +2963,16 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id)
2963 u32 status; 2963 u32 status;
2964 2964
2965 status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); 2965 status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
2966 iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
2967
2968 if (!status) 2966 if (!status)
2969 return IRQ_NONE; 2967 return IRQ_NONE;
2970 2968
2971 if (status & MWL8K_A2H_INT_TX_DONE) 2969 if (status & MWL8K_A2H_INT_TX_DONE) {
2972 tasklet_schedule(&priv->tx_reclaim_task); 2970 status &= ~MWL8K_A2H_INT_TX_DONE;
2971 tasklet_schedule(&priv->poll_tx_task);
2972 }
2973
2974 if (status)
2975 iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
2973 2976
2974 if (status & MWL8K_A2H_INT_RX_READY) { 2977 if (status & MWL8K_A2H_INT_RX_READY) {
2975 while (rxq_process(hw, 0, 1)) 2978 while (rxq_process(hw, 0, 1))
@@ -2990,6 +2993,35 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id)
2990 return IRQ_HANDLED; 2993 return IRQ_HANDLED;
2991} 2994}
2992 2995
2996static void mwl8k_tx_poll(unsigned long data)
2997{
2998 struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
2999 struct mwl8k_priv *priv = hw->priv;
3000 int limit;
3001 int i;
3002
3003 limit = 32;
3004
3005 spin_lock_bh(&priv->tx_lock);
3006
3007 for (i = 0; i < MWL8K_TX_QUEUES; i++)
3008 limit -= mwl8k_txq_reclaim(hw, i, limit, 0);
3009
3010 if (!priv->pending_tx_pkts && priv->tx_wait != NULL) {
3011 complete(priv->tx_wait);
3012 priv->tx_wait = NULL;
3013 }
3014
3015 spin_unlock_bh(&priv->tx_lock);
3016
3017 if (limit) {
3018 writel(~MWL8K_A2H_INT_TX_DONE,
3019 priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
3020 } else {
3021 tasklet_schedule(&priv->poll_tx_task);
3022 }
3023}
3024
2993 3025
2994/* 3026/*
2995 * Core driver operations. 3027 * Core driver operations.
@@ -3026,7 +3058,7 @@ static int mwl8k_start(struct ieee80211_hw *hw)
3026 } 3058 }
3027 3059
3028 /* Enable tx reclaim tasklet */ 3060 /* Enable tx reclaim tasklet */
3029 tasklet_enable(&priv->tx_reclaim_task); 3061 tasklet_enable(&priv->poll_tx_task);
3030 3062
3031 /* Enable interrupts */ 3063 /* Enable interrupts */
3032 iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); 3064 iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
@@ -3059,7 +3091,7 @@ static int mwl8k_start(struct ieee80211_hw *hw)
3059 if (rc) { 3091 if (rc) {
3060 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); 3092 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
3061 free_irq(priv->pdev->irq, hw); 3093 free_irq(priv->pdev->irq, hw);
3062 tasklet_disable(&priv->tx_reclaim_task); 3094 tasklet_disable(&priv->poll_tx_task);
3063 } 3095 }
3064 3096
3065 return rc; 3097 return rc;
@@ -3084,7 +3116,7 @@ static void mwl8k_stop(struct ieee80211_hw *hw)
3084 dev_kfree_skb(priv->beacon_skb); 3116 dev_kfree_skb(priv->beacon_skb);
3085 3117
3086 /* Stop tx reclaim tasklet */ 3118 /* Stop tx reclaim tasklet */
3087 tasklet_disable(&priv->tx_reclaim_task); 3119 tasklet_disable(&priv->poll_tx_task);
3088 3120
3089 /* Return all skbs to mac80211 */ 3121 /* Return all skbs to mac80211 */
3090 for (i = 0; i < MWL8K_TX_QUEUES; i++) 3122 for (i = 0; i < MWL8K_TX_QUEUES; i++)
@@ -3643,23 +3675,6 @@ static const struct ieee80211_ops mwl8k_ops = {
3643 .ampdu_action = mwl8k_ampdu_action, 3675 .ampdu_action = mwl8k_ampdu_action,
3644}; 3676};
3645 3677
3646static void mwl8k_tx_reclaim_handler(unsigned long data)
3647{
3648 int i;
3649 struct ieee80211_hw *hw = (struct ieee80211_hw *) data;
3650 struct mwl8k_priv *priv = hw->priv;
3651
3652 spin_lock_bh(&priv->tx_lock);
3653 for (i = 0; i < MWL8K_TX_QUEUES; i++)
3654 mwl8k_txq_reclaim(hw, i, INT_MAX, 0);
3655
3656 if (priv->tx_wait != NULL && !priv->pending_tx_pkts) {
3657 complete(priv->tx_wait);
3658 priv->tx_wait = NULL;
3659 }
3660 spin_unlock_bh(&priv->tx_lock);
3661}
3662
3663static void mwl8k_finalize_join_worker(struct work_struct *work) 3678static void mwl8k_finalize_join_worker(struct work_struct *work)
3664{ 3679{
3665 struct mwl8k_priv *priv = 3680 struct mwl8k_priv *priv =
@@ -3859,9 +3874,8 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
3859 INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); 3874 INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
3860 3875
3861 /* TX reclaim tasklet */ 3876 /* TX reclaim tasklet */
3862 tasklet_init(&priv->tx_reclaim_task, 3877 tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw);
3863 mwl8k_tx_reclaim_handler, (unsigned long)hw); 3878 tasklet_disable(&priv->poll_tx_task);
3864 tasklet_disable(&priv->tx_reclaim_task);
3865 3879
3866 /* Power management cookie */ 3880 /* Power management cookie */
3867 priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma); 3881 priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma);
@@ -3890,7 +3904,8 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
3890 3904
3891 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); 3905 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);
3892 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); 3906 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
3893 iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); 3907 iowrite32(MWL8K_A2H_INT_TX_DONE,
3908 priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL);
3894 iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); 3909 iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
3895 3910
3896 rc = request_irq(priv->pdev->irq, mwl8k_interrupt, 3911 rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
@@ -4018,7 +4033,7 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev)
4018 ieee80211_unregister_hw(hw); 4033 ieee80211_unregister_hw(hw);
4019 4034
4020 /* Remove tx reclaim tasklet */ 4035 /* Remove tx reclaim tasklet */
4021 tasklet_kill(&priv->tx_reclaim_task); 4036 tasklet_kill(&priv->poll_tx_task);
4022 4037
4023 /* Stop hardware */ 4038 /* Stop hardware */
4024 mwl8k_hw_reset(priv); 4039 mwl8k_hw_reset(priv);