diff options
author | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2011-07-11 10:44:57 -0400 |
---|---|---|
committer | Wey-Yi Guy <wey-yi.w.guy@intel.com> | 2011-07-16 10:39:34 -0400 |
commit | 1a361cd838173879672cb0f0ebe1e7654d7edff6 (patch) | |
tree | cc63a7fa0f598cf7117a219b2fc96a0d43b64b95 /drivers/net | |
parent | ab6cf8e816bad473a1496f2006bea3a3849c2519 (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/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-ict.c | 310 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h | 10 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-rx-pcie.c | 285 |
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 | |||
8 | iwlagn-objs += iwl-core.o iwl-eeprom.o iwl-power.o | 8 | iwlagn-objs += iwl-core.o iwl-eeprom.o iwl-power.o |
9 | iwlagn-objs += iwl-rx.o iwl-sta.o | 9 | iwlagn-objs += iwl-rx.o iwl-sta.o |
10 | iwlagn-objs += iwl-scan.o iwl-led.o | 10 | iwlagn-objs += iwl-scan.o iwl-led.o |
11 | iwlagn-objs += iwl-agn-rxon.o iwl-agn-ict.o | 11 | iwlagn-objs += iwl-agn-rxon.o |
12 | iwlagn-objs += iwl-5000.o | 12 | iwlagn-objs += iwl-5000.o |
13 | iwlagn-objs += iwl-6000.o | 13 | iwlagn-objs += iwl-6000.o |
14 | iwlagn-objs += iwl-1000.o | 14 | iwlagn-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 */ | ||
44 | void 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 | */ | ||
63 | int 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 | */ | ||
101 | int 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 */ | ||
134 | void 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 | |||
143 | static 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 | */ | ||
218 | irqreturn_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; | |||
113 | extern struct ieee80211_ops iwlagn_hw_ops; | 113 | extern struct ieee80211_ops iwlagn_hw_ops; |
114 | 114 | ||
115 | int iwl_reset_ict(struct iwl_priv *priv); | 115 | int iwl_reset_ict(struct iwl_priv *priv); |
116 | void iwl_disable_ict(struct iwl_priv *priv); | ||
117 | int iwl_alloc_isr_ict(struct iwl_priv *priv); | ||
118 | void iwl_free_isr_ict(struct iwl_priv *priv); | ||
119 | irqreturn_t iwl_isr_ict(int irq, void *data); | ||
120 | 116 | ||
121 | static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd) | 117 | static 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 | ******************************************************/ | ||
47 | int iwl_reset_ict(struct iwl_priv *priv); | ||
48 | void iwl_disable_ict(struct iwl_priv *priv); | ||
49 | int iwl_alloc_isr_ict(struct iwl_priv *priv); | ||
50 | void iwl_free_isr_ict(struct iwl_priv *priv); | ||
51 | irqreturn_t iwl_isr_ict(int irq, void *data); | ||
52 | |||
53 | |||
54 | /***************************************************** | ||
45 | * TX / HCMD | 55 | * TX / HCMD |
46 | ******************************************************/ | 56 | ******************************************************/ |
47 | void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq); | 57 | void 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 */ | ||
704 | void 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 | */ | ||
723 | int 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 | */ | ||
766 | int 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 */ | ||
800 | void 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 | |||
809 | static 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 | */ | ||
885 | irqreturn_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 | } | ||