ath5k: Use QUIET mechanism on tx dma stop
authorNick Kossifidis <mick@madwifi.org>
Sun, 28 Sep 2008 22:23:07 +0000 (01:23 +0300)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 30 Sep 2008 18:07:26 +0000 (14:07 -0400)
 * Use QUIET mechanism to drain tx buffer on PCU for newer chips
 * Make sure that INTPEND is really 1 and not 0xffffffff while checking for pending interrupts

Changes-Licensed-under: ISC
Signed-Off-by: Nick Kossifidis <mickflemm@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath5k/dma.c
drivers/net/wireless/ath5k/reg.h

index a28090be96036d44440925288e0516ea0222d71e..7adceb2c7fab50c33d4769b04364ec6655b4ad3e 100644 (file)
@@ -68,7 +68,7 @@ int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah)
        /*
         * It may take some time to disable the DMA receive unit
         */
-       for (i = 2000; i > 0 &&
+       for (i = 1000; i > 0 &&
                        (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0;
                        i--)
                udelay(10);
@@ -182,11 +182,10 @@ int ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue)
  * have any pending frames. Returns -EBUSY if we still have pending frames,
  * -EINVAL if queue number is out of range.
  *
- * TODO: Test queue drain code
  */
 int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
 {
-       unsigned int i = 100;
+       unsigned int i = 40;
        u32 tx_queue, pending;
 
        ATH5K_TRACE(ah->ah_sc);
@@ -233,13 +232,53 @@ int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
                        udelay(100);
                } while (--i && pending);
 
+               /* For 2413+ order PCU to drop packets using
+                * QUIET mechanism */
+               if (ah->ah_mac_version >= (AR5K_SREV_AR2414 >> 4) &&
+               pending){
+                       /* Set periodicity and duration */
+                       ath5k_hw_reg_write(ah,
+                               AR5K_REG_SM(100, AR5K_QUIET_CTL2_QT_PER)|
+                               AR5K_REG_SM(10, AR5K_QUIET_CTL2_QT_DUR),
+                               AR5K_QUIET_CTL2);
+
+                       /* Enable quiet period for current TSF */
+                       ath5k_hw_reg_write(ah,
+                               AR5K_QUIET_CTL1_QT_EN |
+                               AR5K_REG_SM(ath5k_hw_reg_read(ah,
+                                               AR5K_TSF_L32_5211) >> 10,
+                                               AR5K_QUIET_CTL1_NEXT_QT_TSF),
+                               AR5K_QUIET_CTL1);
+
+                       /* Force channel idle high */
+                       AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211,
+                                       AR5K_DIAG_SW_CHANEL_IDLE_HIGH);
+
+                       /* Wait a while and disable mechanism */
+                       udelay(200);
+                       AR5K_REG_DISABLE_BITS(ah, AR5K_QUIET_CTL1,
+                                               AR5K_QUIET_CTL1_QT_EN);
+
+                       /* Re-check for pending frames */
+                       i = 40;
+                       do {
+                               pending = ath5k_hw_reg_read(ah,
+                                       AR5K_QUEUE_STATUS(queue)) &
+                                       AR5K_QCU_STS_FRMPENDCNT;
+                               udelay(100);
+                       } while (--i && pending);
+
+                       AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5211,
+                                       AR5K_DIAG_SW_CHANEL_IDLE_HIGH);
+               }
+
                /* Clear register */
                ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD);
                if (pending)
                        return -EBUSY;
        }
 
-       /* TODO: Check for success else return error */
+       /* TODO: Check for success on 5210 else return error */
        return 0;
 }
 
@@ -415,7 +454,7 @@ done:
 bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah)
 {
        ATH5K_TRACE(ah->ah_sc);
-       return ath5k_hw_reg_read(ah, AR5K_INTPEND);
+       return ath5k_hw_reg_read(ah, AR5K_INTPEND) == 1 ? 1 : 0;
 }
 
 /**
index 55054b575e836c20277777286e2fa5b753fadb6f..e557fe178bbf9401af6c7846118d0a7f46a16c84 100644 (file)
 #define AR5K_DIAG_SW_OBSPT_S           18
 #define AR5K_DIAG_SW_RX_CLEAR_HIGH     0x0010000       /* Force RX Clear high */
 #define AR5K_DIAG_SW_IGNORE_CARR_SENSE 0x0020000       /* Ignore virtual carrier sense */
-#define AR5K_DIAG_SW_CHANEL_IDLE_HIGH  0x0040000       /* Force channel idle high (?) */
+#define AR5K_DIAG_SW_CHANEL_IDLE_HIGH  0x0040000       /* Force channel idle high */
 #define AR5K_DIAG_SW_PHEAR_ME          0x0080000       /* ??? */
 
 /*
  */
 #define AR5K_QUIET_CTL1                        0x80fc                  /* Register Address */
 #define AR5K_QUIET_CTL1_NEXT_QT_TSF    0x0000ffff      /* Next quiet period TSF (TU) */
-#define AR5K_QUIET_CTL1_NEXT_QT_TSF_0
+#define AR5K_QUIET_CTL1_NEXT_QT_TSF_S  0
 #define AR5K_QUIET_CTL1_QT_EN          0x00010000      /* Enable quiet period */
 #define AR5K_QUIET_CTL1_ACK_CTS_EN     0x00020000      /* Send ACK/CTS during quiet period */