diff options
author | Gabor Juhos <juhosg@openwrt.org> | 2013-10-17 03:42:21 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-10-18 14:06:59 -0400 |
commit | 8d03e77218ff4bc59e4645438acbd3c5c7e0f654 (patch) | |
tree | 78610b13d390385c4b486078d1118297bb8a9517 | |
parent | b5cfde3fd9ff44c4ba831815a441d2b5a5af0b2f (diff) |
rt2x00: rt2800pci: move interrupt functions to the rt2800mmio module
Move the functions into a separate module, in order
to make those usable from other modules.
Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2800mmio.c | 396 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2800mmio.h | 9 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2800pci.c | 388 |
3 files changed, 405 insertions, 388 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.c b/drivers/net/wireless/rt2x00/rt2800mmio.c index 32b8e5058867..bfae4f03a522 100644 --- a/drivers/net/wireless/rt2x00/rt2800mmio.c +++ b/drivers/net/wireless/rt2x00/rt2800mmio.c | |||
@@ -34,6 +34,7 @@ | |||
34 | 34 | ||
35 | #include "rt2x00.h" | 35 | #include "rt2x00.h" |
36 | #include "rt2x00mmio.h" | 36 | #include "rt2x00mmio.h" |
37 | #include "rt2800.h" | ||
37 | #include "rt2800lib.h" | 38 | #include "rt2800lib.h" |
38 | #include "rt2800mmio.h" | 39 | #include "rt2800mmio.h" |
39 | 40 | ||
@@ -156,6 +157,401 @@ void rt2800mmio_fill_rxdone(struct queue_entry *entry, | |||
156 | } | 157 | } |
157 | EXPORT_SYMBOL_GPL(rt2800mmio_fill_rxdone); | 158 | EXPORT_SYMBOL_GPL(rt2800mmio_fill_rxdone); |
158 | 159 | ||
160 | /* | ||
161 | * Interrupt functions. | ||
162 | */ | ||
163 | static void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev) | ||
164 | { | ||
165 | struct ieee80211_conf conf = { .flags = 0 }; | ||
166 | struct rt2x00lib_conf libconf = { .conf = &conf }; | ||
167 | |||
168 | rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); | ||
169 | } | ||
170 | |||
171 | static bool rt2800mmio_txdone_entry_check(struct queue_entry *entry, u32 status) | ||
172 | { | ||
173 | __le32 *txwi; | ||
174 | u32 word; | ||
175 | int wcid, tx_wcid; | ||
176 | |||
177 | wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID); | ||
178 | |||
179 | txwi = rt2800_drv_get_txwi(entry); | ||
180 | rt2x00_desc_read(txwi, 1, &word); | ||
181 | tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); | ||
182 | |||
183 | return (tx_wcid == wcid); | ||
184 | } | ||
185 | |||
186 | static bool rt2800mmio_txdone_find_entry(struct queue_entry *entry, void *data) | ||
187 | { | ||
188 | u32 status = *(u32 *)data; | ||
189 | |||
190 | /* | ||
191 | * rt2800pci hardware might reorder frames when exchanging traffic | ||
192 | * with multiple BA enabled STAs. | ||
193 | * | ||
194 | * For example, a tx queue | ||
195 | * [ STA1 | STA2 | STA1 | STA2 ] | ||
196 | * can result in tx status reports | ||
197 | * [ STA1 | STA1 | STA2 | STA2 ] | ||
198 | * when the hw decides to aggregate the frames for STA1 into one AMPDU. | ||
199 | * | ||
200 | * To mitigate this effect, associate the tx status to the first frame | ||
201 | * in the tx queue with a matching wcid. | ||
202 | */ | ||
203 | if (rt2800mmio_txdone_entry_check(entry, status) && | ||
204 | !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { | ||
205 | /* | ||
206 | * Got a matching frame, associate the tx status with | ||
207 | * the frame | ||
208 | */ | ||
209 | entry->status = status; | ||
210 | set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); | ||
211 | return true; | ||
212 | } | ||
213 | |||
214 | /* Check the next frame */ | ||
215 | return false; | ||
216 | } | ||
217 | |||
218 | static bool rt2800mmio_txdone_match_first(struct queue_entry *entry, void *data) | ||
219 | { | ||
220 | u32 status = *(u32 *)data; | ||
221 | |||
222 | /* | ||
223 | * Find the first frame without tx status and assign this status to it | ||
224 | * regardless if it matches or not. | ||
225 | */ | ||
226 | if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { | ||
227 | /* | ||
228 | * Got a matching frame, associate the tx status with | ||
229 | * the frame | ||
230 | */ | ||
231 | entry->status = status; | ||
232 | set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); | ||
233 | return true; | ||
234 | } | ||
235 | |||
236 | /* Check the next frame */ | ||
237 | return false; | ||
238 | } | ||
239 | static bool rt2800mmio_txdone_release_entries(struct queue_entry *entry, | ||
240 | void *data) | ||
241 | { | ||
242 | if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { | ||
243 | rt2800_txdone_entry(entry, entry->status, | ||
244 | rt2800mmio_get_txwi(entry)); | ||
245 | return false; | ||
246 | } | ||
247 | |||
248 | /* No more frames to release */ | ||
249 | return true; | ||
250 | } | ||
251 | |||
252 | static bool rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev) | ||
253 | { | ||
254 | struct data_queue *queue; | ||
255 | u32 status; | ||
256 | u8 qid; | ||
257 | int max_tx_done = 16; | ||
258 | |||
259 | while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) { | ||
260 | qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE); | ||
261 | if (unlikely(qid >= QID_RX)) { | ||
262 | /* | ||
263 | * Unknown queue, this shouldn't happen. Just drop | ||
264 | * this tx status. | ||
265 | */ | ||
266 | rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n", | ||
267 | qid); | ||
268 | break; | ||
269 | } | ||
270 | |||
271 | queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); | ||
272 | if (unlikely(queue == NULL)) { | ||
273 | /* | ||
274 | * The queue is NULL, this shouldn't happen. Stop | ||
275 | * processing here and drop the tx status | ||
276 | */ | ||
277 | rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n", | ||
278 | qid); | ||
279 | break; | ||
280 | } | ||
281 | |||
282 | if (unlikely(rt2x00queue_empty(queue))) { | ||
283 | /* | ||
284 | * The queue is empty. Stop processing here | ||
285 | * and drop the tx status. | ||
286 | */ | ||
287 | rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", | ||
288 | qid); | ||
289 | break; | ||
290 | } | ||
291 | |||
292 | /* | ||
293 | * Let's associate this tx status with the first | ||
294 | * matching frame. | ||
295 | */ | ||
296 | if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, | ||
297 | Q_INDEX, &status, | ||
298 | rt2800mmio_txdone_find_entry)) { | ||
299 | /* | ||
300 | * We cannot match the tx status to any frame, so just | ||
301 | * use the first one. | ||
302 | */ | ||
303 | if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, | ||
304 | Q_INDEX, &status, | ||
305 | rt2800mmio_txdone_match_first)) { | ||
306 | rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n", | ||
307 | qid); | ||
308 | break; | ||
309 | } | ||
310 | } | ||
311 | |||
312 | /* | ||
313 | * Release all frames with a valid tx status. | ||
314 | */ | ||
315 | rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, | ||
316 | Q_INDEX, NULL, | ||
317 | rt2800mmio_txdone_release_entries); | ||
318 | |||
319 | if (--max_tx_done == 0) | ||
320 | break; | ||
321 | } | ||
322 | |||
323 | return !max_tx_done; | ||
324 | } | ||
325 | |||
326 | static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev, | ||
327 | struct rt2x00_field32 irq_field) | ||
328 | { | ||
329 | u32 reg; | ||
330 | |||
331 | /* | ||
332 | * Enable a single interrupt. The interrupt mask register | ||
333 | * access needs locking. | ||
334 | */ | ||
335 | spin_lock_irq(&rt2x00dev->irqmask_lock); | ||
336 | rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); | ||
337 | rt2x00_set_field32(®, irq_field, 1); | ||
338 | rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
339 | spin_unlock_irq(&rt2x00dev->irqmask_lock); | ||
340 | } | ||
341 | |||
342 | void rt2800mmio_txstatus_tasklet(unsigned long data) | ||
343 | { | ||
344 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
345 | if (rt2800mmio_txdone(rt2x00dev)) | ||
346 | tasklet_schedule(&rt2x00dev->txstatus_tasklet); | ||
347 | |||
348 | /* | ||
349 | * No need to enable the tx status interrupt here as we always | ||
350 | * leave it enabled to minimize the possibility of a tx status | ||
351 | * register overflow. See comment in interrupt handler. | ||
352 | */ | ||
353 | } | ||
354 | EXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet); | ||
355 | |||
356 | void rt2800mmio_pretbtt_tasklet(unsigned long data) | ||
357 | { | ||
358 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
359 | rt2x00lib_pretbtt(rt2x00dev); | ||
360 | if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
361 | rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); | ||
362 | } | ||
363 | EXPORT_SYMBOL_GPL(rt2800mmio_pretbtt_tasklet); | ||
364 | |||
365 | void rt2800mmio_tbtt_tasklet(unsigned long data) | ||
366 | { | ||
367 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
368 | struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; | ||
369 | u32 reg; | ||
370 | |||
371 | rt2x00lib_beacondone(rt2x00dev); | ||
372 | |||
373 | if (rt2x00dev->intf_ap_count) { | ||
374 | /* | ||
375 | * The rt2800pci hardware tbtt timer is off by 1us per tbtt | ||
376 | * causing beacon skew and as a result causing problems with | ||
377 | * some powersaving clients over time. Shorten the beacon | ||
378 | * interval every 64 beacons by 64us to mitigate this effect. | ||
379 | */ | ||
380 | if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) { | ||
381 | rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); | ||
382 | rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, | ||
383 | (rt2x00dev->beacon_int * 16) - 1); | ||
384 | rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); | ||
385 | } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) { | ||
386 | rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); | ||
387 | rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, | ||
388 | (rt2x00dev->beacon_int * 16)); | ||
389 | rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); | ||
390 | } | ||
391 | drv_data->tbtt_tick++; | ||
392 | drv_data->tbtt_tick %= BCN_TBTT_OFFSET; | ||
393 | } | ||
394 | |||
395 | if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
396 | rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); | ||
397 | } | ||
398 | EXPORT_SYMBOL_GPL(rt2800mmio_tbtt_tasklet); | ||
399 | |||
400 | void rt2800mmio_rxdone_tasklet(unsigned long data) | ||
401 | { | ||
402 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
403 | if (rt2x00mmio_rxdone(rt2x00dev)) | ||
404 | tasklet_schedule(&rt2x00dev->rxdone_tasklet); | ||
405 | else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
406 | rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); | ||
407 | } | ||
408 | EXPORT_SYMBOL_GPL(rt2800mmio_rxdone_tasklet); | ||
409 | |||
410 | void rt2800mmio_autowake_tasklet(unsigned long data) | ||
411 | { | ||
412 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
413 | rt2800mmio_wakeup(rt2x00dev); | ||
414 | if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
415 | rt2800mmio_enable_interrupt(rt2x00dev, | ||
416 | INT_MASK_CSR_AUTO_WAKEUP); | ||
417 | } | ||
418 | EXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet); | ||
419 | |||
420 | static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) | ||
421 | { | ||
422 | u32 status; | ||
423 | int i; | ||
424 | |||
425 | /* | ||
426 | * The TX_FIFO_STATUS interrupt needs special care. We should | ||
427 | * read TX_STA_FIFO but we should do it immediately as otherwise | ||
428 | * the register can overflow and we would lose status reports. | ||
429 | * | ||
430 | * Hence, read the TX_STA_FIFO register and copy all tx status | ||
431 | * reports into a kernel FIFO which is handled in the txstatus | ||
432 | * tasklet. We use a tasklet to process the tx status reports | ||
433 | * because we can schedule the tasklet multiple times (when the | ||
434 | * interrupt fires again during tx status processing). | ||
435 | * | ||
436 | * Furthermore we don't disable the TX_FIFO_STATUS | ||
437 | * interrupt here but leave it enabled so that the TX_STA_FIFO | ||
438 | * can also be read while the tx status tasklet gets executed. | ||
439 | * | ||
440 | * Since we have only one producer and one consumer we don't | ||
441 | * need to lock the kfifo. | ||
442 | */ | ||
443 | for (i = 0; i < rt2x00dev->tx->limit; i++) { | ||
444 | rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status); | ||
445 | |||
446 | if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) | ||
447 | break; | ||
448 | |||
449 | if (!kfifo_put(&rt2x00dev->txstatus_fifo, &status)) { | ||
450 | rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n"); | ||
451 | break; | ||
452 | } | ||
453 | } | ||
454 | |||
455 | /* Schedule the tasklet for processing the tx status. */ | ||
456 | tasklet_schedule(&rt2x00dev->txstatus_tasklet); | ||
457 | } | ||
458 | |||
459 | irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance) | ||
460 | { | ||
461 | struct rt2x00_dev *rt2x00dev = dev_instance; | ||
462 | u32 reg, mask; | ||
463 | |||
464 | /* Read status and ACK all interrupts */ | ||
465 | rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); | ||
466 | rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); | ||
467 | |||
468 | if (!reg) | ||
469 | return IRQ_NONE; | ||
470 | |||
471 | if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
472 | return IRQ_HANDLED; | ||
473 | |||
474 | /* | ||
475 | * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits | ||
476 | * for interrupts and interrupt masks we can just use the value of | ||
477 | * INT_SOURCE_CSR to create the interrupt mask. | ||
478 | */ | ||
479 | mask = ~reg; | ||
480 | |||
481 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { | ||
482 | rt2800mmio_txstatus_interrupt(rt2x00dev); | ||
483 | /* | ||
484 | * Never disable the TX_FIFO_STATUS interrupt. | ||
485 | */ | ||
486 | rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); | ||
487 | } | ||
488 | |||
489 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) | ||
490 | tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); | ||
491 | |||
492 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) | ||
493 | tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); | ||
494 | |||
495 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) | ||
496 | tasklet_schedule(&rt2x00dev->rxdone_tasklet); | ||
497 | |||
498 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) | ||
499 | tasklet_schedule(&rt2x00dev->autowake_tasklet); | ||
500 | |||
501 | /* | ||
502 | * Disable all interrupts for which a tasklet was scheduled right now, | ||
503 | * the tasklet will reenable the appropriate interrupts. | ||
504 | */ | ||
505 | spin_lock(&rt2x00dev->irqmask_lock); | ||
506 | rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); | ||
507 | reg &= mask; | ||
508 | rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
509 | spin_unlock(&rt2x00dev->irqmask_lock); | ||
510 | |||
511 | return IRQ_HANDLED; | ||
512 | } | ||
513 | EXPORT_SYMBOL_GPL(rt2800mmio_interrupt); | ||
514 | |||
515 | void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, | ||
516 | enum dev_state state) | ||
517 | { | ||
518 | u32 reg; | ||
519 | unsigned long flags; | ||
520 | |||
521 | /* | ||
522 | * When interrupts are being enabled, the interrupt registers | ||
523 | * should clear the register to assure a clean state. | ||
524 | */ | ||
525 | if (state == STATE_RADIO_IRQ_ON) { | ||
526 | rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); | ||
527 | rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); | ||
528 | } | ||
529 | |||
530 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
531 | reg = 0; | ||
532 | if (state == STATE_RADIO_IRQ_ON) { | ||
533 | rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); | ||
534 | rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); | ||
535 | rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); | ||
536 | rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); | ||
537 | rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); | ||
538 | } | ||
539 | rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
540 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); | ||
541 | |||
542 | if (state == STATE_RADIO_IRQ_OFF) { | ||
543 | /* | ||
544 | * Wait for possibly running tasklets to finish. | ||
545 | */ | ||
546 | tasklet_kill(&rt2x00dev->txstatus_tasklet); | ||
547 | tasklet_kill(&rt2x00dev->rxdone_tasklet); | ||
548 | tasklet_kill(&rt2x00dev->autowake_tasklet); | ||
549 | tasklet_kill(&rt2x00dev->tbtt_tasklet); | ||
550 | tasklet_kill(&rt2x00dev->pretbtt_tasklet); | ||
551 | } | ||
552 | } | ||
553 | EXPORT_SYMBOL_GPL(rt2800mmio_toggle_irq); | ||
554 | |||
159 | MODULE_AUTHOR(DRV_PROJECT); | 555 | MODULE_AUTHOR(DRV_PROJECT); |
160 | MODULE_VERSION(DRV_VERSION); | 556 | MODULE_VERSION(DRV_VERSION); |
161 | MODULE_DESCRIPTION("rt2800 MMIO library"); | 557 | MODULE_DESCRIPTION("rt2800 MMIO library"); |
diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.h b/drivers/net/wireless/rt2x00/rt2800mmio.h index 71e3d2268436..30e66ab9af37 100644 --- a/drivers/net/wireless/rt2x00/rt2800mmio.h +++ b/drivers/net/wireless/rt2x00/rt2800mmio.h | |||
@@ -128,5 +128,14 @@ void rt2800mmio_write_tx_desc(struct queue_entry *entry, | |||
128 | void rt2800mmio_fill_rxdone(struct queue_entry *entry, | 128 | void rt2800mmio_fill_rxdone(struct queue_entry *entry, |
129 | struct rxdone_entry_desc *rxdesc); | 129 | struct rxdone_entry_desc *rxdesc); |
130 | 130 | ||
131 | /* Interrupt functions */ | ||
132 | void rt2800mmio_txstatus_tasklet(unsigned long data); | ||
133 | void rt2800mmio_pretbtt_tasklet(unsigned long data); | ||
134 | void rt2800mmio_tbtt_tasklet(unsigned long data); | ||
135 | void rt2800mmio_rxdone_tasklet(unsigned long data); | ||
136 | void rt2800mmio_autowake_tasklet(unsigned long data); | ||
137 | irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance); | ||
138 | void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, | ||
139 | enum dev_state state); | ||
131 | 140 | ||
132 | #endif /* RT2800MMIO_H */ | 141 | #endif /* RT2800MMIO_H */ |
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index a829ce59e026..12454b0a6383 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c | |||
@@ -448,45 +448,6 @@ static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev) | |||
448 | /* | 448 | /* |
449 | * Device state switch handlers. | 449 | * Device state switch handlers. |
450 | */ | 450 | */ |
451 | static void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, | ||
452 | enum dev_state state) | ||
453 | { | ||
454 | u32 reg; | ||
455 | unsigned long flags; | ||
456 | |||
457 | /* | ||
458 | * When interrupts are being enabled, the interrupt registers | ||
459 | * should clear the register to assure a clean state. | ||
460 | */ | ||
461 | if (state == STATE_RADIO_IRQ_ON) { | ||
462 | rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); | ||
463 | rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); | ||
464 | } | ||
465 | |||
466 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
467 | reg = 0; | ||
468 | if (state == STATE_RADIO_IRQ_ON) { | ||
469 | rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); | ||
470 | rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); | ||
471 | rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); | ||
472 | rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); | ||
473 | rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); | ||
474 | } | ||
475 | rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
476 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); | ||
477 | |||
478 | if (state == STATE_RADIO_IRQ_OFF) { | ||
479 | /* | ||
480 | * Wait for possibly running tasklets to finish. | ||
481 | */ | ||
482 | tasklet_kill(&rt2x00dev->txstatus_tasklet); | ||
483 | tasklet_kill(&rt2x00dev->rxdone_tasklet); | ||
484 | tasklet_kill(&rt2x00dev->autowake_tasklet); | ||
485 | tasklet_kill(&rt2x00dev->tbtt_tasklet); | ||
486 | tasklet_kill(&rt2x00dev->pretbtt_tasklet); | ||
487 | } | ||
488 | } | ||
489 | |||
490 | static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev) | 451 | static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev) |
491 | { | 452 | { |
492 | u32 reg; | 453 | u32 reg; |
@@ -628,355 +589,6 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev, | |||
628 | } | 589 | } |
629 | 590 | ||
630 | /* | 591 | /* |
631 | * Interrupt functions. | ||
632 | */ | ||
633 | static void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev) | ||
634 | { | ||
635 | struct ieee80211_conf conf = { .flags = 0 }; | ||
636 | struct rt2x00lib_conf libconf = { .conf = &conf }; | ||
637 | |||
638 | rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); | ||
639 | } | ||
640 | |||
641 | static bool rt2800mmio_txdone_entry_check(struct queue_entry *entry, u32 status) | ||
642 | { | ||
643 | __le32 *txwi; | ||
644 | u32 word; | ||
645 | int wcid, tx_wcid; | ||
646 | |||
647 | wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID); | ||
648 | |||
649 | txwi = rt2800_drv_get_txwi(entry); | ||
650 | rt2x00_desc_read(txwi, 1, &word); | ||
651 | tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); | ||
652 | |||
653 | return (tx_wcid == wcid); | ||
654 | } | ||
655 | |||
656 | static bool rt2800mmio_txdone_find_entry(struct queue_entry *entry, void *data) | ||
657 | { | ||
658 | u32 status = *(u32 *)data; | ||
659 | |||
660 | /* | ||
661 | * rt2800pci hardware might reorder frames when exchanging traffic | ||
662 | * with multiple BA enabled STAs. | ||
663 | * | ||
664 | * For example, a tx queue | ||
665 | * [ STA1 | STA2 | STA1 | STA2 ] | ||
666 | * can result in tx status reports | ||
667 | * [ STA1 | STA1 | STA2 | STA2 ] | ||
668 | * when the hw decides to aggregate the frames for STA1 into one AMPDU. | ||
669 | * | ||
670 | * To mitigate this effect, associate the tx status to the first frame | ||
671 | * in the tx queue with a matching wcid. | ||
672 | */ | ||
673 | if (rt2800mmio_txdone_entry_check(entry, status) && | ||
674 | !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { | ||
675 | /* | ||
676 | * Got a matching frame, associate the tx status with | ||
677 | * the frame | ||
678 | */ | ||
679 | entry->status = status; | ||
680 | set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); | ||
681 | return true; | ||
682 | } | ||
683 | |||
684 | /* Check the next frame */ | ||
685 | return false; | ||
686 | } | ||
687 | |||
688 | static bool rt2800mmio_txdone_match_first(struct queue_entry *entry, void *data) | ||
689 | { | ||
690 | u32 status = *(u32 *)data; | ||
691 | |||
692 | /* | ||
693 | * Find the first frame without tx status and assign this status to it | ||
694 | * regardless if it matches or not. | ||
695 | */ | ||
696 | if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { | ||
697 | /* | ||
698 | * Got a matching frame, associate the tx status with | ||
699 | * the frame | ||
700 | */ | ||
701 | entry->status = status; | ||
702 | set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); | ||
703 | return true; | ||
704 | } | ||
705 | |||
706 | /* Check the next frame */ | ||
707 | return false; | ||
708 | } | ||
709 | static bool rt2800mmio_txdone_release_entries(struct queue_entry *entry, | ||
710 | void *data) | ||
711 | { | ||
712 | if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { | ||
713 | rt2800_txdone_entry(entry, entry->status, | ||
714 | rt2800mmio_get_txwi(entry)); | ||
715 | return false; | ||
716 | } | ||
717 | |||
718 | /* No more frames to release */ | ||
719 | return true; | ||
720 | } | ||
721 | |||
722 | static bool rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev) | ||
723 | { | ||
724 | struct data_queue *queue; | ||
725 | u32 status; | ||
726 | u8 qid; | ||
727 | int max_tx_done = 16; | ||
728 | |||
729 | while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) { | ||
730 | qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE); | ||
731 | if (unlikely(qid >= QID_RX)) { | ||
732 | /* | ||
733 | * Unknown queue, this shouldn't happen. Just drop | ||
734 | * this tx status. | ||
735 | */ | ||
736 | rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n", | ||
737 | qid); | ||
738 | break; | ||
739 | } | ||
740 | |||
741 | queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); | ||
742 | if (unlikely(queue == NULL)) { | ||
743 | /* | ||
744 | * The queue is NULL, this shouldn't happen. Stop | ||
745 | * processing here and drop the tx status | ||
746 | */ | ||
747 | rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n", | ||
748 | qid); | ||
749 | break; | ||
750 | } | ||
751 | |||
752 | if (unlikely(rt2x00queue_empty(queue))) { | ||
753 | /* | ||
754 | * The queue is empty. Stop processing here | ||
755 | * and drop the tx status. | ||
756 | */ | ||
757 | rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", | ||
758 | qid); | ||
759 | break; | ||
760 | } | ||
761 | |||
762 | /* | ||
763 | * Let's associate this tx status with the first | ||
764 | * matching frame. | ||
765 | */ | ||
766 | if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, | ||
767 | Q_INDEX, &status, | ||
768 | rt2800mmio_txdone_find_entry)) { | ||
769 | /* | ||
770 | * We cannot match the tx status to any frame, so just | ||
771 | * use the first one. | ||
772 | */ | ||
773 | if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, | ||
774 | Q_INDEX, &status, | ||
775 | rt2800mmio_txdone_match_first)) { | ||
776 | rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n", | ||
777 | qid); | ||
778 | break; | ||
779 | } | ||
780 | } | ||
781 | |||
782 | /* | ||
783 | * Release all frames with a valid tx status. | ||
784 | */ | ||
785 | rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, | ||
786 | Q_INDEX, NULL, | ||
787 | rt2800mmio_txdone_release_entries); | ||
788 | |||
789 | if (--max_tx_done == 0) | ||
790 | break; | ||
791 | } | ||
792 | |||
793 | return !max_tx_done; | ||
794 | } | ||
795 | |||
796 | static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev, | ||
797 | struct rt2x00_field32 irq_field) | ||
798 | { | ||
799 | u32 reg; | ||
800 | |||
801 | /* | ||
802 | * Enable a single interrupt. The interrupt mask register | ||
803 | * access needs locking. | ||
804 | */ | ||
805 | spin_lock_irq(&rt2x00dev->irqmask_lock); | ||
806 | rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); | ||
807 | rt2x00_set_field32(®, irq_field, 1); | ||
808 | rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
809 | spin_unlock_irq(&rt2x00dev->irqmask_lock); | ||
810 | } | ||
811 | |||
812 | static void rt2800mmio_txstatus_tasklet(unsigned long data) | ||
813 | { | ||
814 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
815 | if (rt2800mmio_txdone(rt2x00dev)) | ||
816 | tasklet_schedule(&rt2x00dev->txstatus_tasklet); | ||
817 | |||
818 | /* | ||
819 | * No need to enable the tx status interrupt here as we always | ||
820 | * leave it enabled to minimize the possibility of a tx status | ||
821 | * register overflow. See comment in interrupt handler. | ||
822 | */ | ||
823 | } | ||
824 | |||
825 | static void rt2800mmio_pretbtt_tasklet(unsigned long data) | ||
826 | { | ||
827 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
828 | rt2x00lib_pretbtt(rt2x00dev); | ||
829 | if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
830 | rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); | ||
831 | } | ||
832 | |||
833 | static void rt2800mmio_tbtt_tasklet(unsigned long data) | ||
834 | { | ||
835 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
836 | struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; | ||
837 | u32 reg; | ||
838 | |||
839 | rt2x00lib_beacondone(rt2x00dev); | ||
840 | |||
841 | if (rt2x00dev->intf_ap_count) { | ||
842 | /* | ||
843 | * The rt2800pci hardware tbtt timer is off by 1us per tbtt | ||
844 | * causing beacon skew and as a result causing problems with | ||
845 | * some powersaving clients over time. Shorten the beacon | ||
846 | * interval every 64 beacons by 64us to mitigate this effect. | ||
847 | */ | ||
848 | if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) { | ||
849 | rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); | ||
850 | rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, | ||
851 | (rt2x00dev->beacon_int * 16) - 1); | ||
852 | rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); | ||
853 | } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) { | ||
854 | rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); | ||
855 | rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, | ||
856 | (rt2x00dev->beacon_int * 16)); | ||
857 | rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); | ||
858 | } | ||
859 | drv_data->tbtt_tick++; | ||
860 | drv_data->tbtt_tick %= BCN_TBTT_OFFSET; | ||
861 | } | ||
862 | |||
863 | if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
864 | rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); | ||
865 | } | ||
866 | |||
867 | static void rt2800mmio_rxdone_tasklet(unsigned long data) | ||
868 | { | ||
869 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
870 | if (rt2x00mmio_rxdone(rt2x00dev)) | ||
871 | tasklet_schedule(&rt2x00dev->rxdone_tasklet); | ||
872 | else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
873 | rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); | ||
874 | } | ||
875 | |||
876 | static void rt2800mmio_autowake_tasklet(unsigned long data) | ||
877 | { | ||
878 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
879 | rt2800mmio_wakeup(rt2x00dev); | ||
880 | if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
881 | rt2800mmio_enable_interrupt(rt2x00dev, | ||
882 | INT_MASK_CSR_AUTO_WAKEUP); | ||
883 | } | ||
884 | |||
885 | static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) | ||
886 | { | ||
887 | u32 status; | ||
888 | int i; | ||
889 | |||
890 | /* | ||
891 | * The TX_FIFO_STATUS interrupt needs special care. We should | ||
892 | * read TX_STA_FIFO but we should do it immediately as otherwise | ||
893 | * the register can overflow and we would lose status reports. | ||
894 | * | ||
895 | * Hence, read the TX_STA_FIFO register and copy all tx status | ||
896 | * reports into a kernel FIFO which is handled in the txstatus | ||
897 | * tasklet. We use a tasklet to process the tx status reports | ||
898 | * because we can schedule the tasklet multiple times (when the | ||
899 | * interrupt fires again during tx status processing). | ||
900 | * | ||
901 | * Furthermore we don't disable the TX_FIFO_STATUS | ||
902 | * interrupt here but leave it enabled so that the TX_STA_FIFO | ||
903 | * can also be read while the tx status tasklet gets executed. | ||
904 | * | ||
905 | * Since we have only one producer and one consumer we don't | ||
906 | * need to lock the kfifo. | ||
907 | */ | ||
908 | for (i = 0; i < rt2x00dev->tx->limit; i++) { | ||
909 | rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status); | ||
910 | |||
911 | if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) | ||
912 | break; | ||
913 | |||
914 | if (!kfifo_put(&rt2x00dev->txstatus_fifo, &status)) { | ||
915 | rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n"); | ||
916 | break; | ||
917 | } | ||
918 | } | ||
919 | |||
920 | /* Schedule the tasklet for processing the tx status. */ | ||
921 | tasklet_schedule(&rt2x00dev->txstatus_tasklet); | ||
922 | } | ||
923 | |||
924 | static irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance) | ||
925 | { | ||
926 | struct rt2x00_dev *rt2x00dev = dev_instance; | ||
927 | u32 reg, mask; | ||
928 | |||
929 | /* Read status and ACK all interrupts */ | ||
930 | rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); | ||
931 | rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); | ||
932 | |||
933 | if (!reg) | ||
934 | return IRQ_NONE; | ||
935 | |||
936 | if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
937 | return IRQ_HANDLED; | ||
938 | |||
939 | /* | ||
940 | * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits | ||
941 | * for interrupts and interrupt masks we can just use the value of | ||
942 | * INT_SOURCE_CSR to create the interrupt mask. | ||
943 | */ | ||
944 | mask = ~reg; | ||
945 | |||
946 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { | ||
947 | rt2800mmio_txstatus_interrupt(rt2x00dev); | ||
948 | /* | ||
949 | * Never disable the TX_FIFO_STATUS interrupt. | ||
950 | */ | ||
951 | rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); | ||
952 | } | ||
953 | |||
954 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) | ||
955 | tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); | ||
956 | |||
957 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) | ||
958 | tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); | ||
959 | |||
960 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) | ||
961 | tasklet_schedule(&rt2x00dev->rxdone_tasklet); | ||
962 | |||
963 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) | ||
964 | tasklet_schedule(&rt2x00dev->autowake_tasklet); | ||
965 | |||
966 | /* | ||
967 | * Disable all interrupts for which a tasklet was scheduled right now, | ||
968 | * the tasklet will reenable the appropriate interrupts. | ||
969 | */ | ||
970 | spin_lock(&rt2x00dev->irqmask_lock); | ||
971 | rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); | ||
972 | reg &= mask; | ||
973 | rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
974 | spin_unlock(&rt2x00dev->irqmask_lock); | ||
975 | |||
976 | return IRQ_HANDLED; | ||
977 | } | ||
978 | |||
979 | /* | ||
980 | * Device probe functions. | 592 | * Device probe functions. |
981 | */ | 593 | */ |
982 | static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) | 594 | static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) |