diff options
Diffstat (limited to 'drivers/net/wireless/ath5k/dma.c')
-rw-r--r-- | drivers/net/wireless/ath5k/dma.c | 190 |
1 files changed, 145 insertions, 45 deletions
diff --git a/drivers/net/wireless/ath5k/dma.c b/drivers/net/wireless/ath5k/dma.c index 7adceb2c7fab..7e2b1a67e5da 100644 --- a/drivers/net/wireless/ath5k/dma.c +++ b/drivers/net/wireless/ath5k/dma.c | |||
@@ -472,9 +472,6 @@ bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah) | |||
472 | * | 472 | * |
473 | * NOTE: We use read-and-clear register, so after this function is called ISR | 473 | * NOTE: We use read-and-clear register, so after this function is called ISR |
474 | * is zeroed. | 474 | * is zeroed. |
475 | * | ||
476 | * XXX: Why filter interrupts in sw with interrupt_mask ? No benefit at all | ||
477 | * plus it can be misleading (one might thing that we save interrupts this way) | ||
478 | */ | 475 | */ |
479 | int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) | 476 | int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) |
480 | { | 477 | { |
@@ -494,11 +491,16 @@ int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) | |||
494 | } | 491 | } |
495 | } else { | 492 | } else { |
496 | /* | 493 | /* |
497 | * Read interrupt status from the Read-And-Clear | 494 | * Read interrupt status from Interrupt |
498 | * shadow register. | 495 | * Status Register shadow copy (Read And Clear) |
496 | * | ||
499 | * Note: PISR/SISR Not available on 5210 | 497 | * Note: PISR/SISR Not available on 5210 |
500 | */ | 498 | */ |
501 | data = ath5k_hw_reg_read(ah, AR5K_RAC_PISR); | 499 | data = ath5k_hw_reg_read(ah, AR5K_RAC_PISR); |
500 | if (unlikely(data == AR5K_INT_NOCARD)) { | ||
501 | *interrupt_mask = data; | ||
502 | return -ENODEV; | ||
503 | } | ||
502 | } | 504 | } |
503 | 505 | ||
504 | /* | 506 | /* |
@@ -506,17 +508,9 @@ int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) | |||
506 | */ | 508 | */ |
507 | *interrupt_mask = (data & AR5K_INT_COMMON) & ah->ah_imr; | 509 | *interrupt_mask = (data & AR5K_INT_COMMON) & ah->ah_imr; |
508 | 510 | ||
509 | if (unlikely(data == AR5K_INT_NOCARD)) | ||
510 | return -ENODEV; | ||
511 | |||
512 | if (data & (AR5K_ISR_RXOK | AR5K_ISR_RXERR)) | ||
513 | *interrupt_mask |= AR5K_INT_RX; | ||
514 | |||
515 | if (data & (AR5K_ISR_TXOK | AR5K_ISR_TXERR | ||
516 | | AR5K_ISR_TXDESC | AR5K_ISR_TXEOL)) | ||
517 | *interrupt_mask |= AR5K_INT_TX; | ||
518 | |||
519 | if (ah->ah_version != AR5K_AR5210) { | 511 | if (ah->ah_version != AR5K_AR5210) { |
512 | u32 sisr2 = ath5k_hw_reg_read(ah, AR5K_RAC_SISR2); | ||
513 | |||
520 | /*HIU = Host Interface Unit (PCI etc)*/ | 514 | /*HIU = Host Interface Unit (PCI etc)*/ |
521 | if (unlikely(data & (AR5K_ISR_HIUERR))) | 515 | if (unlikely(data & (AR5K_ISR_HIUERR))) |
522 | *interrupt_mask |= AR5K_INT_FATAL; | 516 | *interrupt_mask |= AR5K_INT_FATAL; |
@@ -524,24 +518,93 @@ int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) | |||
524 | /*Beacon Not Ready*/ | 518 | /*Beacon Not Ready*/ |
525 | if (unlikely(data & (AR5K_ISR_BNR))) | 519 | if (unlikely(data & (AR5K_ISR_BNR))) |
526 | *interrupt_mask |= AR5K_INT_BNR; | 520 | *interrupt_mask |= AR5K_INT_BNR; |
527 | } | ||
528 | 521 | ||
529 | /* | 522 | if (unlikely(sisr2 & (AR5K_SISR2_SSERR | |
530 | * XXX: BMISS interrupts may occur after association. | 523 | AR5K_SISR2_DPERR | |
531 | * I found this on 5210 code but it needs testing. If this is | 524 | AR5K_SISR2_MCABT))) |
532 | * true we should disable them before assoc and re-enable them | 525 | *interrupt_mask |= AR5K_INT_FATAL; |
533 | * after a successfull assoc + some jiffies. | 526 | |
534 | */ | 527 | if (data & AR5K_ISR_TIM) |
535 | #if 0 | 528 | *interrupt_mask |= AR5K_INT_TIM; |
536 | interrupt_mask &= ~AR5K_INT_BMISS; | 529 | |
537 | #endif | 530 | if (data & AR5K_ISR_BCNMISC) { |
531 | if (sisr2 & AR5K_SISR2_TIM) | ||
532 | *interrupt_mask |= AR5K_INT_TIM; | ||
533 | if (sisr2 & AR5K_SISR2_DTIM) | ||
534 | *interrupt_mask |= AR5K_INT_DTIM; | ||
535 | if (sisr2 & AR5K_SISR2_DTIM_SYNC) | ||
536 | *interrupt_mask |= AR5K_INT_DTIM_SYNC; | ||
537 | if (sisr2 & AR5K_SISR2_BCN_TIMEOUT) | ||
538 | *interrupt_mask |= AR5K_INT_BCN_TIMEOUT; | ||
539 | if (sisr2 & AR5K_SISR2_CAB_TIMEOUT) | ||
540 | *interrupt_mask |= AR5K_INT_CAB_TIMEOUT; | ||
541 | } | ||
542 | |||
543 | if (data & AR5K_ISR_RXDOPPLER) | ||
544 | *interrupt_mask |= AR5K_INT_RX_DOPPLER; | ||
545 | if (data & AR5K_ISR_QCBRORN) { | ||
546 | *interrupt_mask |= AR5K_INT_QCBRORN; | ||
547 | ah->ah_txq_isr |= AR5K_REG_MS( | ||
548 | ath5k_hw_reg_read(ah, AR5K_RAC_SISR3), | ||
549 | AR5K_SISR3_QCBRORN); | ||
550 | } | ||
551 | if (data & AR5K_ISR_QCBRURN) { | ||
552 | *interrupt_mask |= AR5K_INT_QCBRURN; | ||
553 | ah->ah_txq_isr |= AR5K_REG_MS( | ||
554 | ath5k_hw_reg_read(ah, AR5K_RAC_SISR3), | ||
555 | AR5K_SISR3_QCBRURN); | ||
556 | } | ||
557 | if (data & AR5K_ISR_QTRIG) { | ||
558 | *interrupt_mask |= AR5K_INT_QTRIG; | ||
559 | ah->ah_txq_isr |= AR5K_REG_MS( | ||
560 | ath5k_hw_reg_read(ah, AR5K_RAC_SISR4), | ||
561 | AR5K_SISR4_QTRIG); | ||
562 | } | ||
563 | |||
564 | if (data & AR5K_ISR_TXOK) | ||
565 | ah->ah_txq_isr |= AR5K_REG_MS( | ||
566 | ath5k_hw_reg_read(ah, AR5K_RAC_SISR0), | ||
567 | AR5K_SISR0_QCU_TXOK); | ||
568 | |||
569 | if (data & AR5K_ISR_TXDESC) | ||
570 | ah->ah_txq_isr |= AR5K_REG_MS( | ||
571 | ath5k_hw_reg_read(ah, AR5K_RAC_SISR0), | ||
572 | AR5K_SISR0_QCU_TXDESC); | ||
573 | |||
574 | if (data & AR5K_ISR_TXERR) | ||
575 | ah->ah_txq_isr |= AR5K_REG_MS( | ||
576 | ath5k_hw_reg_read(ah, AR5K_RAC_SISR1), | ||
577 | AR5K_SISR1_QCU_TXERR); | ||
578 | |||
579 | if (data & AR5K_ISR_TXEOL) | ||
580 | ah->ah_txq_isr |= AR5K_REG_MS( | ||
581 | ath5k_hw_reg_read(ah, AR5K_RAC_SISR1), | ||
582 | AR5K_SISR1_QCU_TXEOL); | ||
583 | |||
584 | if (data & AR5K_ISR_TXURN) | ||
585 | ah->ah_txq_isr |= AR5K_REG_MS( | ||
586 | ath5k_hw_reg_read(ah, AR5K_RAC_SISR2), | ||
587 | AR5K_SISR2_QCU_TXURN); | ||
588 | } else { | ||
589 | if (unlikely(data & (AR5K_ISR_SSERR | AR5K_ISR_MCABT | ||
590 | | AR5K_ISR_HIUERR | AR5K_ISR_DPERR))) | ||
591 | *interrupt_mask |= AR5K_INT_FATAL; | ||
592 | |||
593 | /* | ||
594 | * XXX: BMISS interrupts may occur after association. | ||
595 | * I found this on 5210 code but it needs testing. If this is | ||
596 | * true we should disable them before assoc and re-enable them | ||
597 | * after a successfull assoc + some jiffies. | ||
598 | interrupt_mask &= ~AR5K_INT_BMISS; | ||
599 | */ | ||
600 | } | ||
538 | 601 | ||
539 | /* | 602 | /* |
540 | * In case we didn't handle anything, | 603 | * In case we didn't handle anything, |
541 | * print the register value. | 604 | * print the register value. |
542 | */ | 605 | */ |
543 | if (unlikely(*interrupt_mask == 0 && net_ratelimit())) | 606 | if (unlikely(*interrupt_mask == 0 && net_ratelimit())) |
544 | ATH5K_PRINTF("0x%08x\n", data); | 607 | ATH5K_PRINTF("ISR: 0x%08x IMR: 0x%08x\n", data, ah->ah_imr); |
545 | 608 | ||
546 | return 0; | 609 | return 0; |
547 | } | 610 | } |
@@ -560,14 +623,17 @@ enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) | |||
560 | { | 623 | { |
561 | enum ath5k_int old_mask, int_mask; | 624 | enum ath5k_int old_mask, int_mask; |
562 | 625 | ||
626 | old_mask = ah->ah_imr; | ||
627 | |||
563 | /* | 628 | /* |
564 | * Disable card interrupts to prevent any race conditions | 629 | * Disable card interrupts to prevent any race conditions |
565 | * (they will be re-enabled afterwards). | 630 | * (they will be re-enabled afterwards if AR5K_INT GLOBAL |
631 | * is set again on the new mask). | ||
566 | */ | 632 | */ |
567 | ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER); | 633 | if (old_mask & AR5K_INT_GLOBAL) { |
568 | ath5k_hw_reg_read(ah, AR5K_IER); | 634 | ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER); |
569 | 635 | ath5k_hw_reg_read(ah, AR5K_IER); | |
570 | old_mask = ah->ah_imr; | 636 | } |
571 | 637 | ||
572 | /* | 638 | /* |
573 | * Add additional, chipset-dependent interrupt mask flags | 639 | * Add additional, chipset-dependent interrupt mask flags |
@@ -575,30 +641,64 @@ enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) | |||
575 | */ | 641 | */ |
576 | int_mask = new_mask & AR5K_INT_COMMON; | 642 | int_mask = new_mask & AR5K_INT_COMMON; |
577 | 643 | ||
578 | if (new_mask & AR5K_INT_RX) | ||
579 | int_mask |= AR5K_IMR_RXOK | AR5K_IMR_RXERR | AR5K_IMR_RXORN | | ||
580 | AR5K_IMR_RXDESC; | ||
581 | |||
582 | if (new_mask & AR5K_INT_TX) | ||
583 | int_mask |= AR5K_IMR_TXOK | AR5K_IMR_TXERR | AR5K_IMR_TXDESC | | ||
584 | AR5K_IMR_TXURN; | ||
585 | |||
586 | if (ah->ah_version != AR5K_AR5210) { | 644 | if (ah->ah_version != AR5K_AR5210) { |
645 | /* Preserve per queue TXURN interrupt mask */ | ||
646 | u32 simr2 = ath5k_hw_reg_read(ah, AR5K_SIMR2) | ||
647 | & AR5K_SIMR2_QCU_TXURN; | ||
648 | |||
587 | if (new_mask & AR5K_INT_FATAL) { | 649 | if (new_mask & AR5K_INT_FATAL) { |
588 | int_mask |= AR5K_IMR_HIUERR; | 650 | int_mask |= AR5K_IMR_HIUERR; |
589 | AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_MCABT | | 651 | simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR |
590 | AR5K_SIMR2_SSERR | AR5K_SIMR2_DPERR); | 652 | | AR5K_SIMR2_DPERR); |
591 | } | 653 | } |
654 | |||
655 | /*Beacon Not Ready*/ | ||
656 | if (new_mask & AR5K_INT_BNR) | ||
657 | int_mask |= AR5K_INT_BNR; | ||
658 | |||
659 | if (new_mask & AR5K_INT_TIM) | ||
660 | int_mask |= AR5K_IMR_TIM; | ||
661 | |||
662 | if (new_mask & AR5K_INT_TIM) | ||
663 | simr2 |= AR5K_SISR2_TIM; | ||
664 | if (new_mask & AR5K_INT_DTIM) | ||
665 | simr2 |= AR5K_SISR2_DTIM; | ||
666 | if (new_mask & AR5K_INT_DTIM_SYNC) | ||
667 | simr2 |= AR5K_SISR2_DTIM_SYNC; | ||
668 | if (new_mask & AR5K_INT_BCN_TIMEOUT) | ||
669 | simr2 |= AR5K_SISR2_BCN_TIMEOUT; | ||
670 | if (new_mask & AR5K_INT_CAB_TIMEOUT) | ||
671 | simr2 |= AR5K_SISR2_CAB_TIMEOUT; | ||
672 | |||
673 | if (new_mask & AR5K_INT_RX_DOPPLER) | ||
674 | int_mask |= AR5K_IMR_RXDOPPLER; | ||
675 | |||
676 | /* Note: Per queue interrupt masks | ||
677 | * are set via reset_tx_queue (qcu.c) */ | ||
678 | ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR); | ||
679 | ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2); | ||
680 | |||
681 | } else { | ||
682 | if (new_mask & AR5K_INT_FATAL) | ||
683 | int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT | ||
684 | | AR5K_IMR_HIUERR | AR5K_IMR_DPERR); | ||
685 | |||
686 | ath5k_hw_reg_write(ah, int_mask, AR5K_IMR); | ||
592 | } | 687 | } |
593 | 688 | ||
594 | ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR); | 689 | /* If RXNOFRM interrupt is masked disable it |
690 | * by setting AR5K_RXNOFRM to zero */ | ||
691 | if (!(new_mask & AR5K_INT_RXNOFRM)) | ||
692 | ath5k_hw_reg_write(ah, 0, AR5K_RXNOFRM); | ||
595 | 693 | ||
596 | /* Store new interrupt mask */ | 694 | /* Store new interrupt mask */ |
597 | ah->ah_imr = new_mask; | 695 | ah->ah_imr = new_mask; |
598 | 696 | ||
599 | /* ..re-enable interrupts */ | 697 | /* ..re-enable interrupts if AR5K_INT_GLOBAL is set */ |
600 | ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER); | 698 | if (new_mask & AR5K_INT_GLOBAL) { |
601 | ath5k_hw_reg_read(ah, AR5K_IER); | 699 | ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER); |
700 | ath5k_hw_reg_read(ah, AR5K_IER); | ||
701 | } | ||
602 | 702 | ||
603 | return old_mask; | 703 | return old_mask; |
604 | } | 704 | } |