aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2011-07-11 10:44:57 -0400
committerWey-Yi Guy <wey-yi.w.guy@intel.com>2011-07-16 10:39:34 -0400
commit1a361cd838173879672cb0f0ebe1e7654d7edff6 (patch)
treecc63a7fa0f598cf7117a219b2fc96a0d43b64b95 /drivers/net
parentab6cf8e816bad473a1496f2006bea3a3849c2519 (diff)
iwlagn: move all the ICT related functions to iwl-trans-rx-pcie.c
Since the ICT is transport related, move all its functions to the transport layer. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/iwlwifi/Makefile2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-ict.c310
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h10
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-rx-pcie.c285
6 files changed, 297 insertions, 315 deletions
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
index 1468e16855ec..48ab9142af38 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/iwlwifi/Makefile
@@ -8,7 +8,7 @@ iwlagn-objs += iwl-agn-tt.o iwl-agn-sta.o iwl-agn-eeprom.o
8iwlagn-objs += iwl-core.o iwl-eeprom.o iwl-power.o 8iwlagn-objs += iwl-core.o iwl-eeprom.o iwl-power.o
9iwlagn-objs += iwl-rx.o iwl-sta.o 9iwlagn-objs += iwl-rx.o iwl-sta.o
10iwlagn-objs += iwl-scan.o iwl-led.o 10iwlagn-objs += iwl-scan.o iwl-led.o
11iwlagn-objs += iwl-agn-rxon.o iwl-agn-ict.o 11iwlagn-objs += iwl-agn-rxon.o
12iwlagn-objs += iwl-5000.o 12iwlagn-objs += iwl-5000.o
13iwlagn-objs += iwl-6000.o 13iwlagn-objs += iwl-6000.o
14iwlagn-objs += iwl-1000.o 14iwlagn-objs += iwl-1000.o
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-ict.c b/drivers/net/wireless/iwlwifi/iwl-agn-ict.c
deleted file mode 100644
index 9a2eb1e426b5..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-agn-ict.c
+++ /dev/null
@@ -1,310 +0,0 @@
1/******************************************************************************
2 *
3 * GPL LICENSE SUMMARY
4 *
5 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of version 2 of the GNU General Public License as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
19 * USA
20 *
21 * The full GNU General Public License is included in this distribution
22 * in the file called LICENSE.GPL.
23 *
24 * Contact Information:
25 * Intel Linux Wireless <ilw@linux.intel.com>
26 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
27 *****************************************************************************/
28
29#include <linux/kernel.h>
30#include <linux/module.h>
31#include <linux/etherdevice.h>
32#include <linux/sched.h>
33#include <linux/gfp.h>
34#include <net/mac80211.h>
35
36#include "iwl-dev.h"
37#include "iwl-core.h"
38#include "iwl-agn.h"
39#include "iwl-helpers.h"
40
41#define ICT_COUNT (PAGE_SIZE/sizeof(u32))
42
43/* Free dram table */
44void iwl_free_isr_ict(struct iwl_priv *priv)
45{
46 if (priv->_agn.ict_tbl_vir) {
47 dma_free_coherent(priv->bus.dev,
48 (sizeof(u32) * ICT_COUNT) + PAGE_SIZE,
49 priv->_agn.ict_tbl_vir,
50 priv->_agn.ict_tbl_dma);
51 priv->_agn.ict_tbl_vir = NULL;
52 memset(&priv->_agn.ict_tbl_dma, 0,
53 sizeof(priv->_agn.ict_tbl_dma));
54 memset(&priv->_agn.aligned_ict_tbl_dma, 0,
55 sizeof(priv->_agn.aligned_ict_tbl_dma));
56 }
57}
58
59
60/* allocate dram shared table it is a PAGE_SIZE aligned
61 * also reset all data related to ICT table interrupt.
62 */
63int iwl_alloc_isr_ict(struct iwl_priv *priv)
64{
65
66 /* allocate shrared data table */
67 priv->_agn.ict_tbl_vir =
68 dma_alloc_coherent(priv->bus.dev,
69 (sizeof(u32) * ICT_COUNT) + PAGE_SIZE,
70 &priv->_agn.ict_tbl_dma, GFP_KERNEL);
71 if (!priv->_agn.ict_tbl_vir)
72 return -ENOMEM;
73
74 /* align table to PAGE_SIZE boundary */
75 priv->_agn.aligned_ict_tbl_dma = ALIGN(priv->_agn.ict_tbl_dma, PAGE_SIZE);
76
77 IWL_DEBUG_ISR(priv, "ict dma addr %Lx dma aligned %Lx diff %d\n",
78 (unsigned long long)priv->_agn.ict_tbl_dma,
79 (unsigned long long)priv->_agn.aligned_ict_tbl_dma,
80 (int)(priv->_agn.aligned_ict_tbl_dma - priv->_agn.ict_tbl_dma));
81
82 priv->_agn.ict_tbl = priv->_agn.ict_tbl_vir +
83 (priv->_agn.aligned_ict_tbl_dma - priv->_agn.ict_tbl_dma);
84
85 IWL_DEBUG_ISR(priv, "ict vir addr %p vir aligned %p diff %d\n",
86 priv->_agn.ict_tbl, priv->_agn.ict_tbl_vir,
87 (int)(priv->_agn.aligned_ict_tbl_dma - priv->_agn.ict_tbl_dma));
88
89 /* reset table and index to all 0 */
90 memset(priv->_agn.ict_tbl_vir,0, (sizeof(u32) * ICT_COUNT) + PAGE_SIZE);
91 priv->_agn.ict_index = 0;
92
93 /* add periodic RX interrupt */
94 priv->inta_mask |= CSR_INT_BIT_RX_PERIODIC;
95 return 0;
96}
97
98/* Device is going up inform it about using ICT interrupt table,
99 * also we need to tell the driver to start using ICT interrupt.
100 */
101int iwl_reset_ict(struct iwl_priv *priv)
102{
103 u32 val;
104 unsigned long flags;
105
106 if (!priv->_agn.ict_tbl_vir)
107 return 0;
108
109 spin_lock_irqsave(&priv->lock, flags);
110 iwl_disable_interrupts(priv);
111
112 memset(&priv->_agn.ict_tbl[0], 0, sizeof(u32) * ICT_COUNT);
113
114 val = priv->_agn.aligned_ict_tbl_dma >> PAGE_SHIFT;
115
116 val |= CSR_DRAM_INT_TBL_ENABLE;
117 val |= CSR_DRAM_INIT_TBL_WRAP_CHECK;
118
119 IWL_DEBUG_ISR(priv, "CSR_DRAM_INT_TBL_REG =0x%X "
120 "aligned dma address %Lx\n",
121 val, (unsigned long long)priv->_agn.aligned_ict_tbl_dma);
122
123 iwl_write32(priv, CSR_DRAM_INT_TBL_REG, val);
124 priv->_agn.use_ict = true;
125 priv->_agn.ict_index = 0;
126 iwl_write32(priv, CSR_INT, priv->inta_mask);
127 iwl_enable_interrupts(priv);
128 spin_unlock_irqrestore(&priv->lock, flags);
129
130 return 0;
131}
132
133/* Device is going down disable ict interrupt usage */
134void iwl_disable_ict(struct iwl_priv *priv)
135{
136 unsigned long flags;
137
138 spin_lock_irqsave(&priv->lock, flags);
139 priv->_agn.use_ict = false;
140 spin_unlock_irqrestore(&priv->lock, flags);
141}
142
143static irqreturn_t iwl_isr(int irq, void *data)
144{
145 struct iwl_priv *priv = data;
146 u32 inta, inta_mask;
147 unsigned long flags;
148#ifdef CONFIG_IWLWIFI_DEBUG
149 u32 inta_fh;
150#endif
151 if (!priv)
152 return IRQ_NONE;
153
154 spin_lock_irqsave(&priv->lock, flags);
155
156 /* Disable (but don't clear!) interrupts here to avoid
157 * back-to-back ISRs and sporadic interrupts from our NIC.
158 * If we have something to service, the tasklet will re-enable ints.
159 * If we *don't* have something, we'll re-enable before leaving here. */
160 inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */
161 iwl_write32(priv, CSR_INT_MASK, 0x00000000);
162
163 /* Discover which interrupts are active/pending */
164 inta = iwl_read32(priv, CSR_INT);
165
166 /* Ignore interrupt if there's nothing in NIC to service.
167 * This may be due to IRQ shared with another device,
168 * or due to sporadic interrupts thrown from our NIC. */
169 if (!inta) {
170 IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n");
171 goto none;
172 }
173
174 if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
175 /* Hardware disappeared. It might have already raised
176 * an interrupt */
177 IWL_WARN(priv, "HARDWARE GONE?? INTA == 0x%08x\n", inta);
178 goto unplugged;
179 }
180
181#ifdef CONFIG_IWLWIFI_DEBUG
182 if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) {
183 inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS);
184 IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, "
185 "fh 0x%08x\n", inta, inta_mask, inta_fh);
186 }
187#endif
188
189 priv->_agn.inta |= inta;
190 /* iwl_irq_tasklet() will service interrupts and re-enable them */
191 if (likely(inta))
192 tasklet_schedule(&priv->irq_tasklet);
193 else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta)
194 iwl_enable_interrupts(priv);
195
196 unplugged:
197 spin_unlock_irqrestore(&priv->lock, flags);
198 return IRQ_HANDLED;
199
200 none:
201 /* re-enable interrupts here since we don't have anything to service. */
202 /* only Re-enable if disabled by irq and no schedules tasklet. */
203 if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta)
204 iwl_enable_interrupts(priv);
205
206 spin_unlock_irqrestore(&priv->lock, flags);
207 return IRQ_NONE;
208}
209
210/* interrupt handler using ict table, with this interrupt driver will
211 * stop using INTA register to get device's interrupt, reading this register
212 * is expensive, device will write interrupts in ICT dram table, increment
213 * index then will fire interrupt to driver, driver will OR all ICT table
214 * entries from current index up to table entry with 0 value. the result is
215 * the interrupt we need to service, driver will set the entries back to 0 and
216 * set index.
217 */
218irqreturn_t iwl_isr_ict(int irq, void *data)
219{
220 struct iwl_priv *priv = data;
221 u32 inta, inta_mask;
222 u32 val = 0;
223 unsigned long flags;
224
225 if (!priv)
226 return IRQ_NONE;
227
228 /* dram interrupt table not set yet,
229 * use legacy interrupt.
230 */
231 if (!priv->_agn.use_ict)
232 return iwl_isr(irq, data);
233
234 spin_lock_irqsave(&priv->lock, flags);
235
236 /* Disable (but don't clear!) interrupts here to avoid
237 * back-to-back ISRs and sporadic interrupts from our NIC.
238 * If we have something to service, the tasklet will re-enable ints.
239 * If we *don't* have something, we'll re-enable before leaving here.
240 */
241 inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */
242 iwl_write32(priv, CSR_INT_MASK, 0x00000000);
243
244
245 /* Ignore interrupt if there's nothing in NIC to service.
246 * This may be due to IRQ shared with another device,
247 * or due to sporadic interrupts thrown from our NIC. */
248 if (!priv->_agn.ict_tbl[priv->_agn.ict_index]) {
249 IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n");
250 goto none;
251 }
252
253 /* read all entries that not 0 start with ict_index */
254 while (priv->_agn.ict_tbl[priv->_agn.ict_index]) {
255
256 val |= le32_to_cpu(priv->_agn.ict_tbl[priv->_agn.ict_index]);
257 IWL_DEBUG_ISR(priv, "ICT index %d value 0x%08X\n",
258 priv->_agn.ict_index,
259 le32_to_cpu(priv->_agn.ict_tbl[priv->_agn.ict_index]));
260 priv->_agn.ict_tbl[priv->_agn.ict_index] = 0;
261 priv->_agn.ict_index = iwl_queue_inc_wrap(priv->_agn.ict_index,
262 ICT_COUNT);
263
264 }
265
266 /* We should not get this value, just ignore it. */
267 if (val == 0xffffffff)
268 val = 0;
269
270 /*
271 * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit
272 * (bit 15 before shifting it to 31) to clear when using interrupt
273 * coalescing. fortunately, bits 18 and 19 stay set when this happens
274 * so we use them to decide on the real state of the Rx bit.
275 * In order words, bit 15 is set if bit 18 or bit 19 are set.
276 */
277 if (val & 0xC0000)
278 val |= 0x8000;
279
280 inta = (0xff & val) | ((0xff00 & val) << 16);
281 IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n",
282 inta, inta_mask, val);
283
284 inta &= priv->inta_mask;
285 priv->_agn.inta |= inta;
286
287 /* iwl_irq_tasklet() will service interrupts and re-enable them */
288 if (likely(inta))
289 tasklet_schedule(&priv->irq_tasklet);
290 else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta) {
291 /* Allow interrupt if was disabled by this handler and
292 * no tasklet was schedules, We should not enable interrupt,
293 * tasklet will enable it.
294 */
295 iwl_enable_interrupts(priv);
296 }
297
298 spin_unlock_irqrestore(&priv->lock, flags);
299 return IRQ_HANDLED;
300
301 none:
302 /* re-enable interrupts here since we don't have anything to service.
303 * only Re-enable if disabled by irq.
304 */
305 if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta)
306 iwl_enable_interrupts(priv);
307
308 spin_unlock_irqrestore(&priv->lock, flags);
309 return IRQ_NONE;
310}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 3de90c540287..d50c68a072ab 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -1643,6 +1643,7 @@ int iwl_alive_start(struct iwl_priv *priv)
1643 int ret = 0; 1643 int ret = 0;
1644 struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 1644 struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
1645 1645
1646 /*TODO: this should go to the transport layer */
1646 iwl_reset_ict(priv); 1647 iwl_reset_ict(priv);
1647 1648
1648 IWL_DEBUG_INFO(priv, "Runtime Alive received.\n"); 1649 IWL_DEBUG_INFO(priv, "Runtime Alive received.\n");
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h
index fa32eb99e1d6..c851fc2fb6b2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.h
@@ -113,10 +113,6 @@ extern struct iwl_mod_params iwlagn_mod_params;
113extern struct ieee80211_ops iwlagn_hw_ops; 113extern struct ieee80211_ops iwlagn_hw_ops;
114 114
115int iwl_reset_ict(struct iwl_priv *priv); 115int iwl_reset_ict(struct iwl_priv *priv);
116void iwl_disable_ict(struct iwl_priv *priv);
117int iwl_alloc_isr_ict(struct iwl_priv *priv);
118void iwl_free_isr_ict(struct iwl_priv *priv);
119irqreturn_t iwl_isr_ict(int irq, void *data);
120 116
121static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd) 117static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd)
122{ 118{
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h b/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h
index 8307064182e1..3015facd775e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h
@@ -42,6 +42,16 @@ void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv,
42 struct iwl_rx_queue *q); 42 struct iwl_rx_queue *q);
43 43
44/***************************************************** 44/*****************************************************
45* ICT
46******************************************************/
47int iwl_reset_ict(struct iwl_priv *priv);
48void iwl_disable_ict(struct iwl_priv *priv);
49int iwl_alloc_isr_ict(struct iwl_priv *priv);
50void iwl_free_isr_ict(struct iwl_priv *priv);
51irqreturn_t iwl_isr_ict(int irq, void *data);
52
53
54/*****************************************************
45* TX / HCMD 55* TX / HCMD
46******************************************************/ 56******************************************************/
47void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq); 57void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq);
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-rx-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-rx-pcie.c
index 046a33f5ca6d..d5b9adfc3cce 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-rx-pcie.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-rx-pcie.c
@@ -28,6 +28,7 @@
28 *****************************************************************************/ 28 *****************************************************************************/
29#include <linux/sched.h> 29#include <linux/sched.h>
30#include <linux/wait.h> 30#include <linux/wait.h>
31#include <linux/gfp.h>
31 32
32#include "iwl-dev.h" 33#include "iwl-dev.h"
33#include "iwl-agn.h" 34#include "iwl-agn.h"
@@ -692,3 +693,287 @@ void iwl_irq_tasklet(struct iwl_priv *priv)
692 iwl_enable_rfkill_int(priv); 693 iwl_enable_rfkill_int(priv);
693} 694}
694 695
696/******************************************************************************
697 *
698 * ICT functions
699 *
700 ******************************************************************************/
701#define ICT_COUNT (PAGE_SIZE/sizeof(u32))
702
703/* Free dram table */
704void iwl_free_isr_ict(struct iwl_priv *priv)
705{
706 if (priv->_agn.ict_tbl_vir) {
707 dma_free_coherent(priv->bus.dev,
708 (sizeof(u32) * ICT_COUNT) + PAGE_SIZE,
709 priv->_agn.ict_tbl_vir,
710 priv->_agn.ict_tbl_dma);
711 priv->_agn.ict_tbl_vir = NULL;
712 memset(&priv->_agn.ict_tbl_dma, 0,
713 sizeof(priv->_agn.ict_tbl_dma));
714 memset(&priv->_agn.aligned_ict_tbl_dma, 0,
715 sizeof(priv->_agn.aligned_ict_tbl_dma));
716 }
717}
718
719
720/* allocate dram shared table it is a PAGE_SIZE aligned
721 * also reset all data related to ICT table interrupt.
722 */
723int iwl_alloc_isr_ict(struct iwl_priv *priv)
724{
725
726 /* allocate shrared data table */
727 priv->_agn.ict_tbl_vir =
728 dma_alloc_coherent(priv->bus.dev,
729 (sizeof(u32) * ICT_COUNT) + PAGE_SIZE,
730 &priv->_agn.ict_tbl_dma, GFP_KERNEL);
731 if (!priv->_agn.ict_tbl_vir)
732 return -ENOMEM;
733
734 /* align table to PAGE_SIZE boundary */
735 priv->_agn.aligned_ict_tbl_dma =
736 ALIGN(priv->_agn.ict_tbl_dma, PAGE_SIZE);
737
738 IWL_DEBUG_ISR(priv, "ict dma addr %Lx dma aligned %Lx diff %d\n",
739 (unsigned long long)priv->_agn.ict_tbl_dma,
740 (unsigned long long)priv->_agn.aligned_ict_tbl_dma,
741 (int)(priv->_agn.aligned_ict_tbl_dma -
742 priv->_agn.ict_tbl_dma));
743
744 priv->_agn.ict_tbl = priv->_agn.ict_tbl_vir +
745 (priv->_agn.aligned_ict_tbl_dma -
746 priv->_agn.ict_tbl_dma);
747
748 IWL_DEBUG_ISR(priv, "ict vir addr %p vir aligned %p diff %d\n",
749 priv->_agn.ict_tbl, priv->_agn.ict_tbl_vir,
750 (int)(priv->_agn.aligned_ict_tbl_dma -
751 priv->_agn.ict_tbl_dma));
752
753 /* reset table and index to all 0 */
754 memset(priv->_agn.ict_tbl_vir, 0,
755 (sizeof(u32) * ICT_COUNT) + PAGE_SIZE);
756 priv->_agn.ict_index = 0;
757
758 /* add periodic RX interrupt */
759 priv->inta_mask |= CSR_INT_BIT_RX_PERIODIC;
760 return 0;
761}
762
763/* Device is going up inform it about using ICT interrupt table,
764 * also we need to tell the driver to start using ICT interrupt.
765 */
766int iwl_reset_ict(struct iwl_priv *priv)
767{
768 u32 val;
769 unsigned long flags;
770
771 if (!priv->_agn.ict_tbl_vir)
772 return 0;
773
774 spin_lock_irqsave(&priv->lock, flags);
775 iwl_disable_interrupts(priv);
776
777 memset(&priv->_agn.ict_tbl[0], 0, sizeof(u32) * ICT_COUNT);
778
779 val = priv->_agn.aligned_ict_tbl_dma >> PAGE_SHIFT;
780
781 val |= CSR_DRAM_INT_TBL_ENABLE;
782 val |= CSR_DRAM_INIT_TBL_WRAP_CHECK;
783
784 IWL_DEBUG_ISR(priv, "CSR_DRAM_INT_TBL_REG =0x%X "
785 "aligned dma address %Lx\n",
786 val,
787 (unsigned long long)priv->_agn.aligned_ict_tbl_dma);
788
789 iwl_write32(priv, CSR_DRAM_INT_TBL_REG, val);
790 priv->_agn.use_ict = true;
791 priv->_agn.ict_index = 0;
792 iwl_write32(priv, CSR_INT, priv->inta_mask);
793 iwl_enable_interrupts(priv);
794 spin_unlock_irqrestore(&priv->lock, flags);
795
796 return 0;
797}
798
799/* Device is going down disable ict interrupt usage */
800void iwl_disable_ict(struct iwl_priv *priv)
801{
802 unsigned long flags;
803
804 spin_lock_irqsave(&priv->lock, flags);
805 priv->_agn.use_ict = false;
806 spin_unlock_irqrestore(&priv->lock, flags);
807}
808
809static irqreturn_t iwl_isr(int irq, void *data)
810{
811 struct iwl_priv *priv = data;
812 u32 inta, inta_mask;
813 unsigned long flags;
814#ifdef CONFIG_IWLWIFI_DEBUG
815 u32 inta_fh;
816#endif
817 if (!priv)
818 return IRQ_NONE;
819
820 spin_lock_irqsave(&priv->lock, flags);
821
822 /* Disable (but don't clear!) interrupts here to avoid
823 * back-to-back ISRs and sporadic interrupts from our NIC.
824 * If we have something to service, the tasklet will re-enable ints.
825 * If we *don't* have something, we'll re-enable before leaving here. */
826 inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */
827 iwl_write32(priv, CSR_INT_MASK, 0x00000000);
828
829 /* Discover which interrupts are active/pending */
830 inta = iwl_read32(priv, CSR_INT);
831
832 /* Ignore interrupt if there's nothing in NIC to service.
833 * This may be due to IRQ shared with another device,
834 * or due to sporadic interrupts thrown from our NIC. */
835 if (!inta) {
836 IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n");
837 goto none;
838 }
839
840 if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
841 /* Hardware disappeared. It might have already raised
842 * an interrupt */
843 IWL_WARN(priv, "HARDWARE GONE?? INTA == 0x%08x\n", inta);
844 goto unplugged;
845 }
846
847#ifdef CONFIG_IWLWIFI_DEBUG
848 if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) {
849 inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS);
850 IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, "
851 "fh 0x%08x\n", inta, inta_mask, inta_fh);
852 }
853#endif
854
855 priv->_agn.inta |= inta;
856 /* iwl_irq_tasklet() will service interrupts and re-enable them */
857 if (likely(inta))
858 tasklet_schedule(&priv->irq_tasklet);
859 else if (test_bit(STATUS_INT_ENABLED, &priv->status) &&
860 !priv->_agn.inta)
861 iwl_enable_interrupts(priv);
862
863 unplugged:
864 spin_unlock_irqrestore(&priv->lock, flags);
865 return IRQ_HANDLED;
866
867 none:
868 /* re-enable interrupts here since we don't have anything to service. */
869 /* only Re-enable if disabled by irq and no schedules tasklet. */
870 if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta)
871 iwl_enable_interrupts(priv);
872
873 spin_unlock_irqrestore(&priv->lock, flags);
874 return IRQ_NONE;
875}
876
877/* interrupt handler using ict table, with this interrupt driver will
878 * stop using INTA register to get device's interrupt, reading this register
879 * is expensive, device will write interrupts in ICT dram table, increment
880 * index then will fire interrupt to driver, driver will OR all ICT table
881 * entries from current index up to table entry with 0 value. the result is
882 * the interrupt we need to service, driver will set the entries back to 0 and
883 * set index.
884 */
885irqreturn_t iwl_isr_ict(int irq, void *data)
886{
887 struct iwl_priv *priv = data;
888 u32 inta, inta_mask;
889 u32 val = 0;
890 unsigned long flags;
891
892 if (!priv)
893 return IRQ_NONE;
894
895 /* dram interrupt table not set yet,
896 * use legacy interrupt.
897 */
898 if (!priv->_agn.use_ict)
899 return iwl_isr(irq, data);
900
901 spin_lock_irqsave(&priv->lock, flags);
902
903 /* Disable (but don't clear!) interrupts here to avoid
904 * back-to-back ISRs and sporadic interrupts from our NIC.
905 * If we have something to service, the tasklet will re-enable ints.
906 * If we *don't* have something, we'll re-enable before leaving here.
907 */
908 inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */
909 iwl_write32(priv, CSR_INT_MASK, 0x00000000);
910
911
912 /* Ignore interrupt if there's nothing in NIC to service.
913 * This may be due to IRQ shared with another device,
914 * or due to sporadic interrupts thrown from our NIC. */
915 if (!priv->_agn.ict_tbl[priv->_agn.ict_index]) {
916 IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n");
917 goto none;
918 }
919
920 /* read all entries that not 0 start with ict_index */
921 while (priv->_agn.ict_tbl[priv->_agn.ict_index]) {
922
923 val |= le32_to_cpu(priv->_agn.ict_tbl[priv->_agn.ict_index]);
924 IWL_DEBUG_ISR(priv, "ICT index %d value 0x%08X\n",
925 priv->_agn.ict_index,
926 le32_to_cpu(
927 priv->_agn.ict_tbl[priv->_agn.ict_index]));
928 priv->_agn.ict_tbl[priv->_agn.ict_index] = 0;
929 priv->_agn.ict_index = iwl_queue_inc_wrap(priv->_agn.ict_index,
930 ICT_COUNT);
931
932 }
933
934 /* We should not get this value, just ignore it. */
935 if (val == 0xffffffff)
936 val = 0;
937
938 /*
939 * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit
940 * (bit 15 before shifting it to 31) to clear when using interrupt
941 * coalescing. fortunately, bits 18 and 19 stay set when this happens
942 * so we use them to decide on the real state of the Rx bit.
943 * In order words, bit 15 is set if bit 18 or bit 19 are set.
944 */
945 if (val & 0xC0000)
946 val |= 0x8000;
947
948 inta = (0xff & val) | ((0xff00 & val) << 16);
949 IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n",
950 inta, inta_mask, val);
951
952 inta &= priv->inta_mask;
953 priv->_agn.inta |= inta;
954
955 /* iwl_irq_tasklet() will service interrupts and re-enable them */
956 if (likely(inta))
957 tasklet_schedule(&priv->irq_tasklet);
958 else if (test_bit(STATUS_INT_ENABLED, &priv->status) &&
959 !priv->_agn.inta) {
960 /* Allow interrupt if was disabled by this handler and
961 * no tasklet was schedules, We should not enable interrupt,
962 * tasklet will enable it.
963 */
964 iwl_enable_interrupts(priv);
965 }
966
967 spin_unlock_irqrestore(&priv->lock, flags);
968 return IRQ_HANDLED;
969
970 none:
971 /* re-enable interrupts here since we don't have anything to service.
972 * only Re-enable if disabled by irq.
973 */
974 if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta)
975 iwl_enable_interrupts(priv);
976
977 spin_unlock_irqrestore(&priv->lock, flags);
978 return IRQ_NONE;
979}