ath9k_hw: add support for the AR9003 baseband watchdog
authorLuis R. Rodriguez <lrodriguez@atheros.com>
Thu, 13 May 2010 17:33:43 +0000 (13:33 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 2 Jun 2010 20:13:00 +0000 (16:13 -0400)
The baseband watchdog will monitor blocks of the baseband
through timers and will issue an interrupt when things are
detected to be stalled. It is only available on the AR9003
family.

Cc: Sam Ng <sam.ng@atheros.com>
Cc: Paul Shaw <paul.shaw@atheros.com>
Cc: Don Breslin <don.breslin@atheros.com>
Cc: Cliff Holden <cliff.holden@atheros.com
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ar9003_mac.c
drivers/net/wireless/ath/ath9k/ar9003_mac.h
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/reg.h

index 37ba374..4073107 100644 (file)
@@ -90,6 +90,8 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
                                  MAP_ISR_S2_CST);
                        mask2 |= ((isr2 & AR_ISR_S2_TSFOOR) >>
                                  MAP_ISR_S2_TSFOOR);
+                       mask2 |= ((isr2 & AR_ISR_S2_BB_WATCHDOG) >>
+                                 MAP_ISR_S2_BB_WATCHDOG);
 
                        if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) {
                                REG_WRITE(ah, AR_ISR_S2, isr2);
@@ -167,6 +169,9 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
 
                        (void) REG_READ(ah, AR_ISR);
                }
+
+               if (*masked & ATH9K_INT_BB_WATCHDOG)
+                       ar9003_hw_bb_watchdog_read(ah);
        }
 
        if (sync_cause) {
index f17558b..5a7a286 100644 (file)
@@ -47,6 +47,7 @@
 #define MAP_ISR_S2_DTIMSYNC     7
 #define MAP_ISR_S2_DTIM         7
 #define MAP_ISR_S2_TSFOOR       4
+#define MAP_ISR_S2_BB_WATCHDOG  6
 
 #define AR9003TXC_CONST(_ds) ((const struct ar9003_txc *) _ds)
 
index 80431a2..c714579 100644 (file)
@@ -1132,3 +1132,122 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
        priv_ops->do_getnf = ar9003_hw_do_getnf;
        priv_ops->loadnf = ar9003_hw_loadnf;
 }
