diff options
-rw-r--r-- | drivers/net/wireless/iwlwifi/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-1000.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-5000.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-6000.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-ict.c | 303 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.h | 74 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.c | 273 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-helpers.h | 3 |
10 files changed, 385 insertions, 279 deletions
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 4e378faee650..e31a5ccebea2 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile | |||
@@ -9,7 +9,7 @@ CFLAGS_iwl-devtrace.o := -I$(src) | |||
9 | 9 | ||
10 | # AGN | 10 | # AGN |
11 | obj-$(CONFIG_IWLAGN) += iwlagn.o | 11 | obj-$(CONFIG_IWLAGN) += iwlagn.o |
12 | iwlagn-objs := iwl-agn.o iwl-agn-rs.o iwl-agn-led.o | 12 | iwlagn-objs := iwl-agn.o iwl-agn-rs.o iwl-agn-led.o iwl-agn-ict.o |
13 | 13 | ||
14 | iwlagn-$(CONFIG_IWL4965) += iwl-4965.o | 14 | iwlagn-$(CONFIG_IWL4965) += iwl-4965.o |
15 | iwlagn-$(CONFIG_IWL5000) += iwl-5000.o | 15 | iwlagn-$(CONFIG_IWL5000) += iwl-5000.o |
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index 3bf2e6e9b2d9..59b092eaa829 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c | |||
@@ -42,6 +42,7 @@ | |||
42 | #include "iwl-core.h" | 42 | #include "iwl-core.h" |
43 | #include "iwl-io.h" | 43 | #include "iwl-io.h" |
44 | #include "iwl-sta.h" | 44 | #include "iwl-sta.h" |
45 | #include "iwl-agn.h" | ||
45 | #include "iwl-helpers.h" | 46 | #include "iwl-helpers.h" |
46 | #include "iwl-5000-hw.h" | 47 | #include "iwl-5000-hw.h" |
47 | #include "iwl-agn-led.h" | 48 | #include "iwl-agn-led.h" |
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index e476acb53aa7..f59628c1c1ce 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c | |||
@@ -43,6 +43,7 @@ | |||
43 | #include "iwl-io.h" | 43 | #include "iwl-io.h" |
44 | #include "iwl-sta.h" | 44 | #include "iwl-sta.h" |
45 | #include "iwl-helpers.h" | 45 | #include "iwl-helpers.h" |
46 | #include "iwl-agn.h" | ||
46 | #include "iwl-agn-led.h" | 47 | #include "iwl-agn-led.h" |
47 | #include "iwl-5000-hw.h" | 48 | #include "iwl-5000-hw.h" |
48 | #include "iwl-6000-hw.h" | 49 | #include "iwl-6000-hw.h" |
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index c4844adff92a..4b7bc008220f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c | |||
@@ -42,6 +42,7 @@ | |||
42 | #include "iwl-core.h" | 42 | #include "iwl-core.h" |
43 | #include "iwl-io.h" | 43 | #include "iwl-io.h" |
44 | #include "iwl-sta.h" | 44 | #include "iwl-sta.h" |
45 | #include "iwl-agn.h" | ||
45 | #include "iwl-helpers.h" | 46 | #include "iwl-helpers.h" |
46 | #include "iwl-5000-hw.h" | 47 | #include "iwl-5000-hw.h" |
47 | #include "iwl-6000-hw.h" | 48 | #include "iwl-6000-hw.h" |
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-ict.c b/drivers/net/wireless/iwlwifi/iwl-agn-ict.c new file mode 100644 index 000000000000..d6b028b1b6ef --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-agn-ict.c | |||
@@ -0,0 +1,303 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * GPL LICENSE SUMMARY | ||
4 | * | ||
5 | * Copyright(c) 2008 - 2010 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 <net/mac80211.h> | ||
34 | |||
35 | #include "iwl-dev.h" | ||
36 | #include "iwl-core.h" | ||
37 | #include "iwl-agn.h" | ||
38 | #include "iwl-helpers.h" | ||
39 | |||
40 | #define ICT_COUNT (PAGE_SIZE/sizeof(u32)) | ||
41 | |||
42 | /* Free dram table */ | ||
43 | void iwl_free_isr_ict(struct iwl_priv *priv) | ||
44 | { | ||
45 | if (priv->ict_tbl_vir) { | ||
46 | dma_free_coherent(&priv->pci_dev->dev, | ||
47 | (sizeof(u32) * ICT_COUNT) + PAGE_SIZE, | ||
48 | priv->ict_tbl_vir, priv->ict_tbl_dma); | ||
49 | priv->ict_tbl_vir = NULL; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | |||
54 | /* allocate dram shared table it is a PAGE_SIZE aligned | ||
55 | * also reset all data related to ICT table interrupt. | ||
56 | */ | ||
57 | int iwl_alloc_isr_ict(struct iwl_priv *priv) | ||
58 | { | ||
59 | |||
60 | if (priv->cfg->use_isr_legacy) | ||
61 | return 0; | ||
62 | /* allocate shrared data table */ | ||
63 | priv->ict_tbl_vir = dma_alloc_coherent(&priv->pci_dev->dev, | ||
64 | (sizeof(u32) * ICT_COUNT) + PAGE_SIZE, | ||
65 | &priv->ict_tbl_dma, GFP_KERNEL); | ||
66 | if (!priv->ict_tbl_vir) | ||
67 | return -ENOMEM; | ||
68 | |||
69 | /* align table to PAGE_SIZE boundry */ | ||
70 | priv->aligned_ict_tbl_dma = ALIGN(priv->ict_tbl_dma, PAGE_SIZE); | ||
71 | |||
72 | IWL_DEBUG_ISR(priv, "ict dma addr %Lx dma aligned %Lx diff %d\n", | ||
73 | (unsigned long long)priv->ict_tbl_dma, | ||
74 | (unsigned long long)priv->aligned_ict_tbl_dma, | ||
75 | (int)(priv->aligned_ict_tbl_dma - priv->ict_tbl_dma)); | ||
76 | |||
77 | priv->ict_tbl = priv->ict_tbl_vir + | ||
78 | (priv->aligned_ict_tbl_dma - priv->ict_tbl_dma); | ||
79 | |||
80 | IWL_DEBUG_ISR(priv, "ict vir addr %p vir aligned %p diff %d\n", | ||
81 | priv->ict_tbl, priv->ict_tbl_vir, | ||
82 | (int)(priv->aligned_ict_tbl_dma - priv->ict_tbl_dma)); | ||
83 | |||
84 | /* reset table and index to all 0 */ | ||
85 | memset(priv->ict_tbl_vir,0, (sizeof(u32) * ICT_COUNT) + PAGE_SIZE); | ||
86 | priv->ict_index = 0; | ||
87 | |||
88 | /* add periodic RX interrupt */ | ||
89 | priv->inta_mask |= CSR_INT_BIT_RX_PERIODIC; | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | /* Device is going up inform it about using ICT interrupt table, | ||
94 | * also we need to tell the driver to start using ICT interrupt. | ||
95 | */ | ||
96 | int iwl_reset_ict(struct iwl_priv *priv) | ||
97 | { | ||
98 | u32 val; | ||
99 | unsigned long flags; | ||
100 | |||
101 | if (!priv->ict_tbl_vir) | ||
102 | return 0; | ||
103 | |||
104 | spin_lock_irqsave(&priv->lock, flags); | ||
105 | iwl_disable_interrupts(priv); | ||
106 | |||
107 | memset(&priv->ict_tbl[0], 0, sizeof(u32) * ICT_COUNT); | ||
108 | |||
109 | val = priv->aligned_ict_tbl_dma >> PAGE_SHIFT; | ||
110 | |||
111 | val |= CSR_DRAM_INT_TBL_ENABLE; | ||
112 | val |= CSR_DRAM_INIT_TBL_WRAP_CHECK; | ||
113 | |||
114 | IWL_DEBUG_ISR(priv, "CSR_DRAM_INT_TBL_REG =0x%X " | ||
115 | "aligned dma address %Lx\n", | ||
116 | val, (unsigned long long)priv->aligned_ict_tbl_dma); | ||
117 | |||
118 | iwl_write32(priv, CSR_DRAM_INT_TBL_REG, val); | ||
119 | priv->use_ict = true; | ||
120 | priv->ict_index = 0; | ||
121 | iwl_write32(priv, CSR_INT, priv->inta_mask); | ||
122 | iwl_enable_interrupts(priv); | ||
123 | spin_unlock_irqrestore(&priv->lock, flags); | ||
124 | |||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | /* Device is going down disable ict interrupt usage */ | ||
129 | void iwl_disable_ict(struct iwl_priv *priv) | ||
130 | { | ||
131 | unsigned long flags; | ||
132 | |||
133 | spin_lock_irqsave(&priv->lock, flags); | ||
134 | priv->use_ict = false; | ||
135 | spin_unlock_irqrestore(&priv->lock, flags); | ||
136 | } | ||
137 | |||
138 | static irqreturn_t iwl_isr(int irq, void *data) | ||
139 | { | ||
140 | struct iwl_priv *priv = data; | ||
141 | u32 inta, inta_mask; | ||
142 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
143 | u32 inta_fh; | ||
144 | #endif | ||
145 | if (!priv) | ||
146 | return IRQ_NONE; | ||
147 | |||
148 | spin_lock(&priv->lock); | ||
149 | |||
150 | /* Disable (but don't clear!) interrupts here to avoid | ||
151 | * back-to-back ISRs and sporadic interrupts from our NIC. | ||
152 | * If we have something to service, the tasklet will re-enable ints. | ||
153 | * If we *don't* have something, we'll re-enable before leaving here. */ | ||
154 | inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */ | ||
155 | iwl_write32(priv, CSR_INT_MASK, 0x00000000); | ||
156 | |||
157 | /* Discover which interrupts are active/pending */ | ||
158 | inta = iwl_read32(priv, CSR_INT); | ||
159 | |||
160 | /* Ignore interrupt if there's nothing in NIC to service. | ||
161 | * This may be due to IRQ shared with another device, | ||
162 | * or due to sporadic interrupts thrown from our NIC. */ | ||
163 | if (!inta) { | ||
164 | IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n"); | ||
165 | goto none; | ||
166 | } | ||
167 | |||
168 | if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { | ||
169 | /* Hardware disappeared. It might have already raised | ||
170 | * an interrupt */ | ||
171 | IWL_WARN(priv, "HARDWARE GONE?? INTA == 0x%08x\n", inta); | ||
172 | goto unplugged; | ||
173 | } | ||
174 | |||
175 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
176 | if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { | ||
177 | inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); | ||
178 | IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, " | ||
179 | "fh 0x%08x\n", inta, inta_mask, inta_fh); | ||
180 | } | ||
181 | #endif | ||
182 | |||
183 | priv->inta |= inta; | ||
184 | /* iwl_irq_tasklet() will service interrupts and re-enable them */ | ||
185 | if (likely(inta)) | ||
186 | tasklet_schedule(&priv->irq_tasklet); | ||
187 | else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta) | ||
188 | iwl_enable_interrupts(priv); | ||
189 | |||
190 | unplugged: | ||
191 | spin_unlock(&priv->lock); | ||
192 | return IRQ_HANDLED; | ||
193 | |||
194 | none: | ||
195 | /* re-enable interrupts here since we don't have anything to service. */ | ||
196 | /* only Re-enable if diabled by irq and no schedules tasklet. */ | ||
197 | if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta) | ||
198 | iwl_enable_interrupts(priv); | ||
199 | |||
200 | spin_unlock(&priv->lock); | ||
201 | return IRQ_NONE; | ||
202 | } | ||
203 | |||
204 | /* interrupt handler using ict table, with this interrupt driver will | ||
205 | * stop using INTA register to get device's interrupt, reading this register | ||
206 | * is expensive, device will write interrupts in ICT dram table, increment | ||
207 | * index then will fire interrupt to driver, driver will OR all ICT table | ||
208 | * entries from current index up to table entry with 0 value. the result is | ||
209 | * the interrupt we need to service, driver will set the entries back to 0 and | ||
210 | * set index. | ||
211 | */ | ||
212 | irqreturn_t iwl_isr_ict(int irq, void *data) | ||
213 | { | ||
214 | struct iwl_priv *priv = data; | ||
215 | u32 inta, inta_mask; | ||
216 | u32 val = 0; | ||
217 | |||
218 | if (!priv) | ||
219 | return IRQ_NONE; | ||
220 | |||
221 | /* dram interrupt table not set yet, | ||
222 | * use legacy interrupt. | ||
223 | */ | ||
224 | if (!priv->use_ict) | ||
225 | return iwl_isr(irq, data); | ||
226 | |||
227 | spin_lock(&priv->lock); | ||
228 | |||
229 | /* Disable (but don't clear!) interrupts here to avoid | ||
230 | * back-to-back ISRs and sporadic interrupts from our NIC. | ||
231 | * If we have something to service, the tasklet will re-enable ints. | ||
232 | * If we *don't* have something, we'll re-enable before leaving here. | ||
233 | */ | ||
234 | inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */ | ||
235 | iwl_write32(priv, CSR_INT_MASK, 0x00000000); | ||
236 | |||
237 | |||
238 | /* Ignore interrupt if there's nothing in NIC to service. | ||
239 | * This may be due to IRQ shared with another device, | ||
240 | * or due to sporadic interrupts thrown from our NIC. */ | ||
241 | if (!priv->ict_tbl[priv->ict_index]) { | ||
242 | IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n"); | ||
243 | goto none; | ||
244 | } | ||
245 | |||
246 | /* read all entries that not 0 start with ict_index */ | ||
247 | while (priv->ict_tbl[priv->ict_index]) { | ||
248 | |||
249 | val |= le32_to_cpu(priv->ict_tbl[priv->ict_index]); | ||
250 | IWL_DEBUG_ISR(priv, "ICT index %d value 0x%08X\n", | ||
251 | priv->ict_index, | ||
252 | le32_to_cpu(priv->ict_tbl[priv->ict_index])); | ||
253 | priv->ict_tbl[priv->ict_index] = 0; | ||
254 | priv->ict_index = iwl_queue_inc_wrap(priv->ict_index, | ||
255 | ICT_COUNT); | ||
256 | |||
257 | } | ||
258 | |||
259 | /* We should not get this value, just ignore it. */ | ||
260 | if (val == 0xffffffff) | ||
261 | val = 0; | ||
262 | |||
263 | /* | ||
264 | * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit | ||
265 | * (bit 15 before shifting it to 31) to clear when using interrupt | ||
266 | * coalescing. fortunately, bits 18 and 19 stay set when this happens | ||
267 | * so we use them to decide on the real state of the Rx bit. | ||
268 | * In order words, bit 15 is set if bit 18 or bit 19 are set. | ||
269 | */ | ||
270 | if (val & 0xC0000) | ||
271 | val |= 0x8000; | ||
272 | |||
273 | inta = (0xff & val) | ((0xff00 & val) << 16); | ||
274 | IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n", | ||
275 | inta, inta_mask, val); | ||
276 | |||
277 | inta &= priv->inta_mask; | ||
278 | priv->inta |= inta; | ||
279 | |||
280 | /* iwl_irq_tasklet() will service interrupts and re-enable them */ | ||
281 | if (likely(inta)) | ||
282 | tasklet_schedule(&priv->irq_tasklet); | ||
283 | else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta) { | ||
284 | /* Allow interrupt if was disabled by this handler and | ||
285 | * no tasklet was schedules, We should not enable interrupt, | ||
286 | * tasklet will enable it. | ||
287 | */ | ||
288 | iwl_enable_interrupts(priv); | ||
289 | } | ||
290 | |||
291 | spin_unlock(&priv->lock); | ||
292 | return IRQ_HANDLED; | ||
293 | |||
294 | none: | ||
295 | /* re-enable interrupts here since we don't have anything to service. | ||
296 | * only Re-enable if disabled by irq. | ||
297 | */ | ||
298 | if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta) | ||
299 | iwl_enable_interrupts(priv); | ||
300 | |||
301 | spin_unlock(&priv->lock); | ||
302 | return IRQ_NONE; | ||
303 | } | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 47b021477967..d7b8e44985b3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c | |||
@@ -54,6 +54,7 @@ | |||
54 | #include "iwl-helpers.h" | 54 | #include "iwl-helpers.h" |
55 | #include "iwl-sta.h" | 55 | #include "iwl-sta.h" |
56 | #include "iwl-calib.h" | 56 | #include "iwl-calib.h" |
57 | #include "iwl-agn.h" | ||
57 | 58 | ||
58 | 59 | ||
59 | /****************************************************************************** | 60 | /****************************************************************************** |
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h new file mode 100644 index 000000000000..26eeb586ee00 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-agn.h | |||
@@ -0,0 +1,74 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * This file is provided under a dual BSD/GPLv2 license. When using or | ||
4 | * redistributing this file, you may do so under either license. | ||
5 | * | ||
6 | * GPL LICENSE SUMMARY | ||
7 | * | ||
8 | * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of version 2 of the GNU General Public License as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, | ||
22 | * USA | ||
23 | * | ||
24 | * The full GNU General Public License is included in this distribution | ||
25 | * in the file called LICENSE.GPL. | ||
26 | * | ||
27 | * Contact Information: | ||
28 | * Intel Linux Wireless <ilw@linux.intel.com> | ||
29 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | ||
30 | * | ||
31 | * BSD LICENSE | ||
32 | * | ||
33 | * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. | ||
34 | * All rights reserved. | ||
35 | * | ||
36 | * Redistribution and use in source and binary forms, with or without | ||
37 | * modification, are permitted provided that the following conditions | ||
38 | * are met: | ||
39 | * | ||
40 | * * Redistributions of source code must retain the above copyright | ||
41 | * notice, this list of conditions and the following disclaimer. | ||
42 | * * Redistributions in binary form must reproduce the above copyright | ||
43 | * notice, this list of conditions and the following disclaimer in | ||
44 | * the documentation and/or other materials provided with the | ||
45 | * distribution. | ||
46 | * * Neither the name Intel Corporation nor the names of its | ||
47 | * contributors may be used to endorse or promote products derived | ||
48 | * from this software without specific prior written permission. | ||
49 | * | ||
50 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
51 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
52 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
53 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
54 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
55 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
56 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
57 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
58 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
59 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
60 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
61 | *****************************************************************************/ | ||
62 | |||
63 | #ifndef __iwl_agn_h__ | ||
64 | #define __iwl_agn_h__ | ||
65 | |||
66 | #include "iwl-dev.h" | ||
67 | |||
68 | int iwl_reset_ict(struct iwl_priv *priv); | ||
69 | void iwl_disable_ict(struct iwl_priv *priv); | ||
70 | int iwl_alloc_isr_ict(struct iwl_priv *priv); | ||
71 | void iwl_free_isr_ict(struct iwl_priv *priv); | ||
72 | irqreturn_t iwl_isr_ict(int irq, void *data); | ||
73 | |||
74 | #endif /* __iwl_agn_h__ */ | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 112149e9b31e..b994eee771a0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c | |||
@@ -114,8 +114,6 @@ static struct iwl_wimax_coex_event_entry cu_priorities[COEX_NUM_OF_EVENTS] = { | |||
114 | u32 iwl_debug_level; | 114 | u32 iwl_debug_level; |
115 | EXPORT_SYMBOL(iwl_debug_level); | 115 | EXPORT_SYMBOL(iwl_debug_level); |
116 | 116 | ||
117 | static irqreturn_t iwl_isr(int irq, void *data); | ||
118 | |||
119 | /* | 117 | /* |
120 | * Parameter order: | 118 | * Parameter order: |
121 | * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate | 119 | * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate |
@@ -1664,277 +1662,6 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) | |||
1664 | } | 1662 | } |
1665 | EXPORT_SYMBOL(iwl_set_tx_power); | 1663 | EXPORT_SYMBOL(iwl_set_tx_power); |
1666 | 1664 | ||
1667 | #define ICT_COUNT (PAGE_SIZE/sizeof(u32)) | ||
1668 | |||
1669 | /* Free dram table */ | ||
1670 | void iwl_free_isr_ict(struct iwl_priv *priv) | ||
1671 | { | ||
1672 | if (priv->ict_tbl_vir) { | ||
1673 | dma_free_coherent(&priv->pci_dev->dev, | ||
1674 | (sizeof(u32) * ICT_COUNT) + PAGE_SIZE, | ||
1675 | priv->ict_tbl_vir, priv->ict_tbl_dma); | ||
1676 | priv->ict_tbl_vir = NULL; | ||
1677 | } | ||
1678 | } | ||
1679 | EXPORT_SYMBOL(iwl_free_isr_ict); | ||
1680 | |||
1681 | |||
1682 | /* allocate dram shared table it is a PAGE_SIZE aligned | ||
1683 | * also reset all data related to ICT table interrupt. | ||
1684 | */ | ||
1685 | int iwl_alloc_isr_ict(struct iwl_priv *priv) | ||
1686 | { | ||
1687 | |||
1688 | if (priv->cfg->use_isr_legacy) | ||
1689 | return 0; | ||
1690 | /* allocate shrared data table */ | ||
1691 | priv->ict_tbl_vir = dma_alloc_coherent(&priv->pci_dev->dev, | ||
1692 | (sizeof(u32) * ICT_COUNT) + PAGE_SIZE, | ||
1693 | &priv->ict_tbl_dma, GFP_KERNEL); | ||
1694 | if (!priv->ict_tbl_vir) | ||
1695 | return -ENOMEM; | ||
1696 | |||
1697 | /* align table to PAGE_SIZE boundry */ | ||
1698 | priv->aligned_ict_tbl_dma = ALIGN(priv->ict_tbl_dma, PAGE_SIZE); | ||
1699 | |||
1700 | IWL_DEBUG_ISR(priv, "ict dma addr %Lx dma aligned %Lx diff %d\n", | ||
1701 | (unsigned long long)priv->ict_tbl_dma, | ||
1702 | (unsigned long long)priv->aligned_ict_tbl_dma, | ||
1703 | (int)(priv->aligned_ict_tbl_dma - priv->ict_tbl_dma)); | ||
1704 | |||
1705 | priv->ict_tbl = priv->ict_tbl_vir + | ||
1706 | (priv->aligned_ict_tbl_dma - priv->ict_tbl_dma); | ||
1707 | |||
1708 | IWL_DEBUG_ISR(priv, "ict vir addr %p vir aligned %p diff %d\n", | ||
1709 | priv->ict_tbl, priv->ict_tbl_vir, | ||
1710 | (int)(priv->aligned_ict_tbl_dma - priv->ict_tbl_dma)); | ||
1711 | |||
1712 | /* reset table and index to all 0 */ | ||
1713 | memset(priv->ict_tbl_vir,0, (sizeof(u32) * ICT_COUNT) + PAGE_SIZE); | ||
1714 | priv->ict_index = 0; | ||
1715 | |||
1716 | /* add periodic RX interrupt */ | ||
1717 | priv->inta_mask |= CSR_INT_BIT_RX_PERIODIC; | ||
1718 | return 0; | ||
1719 | } | ||
1720 | EXPORT_SYMBOL(iwl_alloc_isr_ict); | ||
1721 | |||
1722 | /* Device is going up inform it about using ICT interrupt table, | ||
1723 | * also we need to tell the driver to start using ICT interrupt. | ||
1724 | */ | ||
1725 | int iwl_reset_ict(struct iwl_priv *priv) | ||
1726 | { | ||
1727 | u32 val; | ||
1728 | unsigned long flags; | ||
1729 | |||
1730 | if (!priv->ict_tbl_vir) | ||
1731 | return 0; | ||
1732 | |||
1733 | spin_lock_irqsave(&priv->lock, flags); | ||
1734 | iwl_disable_interrupts(priv); | ||
1735 | |||
1736 | memset(&priv->ict_tbl[0], 0, sizeof(u32) * ICT_COUNT); | ||
1737 | |||
1738 | val = priv->aligned_ict_tbl_dma >> PAGE_SHIFT; | ||
1739 | |||
1740 | val |= CSR_DRAM_INT_TBL_ENABLE; | ||
1741 | val |= CSR_DRAM_INIT_TBL_WRAP_CHECK; | ||
1742 | |||
1743 | IWL_DEBUG_ISR(priv, "CSR_DRAM_INT_TBL_REG =0x%X " | ||
1744 | "aligned dma address %Lx\n", | ||
1745 | val, (unsigned long long)priv->aligned_ict_tbl_dma); | ||
1746 | |||
1747 | iwl_write32(priv, CSR_DRAM_INT_TBL_REG, val); | ||
1748 | priv->use_ict = true; | ||
1749 | priv->ict_index = 0; | ||
1750 | iwl_write32(priv, CSR_INT, priv->inta_mask); | ||
1751 | iwl_enable_interrupts(priv); | ||
1752 | spin_unlock_irqrestore(&priv->lock, flags); | ||
1753 | |||
1754 | return 0; | ||
1755 | } | ||
1756 | EXPORT_SYMBOL(iwl_reset_ict); | ||
1757 | |||
1758 | /* Device is going down disable ict interrupt usage */ | ||
1759 | void iwl_disable_ict(struct iwl_priv *priv) | ||
1760 | { | ||
1761 | unsigned long flags; | ||
1762 | |||
1763 | spin_lock_irqsave(&priv->lock, flags); | ||
1764 | priv->use_ict = false; | ||
1765 | spin_unlock_irqrestore(&priv->lock, flags); | ||
1766 | } | ||
1767 | EXPORT_SYMBOL(iwl_disable_ict); | ||
1768 | |||
1769 | /* interrupt handler using ict table, with this interrupt driver will | ||
1770 | * stop using INTA register to get device's interrupt, reading this register | ||
1771 | * is expensive, device will write interrupts in ICT dram table, increment | ||
1772 | * index then will fire interrupt to driver, driver will OR all ICT table | ||
1773 | * entries from current index up to table entry with 0 value. the result is | ||
1774 | * the interrupt we need to service, driver will set the entries back to 0 and | ||
1775 | * set index. | ||
1776 | */ | ||
1777 | irqreturn_t iwl_isr_ict(int irq, void *data) | ||
1778 | { | ||
1779 | struct iwl_priv *priv = data; | ||
1780 | u32 inta, inta_mask; | ||
1781 | u32 val = 0; | ||
1782 | |||
1783 | if (!priv) | ||
1784 | return IRQ_NONE; | ||
1785 | |||
1786 | /* dram interrupt table not set yet, | ||
1787 | * use legacy interrupt. | ||
1788 | */ | ||
1789 | if (!priv->use_ict) | ||
1790 | return iwl_isr(irq, data); | ||
1791 | |||
1792 | spin_lock(&priv->lock); | ||
1793 | |||
1794 | /* Disable (but don't clear!) interrupts here to avoid | ||
1795 | * back-to-back ISRs and sporadic interrupts from our NIC. | ||
1796 | * If we have something to service, the tasklet will re-enable ints. | ||
1797 | * If we *don't* have something, we'll re-enable before leaving here. | ||
1798 | */ | ||
1799 | inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */ | ||
1800 | iwl_write32(priv, CSR_INT_MASK, 0x00000000); | ||
1801 | |||
1802 | |||
1803 | /* Ignore interrupt if there's nothing in NIC to service. | ||
1804 | * This may be due to IRQ shared with another device, | ||
1805 | * or due to sporadic interrupts thrown from our NIC. */ | ||
1806 | if (!priv->ict_tbl[priv->ict_index]) { | ||
1807 | IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n"); | ||
1808 | goto none; | ||
1809 | } | ||
1810 | |||
1811 | /* read all entries that not 0 start with ict_index */ | ||
1812 | while (priv->ict_tbl[priv->ict_index]) { | ||
1813 | |||
1814 | val |= le32_to_cpu(priv->ict_tbl[priv->ict_index]); | ||
1815 | IWL_DEBUG_ISR(priv, "ICT index %d value 0x%08X\n", | ||
1816 | priv->ict_index, | ||
1817 | le32_to_cpu(priv->ict_tbl[priv->ict_index])); | ||
1818 | priv->ict_tbl[priv->ict_index] = 0; | ||
1819 | priv->ict_index = iwl_queue_inc_wrap(priv->ict_index, | ||
1820 | ICT_COUNT); | ||
1821 | |||
1822 | } | ||
1823 | |||
1824 | /* We should not get this value, just ignore it. */ | ||
1825 | if (val == 0xffffffff) | ||
1826 | val = 0; | ||
1827 | |||
1828 | /* | ||
1829 | * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit | ||
1830 | * (bit 15 before shifting it to 31) to clear when using interrupt | ||
1831 | * coalescing. fortunately, bits 18 and 19 stay set when this happens | ||
1832 | * so we use them to decide on the real state of the Rx bit. | ||
1833 | * In order words, bit 15 is set if bit 18 or bit 19 are set. | ||
1834 | */ | ||
1835 | if (val & 0xC0000) | ||
1836 | val |= 0x8000; | ||
1837 | |||
1838 | inta = (0xff & val) | ((0xff00 & val) << 16); | ||
1839 | IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n", | ||
1840 | inta, inta_mask, val); | ||
1841 | |||
1842 | inta &= priv->inta_mask; | ||
1843 | priv->inta |= inta; | ||
1844 | |||
1845 | /* iwl_irq_tasklet() will service interrupts and re-enable them */ | ||
1846 | if (likely(inta)) | ||
1847 | tasklet_schedule(&priv->irq_tasklet); | ||
1848 | else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta) { | ||
1849 | /* Allow interrupt if was disabled by this handler and | ||
1850 | * no tasklet was schedules, We should not enable interrupt, | ||
1851 | * tasklet will enable it. | ||
1852 | */ | ||
1853 | iwl_enable_interrupts(priv); | ||
1854 | } | ||
1855 | |||
1856 | spin_unlock(&priv->lock); | ||
1857 | return IRQ_HANDLED; | ||
1858 | |||
1859 | none: | ||
1860 | /* re-enable interrupts here since we don't have anything to service. | ||
1861 | * only Re-enable if disabled by irq. | ||
1862 | */ | ||
1863 | if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta) | ||
1864 | iwl_enable_interrupts(priv); | ||
1865 | |||
1866 | spin_unlock(&priv->lock); | ||
1867 | return IRQ_NONE; | ||
1868 | } | ||
1869 | EXPORT_SYMBOL(iwl_isr_ict); | ||
1870 | |||
1871 | |||
1872 | static irqreturn_t iwl_isr(int irq, void *data) | ||
1873 | { | ||
1874 | struct iwl_priv *priv = data; | ||
1875 | u32 inta, inta_mask; | ||
1876 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
1877 | u32 inta_fh; | ||
1878 | #endif | ||
1879 | if (!priv) | ||
1880 | return IRQ_NONE; | ||
1881 | |||
1882 | spin_lock(&priv->lock); | ||
1883 | |||
1884 | /* Disable (but don't clear!) interrupts here to avoid | ||
1885 | * back-to-back ISRs and sporadic interrupts from our NIC. | ||
1886 | * If we have something to service, the tasklet will re-enable ints. | ||
1887 | * If we *don't* have something, we'll re-enable before leaving here. */ | ||
1888 | inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */ | ||
1889 | iwl_write32(priv, CSR_INT_MASK, 0x00000000); | ||
1890 | |||
1891 | /* Discover which interrupts are active/pending */ | ||
1892 | inta = iwl_read32(priv, CSR_INT); | ||
1893 | |||
1894 | /* Ignore interrupt if there's nothing in NIC to service. | ||
1895 | * This may be due to IRQ shared with another device, | ||
1896 | * or due to sporadic interrupts thrown from our NIC. */ | ||
1897 | if (!inta) { | ||
1898 | IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n"); | ||
1899 | goto none; | ||
1900 | } | ||
1901 | |||
1902 | if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { | ||
1903 | /* Hardware disappeared. It might have already raised | ||
1904 | * an interrupt */ | ||
1905 | IWL_WARN(priv, "HARDWARE GONE?? INTA == 0x%08x\n", inta); | ||
1906 | goto unplugged; | ||
1907 | } | ||
1908 | |||
1909 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
1910 | if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { | ||
1911 | inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); | ||
1912 | IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, " | ||
1913 | "fh 0x%08x\n", inta, inta_mask, inta_fh); | ||
1914 | } | ||
1915 | #endif | ||
1916 | |||
1917 | priv->inta |= inta; | ||
1918 | /* iwl_irq_tasklet() will service interrupts and re-enable them */ | ||
1919 | if (likely(inta)) | ||
1920 | tasklet_schedule(&priv->irq_tasklet); | ||
1921 | else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta) | ||
1922 | iwl_enable_interrupts(priv); | ||
1923 | |||
1924 | unplugged: | ||
1925 | spin_unlock(&priv->lock); | ||
1926 | return IRQ_HANDLED; | ||
1927 | |||
1928 | none: | ||
1929 | /* re-enable interrupts here since we don't have anything to service. */ | ||
1930 | /* only Re-enable if diabled by irq and no schedules tasklet. */ | ||
1931 | if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta) | ||
1932 | iwl_enable_interrupts(priv); | ||
1933 | |||
1934 | spin_unlock(&priv->lock); | ||
1935 | return IRQ_NONE; | ||
1936 | } | ||
1937 | |||
1938 | irqreturn_t iwl_isr_legacy(int irq, void *data) | 1665 | irqreturn_t iwl_isr_legacy(int irq, void *data) |
1939 | { | 1666 | { |
1940 | struct iwl_priv *priv = data; | 1667 | struct iwl_priv *priv = data; |
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 4ef7739f9e8e..f493248d4727 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h | |||
@@ -560,11 +560,6 @@ int iwl_send_card_state(struct iwl_priv *priv, u32 flags, | |||
560 | * PCI * | 560 | * PCI * |
561 | *****************************************************/ | 561 | *****************************************************/ |
562 | irqreturn_t iwl_isr_legacy(int irq, void *data); | 562 | irqreturn_t iwl_isr_legacy(int irq, void *data); |
563 | int iwl_reset_ict(struct iwl_priv *priv); | ||
564 | void iwl_disable_ict(struct iwl_priv *priv); | ||
565 | int iwl_alloc_isr_ict(struct iwl_priv *priv); | ||
566 | void iwl_free_isr_ict(struct iwl_priv *priv); | ||
567 | irqreturn_t iwl_isr_ict(int irq, void *data); | ||
568 | 563 | ||
569 | static inline u16 iwl_pcie_link_ctl(struct iwl_priv *priv) | 564 | static inline u16 iwl_pcie_link_ctl(struct iwl_priv *priv) |
570 | { | 565 | { |
diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h index 51a67fb2e185..3ff6b9d25a10 100644 --- a/drivers/net/wireless/iwlwifi/iwl-helpers.h +++ b/drivers/net/wireless/iwlwifi/iwl-helpers.h | |||
@@ -31,6 +31,9 @@ | |||
31 | #define __iwl_helpers_h__ | 31 | #define __iwl_helpers_h__ |
32 | 32 | ||
33 | #include <linux/ctype.h> | 33 | #include <linux/ctype.h> |
34 | #include <net/mac80211.h> | ||
35 | |||
36 | #include "iwl-io.h" | ||
34 | 37 | ||
35 | #define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) | 38 | #define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) |
36 | 39 | ||