ath9k: never read from the AR_IMR_S2 register
authorPavel Roskin <proski@gnu.org>
Tue, 23 Feb 2010 23:15:27 +0000 (18:15 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 9 Mar 2010 20:03:06 +0000 (15:03 -0500)
The AR_IMR_S2 register sometimes cannot be read correctly.  Instead of a
valid value, 0xdeadbeef is returned.  The driver has been observed
writing that value back to AR_IMR_S2 after changing a few bits.

Cache the register value in ah->imrs2_reg and always write chached value
to the register.

Signed-off-by: Pavel Roskin <proski@gnu.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/mac.c

index 2e767cf..1fb14ed 100644 (file)
@@ -1151,7 +1151,8 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
                ah->mask_reg |= AR_IMR_MIB;
 
        REG_WRITE(ah, AR_IMR, ah->mask_reg);
-       REG_WRITE(ah, AR_IMR_S2, REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_GTT);
+       ah->imrs2_reg |= AR_IMR_S2_GTT;
+       REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg);
 
        if (!AR_SREV_9100(ah)) {
                REG_WRITE(ah, AR_INTR_SYNC_CAUSE, 0xFFFFFFFF);
@@ -2920,14 +2921,11 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
 
        ath_print(common, ATH_DBG_INTERRUPT, "new IMR 0x%x\n", mask);
        REG_WRITE(ah, AR_IMR, mask);
-       mask = REG_READ(ah, AR_IMR_S2) & ~(AR_IMR_S2_TIM |
-                                          AR_IMR_S2_DTIM |
-                                          AR_IMR_S2_DTIMSYNC |
-                                          AR_IMR_S2_CABEND |
-                                          AR_IMR_S2_CABTO |
-                                          AR_IMR_S2_TSFOOR |
-                                          AR_IMR_S2_GTT | AR_IMR_S2_CST);
-       REG_WRITE(ah, AR_IMR_S2, mask | mask2);
+       ah->imrs2_reg &= ~(AR_IMR_S2_TIM | AR_IMR_S2_DTIM | AR_IMR_S2_DTIMSYNC |
+                          AR_IMR_S2_CABEND | AR_IMR_S2_CABTO |
+                          AR_IMR_S2_TSFOOR | AR_IMR_S2_GTT | AR_IMR_S2_CST);
+       ah->imrs2_reg |= mask2;
+       REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg);
        ah->mask_reg = ints;
 
        if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
index dbbf7ca..20d9026 100644 (file)
@@ -479,6 +479,7 @@ struct ath_hw {
 
        int16_t curchan_rad_index;
        u32 mask_reg;
+       u32 imrs2_reg;
        u32 txok_interrupt_mask;
        u32 txerr_interrupt_mask;
        u32 txdesc_interrupt_mask;
index efc420c..589490b 100644 (file)
@@ -31,8 +31,10 @@ static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah,
        REG_WRITE(ah, AR_IMR_S1,
                  SM(ah->txerr_interrupt_mask, AR_IMR_S1_QCU_TXERR)
                  | SM(ah->txeol_interrupt_mask, AR_IMR_S1_QCU_TXEOL));
-       REG_RMW_FIELD(ah, AR_IMR_S2,
-                     AR_IMR_S2_QCU_TXURN, ah->txurn_interrupt_mask);
+
+       ah->imrs2_reg &= ~AR_IMR_S2_QCU_TXURN;
+       ah->imrs2_reg |= (ah->txurn_interrupt_mask & AR_IMR_S2_QCU_TXURN);
+       REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg);
 }
 
 u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q)