+
+void ar9003_hw_bb_watchdog_config(struct ath_hw *ah)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+       u32 idle_tmo_ms = ah->bb_watchdog_timeout_ms;
+       u32 val, idle_count;
+
+       if (!idle_tmo_ms) {
+               /* disable IRQ, disable chip-reset for BB panic */
+               REG_WRITE(ah, AR_PHY_WATCHDOG_CTL_2,
+                         REG_READ(ah, AR_PHY_WATCHDOG_CTL_2) &
+                         ~(AR_PHY_WATCHDOG_RST_ENABLE |
+                           AR_PHY_WATCHDOG_IRQ_ENABLE));
+
+               /* disable watchdog in non-IDLE mode, disable in IDLE mode */
+               REG_WRITE(ah, AR_PHY_WATCHDOG_CTL_1,
+                         REG_READ(ah, AR_PHY_WATCHDOG_CTL_1) &
+                         ~(AR_PHY_WATCHDOG_NON_IDLE_ENABLE |
+                           AR_PHY_WATCHDOG_IDLE_ENABLE));
+
+               ath_print(common, ATH_DBG_RESET, "Disabled BB Watchdog\n");
+               return;
+       }
+
+       /* enable IRQ, disable chip-reset for BB watchdog */
+       val = REG_READ(ah, AR_PHY_WATCHDOG_CTL_2) & AR_PHY_WATCHDOG_CNTL2_MASK;
+       REG_WRITE(ah, AR_PHY_WATCHDOG_CTL_2,
+                 (val | AR_PHY_WATCHDOG_IRQ_ENABLE) &
+                 ~AR_PHY_WATCHDOG_RST_ENABLE);
+
+       /* bound limit to 10 secs */
+       if (idle_tmo_ms > 10000)
+               idle_tmo_ms = 10000;
+
+       /*
+        * The time unit for watchdog event is 2^15 44/88MHz cycles.
+        *
+        * For HT20 we have a time unit of 2^15/44 MHz = .74 ms per tick
+        * For HT40 we have a time unit of 2^15/88 MHz = .37 ms per tick
+        *
+        * Given we use fast clock now in 5 GHz, these time units should
+        * be common for both 2 GHz and 5 GHz.
+        */
+       idle_count = (100 * idle_tmo_ms) / 74;
+       if (ah->curchan && IS_CHAN_HT40(ah->curchan))
+               idle_count = (100 * idle_tmo_ms) / 37;
+
+       /*
+        * enable watchdog in non-IDLE mode, disable in IDLE mode,
+        * set idle time-out.
+        */
+       REG_WRITE(ah, AR_PHY_WATCHDOG_CTL_1,
+                 AR_PHY_WATCHDOG_NON_IDLE_ENABLE |
+                 AR_PHY_WATCHDOG_IDLE_MASK |
+                 (AR_PHY_WATCHDOG_NON_IDLE_MASK & (idle_count << 2)));
+
+       ath_print(common, ATH_DBG_RESET,
+                 "Enabled BB Watchdog timeout (%u ms)\n",
+                 idle_tmo_ms);
+}
+
+void ar9003_hw_bb_watchdog_read(struct ath_hw *ah)
+{
+       /*
+        * we want to avoid printing in ISR context so we save the
+        * watchdog status to be printed later in bottom half context.
+        */
+       ah->bb_watchdog_last_status = REG_READ(ah, AR_PHY_WATCHDOG_STATUS);
+
+       /*
+        * the watchdog timer should reset on status read but to be sure
+        * sure we write 0 to the watchdog status bit.
+        */
+       REG_WRITE(ah, AR_PHY_WATCHDOG_STATUS,
+                 ah->bb_watchdog_last_status & ~AR_PHY_WATCHDOG_STATUS_CLR);
+}
+
+void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+       u32 rxc_pcnt = 0, rxf_pcnt = 0, txf_pcnt = 0, status;
+
+       if (likely(!(common->debug_mask & ATH_DBG_RESET)))
+               return;
+
+       status = ah->bb_watchdog_last_status;
+       ath_print(common, ATH_DBG_RESET,
+                 "\n==== BB update: BB status=0x%08x ====\n", status);
+       ath_print(common, ATH_DBG_RESET,
+                 "** BB state: wd=%u det=%u rdar=%u rOFDM=%d "
+                 "rCCK=%u tOFDM=%u tCCK=%u agc=%u src=%u **\n",
+                 MS(status, AR_PHY_WATCHDOG_INFO),
+                 MS(status, AR_PHY_WATCHDOG_DET_HANG),
+                 MS(status, AR_PHY_WATCHDOG_RADAR_SM),
+                 MS(status, AR_PHY_WATCHDOG_RX_OFDM_SM),
+                 MS(status, AR_PHY_WATCHDOG_RX_CCK_SM),
+                 MS(status, AR_PHY_WATCHDOG_TX_OFDM_SM),
+                 MS(status, AR_PHY_WATCHDOG_TX_CCK_SM),
+                 MS(status, AR_PHY_WATCHDOG_AGC_SM),
+                 MS(status,AR_PHY_WATCHDOG_SRCH_SM));
+
+       ath_print(common, ATH_DBG_RESET,
+                 "** BB WD cntl: cntl1=0x%08x cntl2=0x%08x **\n",
+                 REG_READ(ah, AR_PHY_WATCHDOG_CTL_1),
+                 REG_READ(ah, AR_PHY_WATCHDOG_CTL_2));
+       ath_print(common, ATH_DBG_RESET,
+                 "** BB mode: BB_gen_controls=0x%08x **\n",
+                 REG_READ(ah, AR_PHY_GEN_CTRL));
+
+       if (ath9k_hw_GetMibCycleCountsPct(ah, &rxc_pcnt, &rxf_pcnt, &txf_pcnt))
+               ath_print(common, ATH_DBG_RESET,
+                         "** BB busy times: rx_clear=%d%%, "
+                         "rx_frame=%d%%, tx_frame=%d%% **\n",
+                         rxc_pcnt, rxf_pcnt, txf_pcnt);
+
+       ath_print(common, ATH_DBG_RESET,
+                 "==== BB update: done ====\n\n");
+}
+EXPORT_SYMBOL(ar9003_hw_bb_watchdog_dbg_info);
index f08cc8b..676d3f1 100644 (file)
 #define AR_PHY_TX_IQCAL_STATUS_B0   (AR_SM_BASE + 0x48c)
 #define AR_PHY_TX_IQCAL_CORR_COEFF_01_B0    (AR_SM_BASE + 0x450)
 
-#define AR_PHY_PANIC_WD_STATUS      (AR_SM_BASE + 0x5c0)
-#define AR_PHY_PANIC_WD_CTL_1       (AR_SM_BASE + 0x5c4)
-#define AR_PHY_PANIC_WD_CTL_2       (AR_SM_BASE + 0x5c8)
-#define AR_PHY_BT_CTL               (AR_SM_BASE + 0x5cc)
+#define AR_PHY_WATCHDOG_STATUS      (AR_SM_BASE + 0x5c0)
+#define AR_PHY_WATCHDOG_CTL_1       (AR_SM_BASE + 0x5c4)
+#define AR_PHY_WATCHDOG_CTL_2       (AR_SM_BASE + 0x5c8)
+#define AR_PHY_WATCHDOG_CTL         (AR_SM_BASE + 0x5cc)
 #define AR_PHY_ONLY_WARMRESET       (AR_SM_BASE + 0x5d0)
 #define AR_PHY_ONLY_CTL             (AR_SM_BASE + 0x5d4)
 #define AR_PHY_ECO_CTRL             (AR_SM_BASE + 0x5dc)
 #define AR_PHY_CAL_MEAS_2_9300_10(_i) (AR_PHY_IQ_ADC_MEAS_2_B0_9300_10 + (AR_PHY_CHAIN_OFFSET * (_i)))
 #define AR_PHY_CAL_MEAS_3_9300_10(_i) (AR_PHY_IQ_ADC_MEAS_3_B0_9300_10 + (AR_PHY_CHAIN_OFFSET * (_i)))
 
-#define AR_PHY_BB_PANIC_NON_IDLE_ENABLE 0x00000001
-#define AR_PHY_BB_PANIC_IDLE_ENABLE     0x00000002
-#define AR_PHY_BB_PANIC_IDLE_MASK       0xFFFF0000
-#define AR_PHY_BB_PANIC_NON_IDLE_MASK   0x0000FFFC
-
-#define AR_PHY_BB_PANIC_RST_ENABLE      0x00000002
-#define AR_PHY_BB_PANIC_IRQ_ENABLE      0x00000004
-#define AR_PHY_BB_PANIC_CNTL2_MASK      0xFFFFFFF9
-
-#define AR_PHY_BB_WD_STATUS             0x00000007
-#define AR_PHY_BB_WD_STATUS_S           0
-#define AR_PHY_BB_WD_DET_HANG           0x00000008
-#define AR_PHY_BB_WD_DET_HANG_S         3
-#define AR_PHY_BB_WD_RADAR_SM           0x000000F0
-#define AR_PHY_BB_WD_RADAR_SM_S         4
-#define AR_PHY_BB_WD_RX_OFDM_SM         0x00000F00
-#define AR_PHY_BB_WD_RX_OFDM_SM_S       8
-#define AR_PHY_BB_WD_RX_CCK_SM          0x0000F000
-#define AR_PHY_BB_WD_RX_CCK_SM_S        12
-#define AR_PHY_BB_WD_TX_OFDM_SM         0x000F0000
-#define AR_PHY_BB_WD_TX_OFDM_SM_S       16
-#define AR_PHY_BB_WD_TX_CCK_SM          0x00F00000
-#define AR_PHY_BB_WD_TX_CCK_SM_S        20
-#define AR_PHY_BB_WD_AGC_SM             0x0F000000
-#define AR_PHY_BB_WD_AGC_SM_S           24
-#define AR_PHY_BB_WD_SRCH_SM            0xF0000000
-#define AR_PHY_BB_WD_SRCH_SM_S          28
-
-#define AR_PHY_BB_WD_STATUS_CLR         0x00000008
+#define AR_PHY_WATCHDOG_NON_IDLE_ENABLE    0x00000001
+#define AR_PHY_WATCHDOG_IDLE_ENABLE        0x00000002
+#define AR_PHY_WATCHDOG_IDLE_MASK          0xFFFF0000
+#define AR_PHY_WATCHDOG_NON_IDLE_MASK      0x0000FFFC
+
+#define AR_PHY_WATCHDOG_RST_ENABLE         0x00000002
+#define AR_PHY_WATCHDOG_IRQ_ENABLE         0x00000004
+#define AR_PHY_WATCHDOG_CNTL2_MASK         0xFFFFFFF9
+
+#define AR_PHY_WATCHDOG_INFO               0x00000007
+#define AR_PHY_WATCHDOG_INFO_S             0
+#define AR_PHY_WATCHDOG_DET_HANG           0x00000008
+#define AR_PHY_WATCHDOG_DET_HANG_S         3
+#define AR_PHY_WATCHDOG_RADAR_SM           0x000000F0
+#define AR_PHY_WATCHDOG_RADAR_SM_S         4
+#define AR_PHY_WATCHDOG_RX_OFDM_SM         0x00000F00
+#define AR_PHY_WATCHDOG_RX_OFDM_SM_S       8
+#define AR_PHY_WATCHDOG_RX_CCK_SM          0x0000F000
+#define AR_PHY_WATCHDOG_RX_CCK_SM_S        12
+#define AR_PHY_WATCHDOG_TX_OFDM_SM         0x000F0000
+#define AR_PHY_WATCHDOG_TX_OFDM_SM_S       16
+#define AR_PHY_WATCHDOG_TX_CCK_SM          0x00F00000
+#define AR_PHY_WATCHDOG_TX_CCK_SM_S        20
+#define AR_PHY_WATCHDOG_AGC_SM             0x0F000000
+#define AR_PHY_WATCHDOG_AGC_SM_S           24
+#define AR_PHY_WATCHDOG_SRCH_SM            0xF0000000
+#define AR_PHY_WATCHDOG_SRCH_SM_S          28
+
+#define AR_PHY_WATCHDOG_STATUS_CLR         0x00000008
 
 void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx);
 
index c33f17d..6bfac1c 100644 (file)
@@ -627,6 +627,7 @@ static int __ath9k_hw_init(struct ath_hw *ah)
                ar9003_hw_set_nf_limits(ah);
 
        ath9k_init_nfcal_hist_buffer(ah);
+       ah->bb_watchdog_timeout_ms = 25;
 
        common->state = ATH_HW_INITIALIZED;
 
@@ -1441,6 +1442,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        if (AR_SREV_9300_20_OR_LATER(ah)) {
                ath9k_hw_loadnf(ah, curchan);
                ath9k_hw_start_nfcal(ah);
+               ar9003_hw_bb_watchdog_config(ah);
        }
 
        return 0;
index 77245df..bfecde0 100644 (file)
@@ -279,6 +279,7 @@ enum ath9k_int {
        ATH9K_INT_TX = 0x00000040,
        ATH9K_INT_TXDESC = 0x00000080,
        ATH9K_INT_TIM_TIMER = 0x00000100,
+       ATH9K_INT_BB_WATCHDOG = 0x00000400,
        ATH9K_INT_TXURN = 0x00000800,
        ATH9K_INT_MIB = 0x00001000,
        ATH9K_INT_RXPHY = 0x00004000,
@@ -789,6 +790,9 @@ struct ath_hw {
        u32 ts_paddr_end;
        u16 ts_tail;
        u8 ts_size;
+
+       u32 bb_watchdog_last_status;
+       u32 bb_watchdog_timeout_ms; /* in ms, 0 to disable */
 };
 
 static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah)
@@ -910,10 +914,13 @@ void ar9002_hw_enable_async_fifo(struct ath_hw *ah);
 void ar9002_hw_enable_wep_aggregation(struct ath_hw *ah);
 
 /*
- * Code specifric to AR9003, we stuff these here to avoid callbacks
+ * Code specific to AR9003, we stuff these here to avoid callbacks
  * for older families
  */
 void ar9003_hw_set_nf_limits(struct ath_hw *ah);
+void ar9003_hw_bb_watchdog_config(struct ath_hw *ah);
+void ar9003_hw_bb_watchdog_read(struct ath_hw *ah);
+void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah);
 
 /* Hardware family op attach helpers */
 void ar5008_hw_attach_phy_ops(struct ath_hw *ah);
index d4371a4..c9a009f 100644 (file)
 
 #define AR_ISR_S2              0x008c
 #define AR_ISR_S2_QCU_TXURN    0x000003FF
+#define AR_ISR_S2_BB_WATCHDOG  0x00010000
 #define AR_ISR_S2_CST          0x00400000
 #define AR_ISR_S2_GTT          0x00800000
 #define AR_ISR_S2_TIM          0x01000000