Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / net / wireless / ath / ath9k / hw.c
index fa84e37bf091546035c6638130892a5bbe559c66..f84477c5ebb1b91862b653842f980b6607ea830e 100644 (file)
@@ -24,6 +24,8 @@
 #include "rc.h"
 #include "ar9003_mac.h"
 #include "ar9003_mci.h"
+#include "debug.h"
+#include "ath9k.h"
 
 static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type);
 
@@ -83,6 +85,53 @@ static void ath9k_hw_ani_cache_ini_regs(struct ath_hw *ah)
 /* Helper Functions */
 /********************/
 
+#ifdef CONFIG_ATH9K_DEBUGFS
+
+void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause)
+{
+       struct ath_softc *sc = common->priv;
+       if (sync_cause)
+               sc->debug.stats.istats.sync_cause_all++;
+       if (sync_cause & AR_INTR_SYNC_RTC_IRQ)
+               sc->debug.stats.istats.sync_rtc_irq++;
+       if (sync_cause & AR_INTR_SYNC_MAC_IRQ)
+               sc->debug.stats.istats.sync_mac_irq++;
+       if (sync_cause & AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS)
+               sc->debug.stats.istats.eeprom_illegal_access++;
+       if (sync_cause & AR_INTR_SYNC_APB_TIMEOUT)
+               sc->debug.stats.istats.apb_timeout++;
+       if (sync_cause & AR_INTR_SYNC_PCI_MODE_CONFLICT)
+               sc->debug.stats.istats.pci_mode_conflict++;
+       if (sync_cause & AR_INTR_SYNC_HOST1_FATAL)
+               sc->debug.stats.istats.host1_fatal++;
+       if (sync_cause & AR_INTR_SYNC_HOST1_PERR)
+               sc->debug.stats.istats.host1_perr++;
+       if (sync_cause & AR_INTR_SYNC_TRCV_FIFO_PERR)
+               sc->debug.stats.istats.trcv_fifo_perr++;
+       if (sync_cause & AR_INTR_SYNC_RADM_CPL_EP)
+               sc->debug.stats.istats.radm_cpl_ep++;
+       if (sync_cause & AR_INTR_SYNC_RADM_CPL_DLLP_ABORT)
+               sc->debug.stats.istats.radm_cpl_dllp_abort++;
+       if (sync_cause & AR_INTR_SYNC_RADM_CPL_TLP_ABORT)
+               sc->debug.stats.istats.radm_cpl_tlp_abort++;
+       if (sync_cause & AR_INTR_SYNC_RADM_CPL_ECRC_ERR)
+               sc->debug.stats.istats.radm_cpl_ecrc_err++;
+       if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT)
+               sc->debug.stats.istats.radm_cpl_timeout++;
+       if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT)
+               sc->debug.stats.istats.local_timeout++;
+       if (sync_cause & AR_INTR_SYNC_PM_ACCESS)
+               sc->debug.stats.istats.pm_access++;
+       if (sync_cause & AR_INTR_SYNC_MAC_AWAKE)
+               sc->debug.stats.istats.mac_awake++;
+       if (sync_cause & AR_INTR_SYNC_MAC_ASLEEP)
+               sc->debug.stats.istats.mac_asleep++;
+       if (sync_cause & AR_INTR_SYNC_MAC_SLEEP_ACCESS)
+               sc->debug.stats.istats.mac_sleep_access++;
+}
+#endif
+
+
 static void ath9k_hw_set_clockrate(struct ath_hw *ah)
 {
        struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
@@ -142,6 +191,22 @@ bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout)
 }
 EXPORT_SYMBOL(ath9k_hw_wait);
 
+void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan,
+                         int hw_delay)
+{
+       if (IS_CHAN_B(chan))
+               hw_delay = (4 * hw_delay) / 22;
+       else
+               hw_delay /= 10;
+
+       if (IS_CHAN_HALF_RATE(chan))
+               hw_delay *= 2;
+       else if (IS_CHAN_QUARTER_RATE(chan))
+               hw_delay *= 4;
+
+       udelay(hw_delay + BASE_ACTIVATE_DELAY);
+}
+
 void ath9k_hw_write_array(struct ath_hw *ah, struct ar5416IniArray *array,
                          int column, unsigned int *writecnt)
 {
@@ -388,8 +453,8 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
 {
        int i;
 
-       ah->config.dma_beacon_response_time = 2;
-       ah->config.sw_beacon_response_time = 10;
+       ah->config.dma_beacon_response_time = 1;
+       ah->config.sw_beacon_response_time = 6;
        ah->config.additional_swba_backoff = 0;
        ah->config.ack_6mb = 0x0;
        ah->config.cwm_ignore_extcca = 0;
@@ -445,7 +510,6 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah)
                AR_STA_ID1_MCAST_KSRCH;
        if (AR_SREV_9100(ah))
                ah->sta_id1_defaults |= AR_STA_ID1_AR9100_BA_FIX;
-       ah->enable_32kHz_clock = DONT_USE_32KHZ;
        ah->slottime = ATH9K_SLOT_TIME_9;
        ah->globaltxtimeout = (u32) -1;
        ah->power_mode = ATH9K_PM_UNDEFINED;
@@ -972,7 +1036,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
        struct ath_common *common = ath9k_hw_common(ah);
        struct ieee80211_conf *conf = &common->hw->conf;
        const struct ath9k_channel *chan = ah->curchan;
-       int acktimeout, ctstimeout;
+       int acktimeout, ctstimeout, ack_offset = 0;
        int slottime;
        int sifstime;
        int rx_lat = 0, tx_lat = 0, eifs = 0;
@@ -993,6 +1057,11 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
                rx_lat = 37;
        tx_lat = 54;
 
+       if (IS_CHAN_5GHZ(chan))
+               sifstime = 16;
+       else
+               sifstime = 10;
+
        if (IS_CHAN_HALF_RATE(chan)) {
                eifs = 175;
                rx_lat *= 2;
@@ -1000,8 +1069,9 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
                if (IS_CHAN_A_FAST_CLOCK(ah, chan))
                    tx_lat += 11;
 
+               sifstime *= 2;
+               ack_offset = 16;
                slottime = 13;
-               sifstime = 32;
        } else if (IS_CHAN_QUARTER_RATE(chan)) {
                eifs = 340;
                rx_lat = (rx_lat * 4) - 1;
@@ -1009,8 +1079,9 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
                if (IS_CHAN_A_FAST_CLOCK(ah, chan))
                    tx_lat += 22;
 
+               sifstime *= 4;
+               ack_offset = 32;
                slottime = 21;
-               sifstime = 64;
        } else {
                if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) {
                        eifs = AR_D_GBL_IFS_EIFS_ASYNC_FIFO;
@@ -1024,14 +1095,10 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
                tx_lat = MS(reg, AR_USEC_TX_LAT);
 
                slottime = ah->slottime;
-               if (IS_CHAN_5GHZ(chan))
-                       sifstime = 16;
-               else
-                       sifstime = 10;
        }
 
        /* As defined by IEEE 802.11-2007 17.3.8.6 */
-       acktimeout = slottime + sifstime + 3 * ah->coverage_class;
+       acktimeout = slottime + sifstime + 3 * ah->coverage_class + ack_offset;
        ctstimeout = acktimeout;
 
        /*
@@ -1041,7 +1108,8 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
         * BA frames in some implementations, but it has been found to fix ACK
         * timeout issues in other cases as well.
         */
-       if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ) {
+       if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ &&
+           !IS_CHAN_HALF_RATE(chan) && !IS_CHAN_QUARTER_RATE(chan)) {
                acktimeout += 64 - sifstime - ah->slottime;
                ctstimeout += 48 - sifstime - ah->slottime;
        }
@@ -1491,11 +1559,84 @@ static void ath9k_hw_apply_gpio_override(struct ath_hw *ah)
        }
 }
 
+static bool ath9k_hw_check_dcs(u32 dma_dbg, u32 num_dcu_states,
+                              int *hang_state, int *hang_pos)
+{
+       static u32 dcu_chain_state[] = {5, 6, 9}; /* DCU chain stuck states */
+       u32 chain_state, dcs_pos, i;
+
+       for (dcs_pos = 0; dcs_pos < num_dcu_states; dcs_pos++) {
+               chain_state = (dma_dbg >> (5 * dcs_pos)) & 0x1f;
+               for (i = 0; i < 3; i++) {
+                       if (chain_state == dcu_chain_state[i]) {
+                               *hang_state = chain_state;
+                               *hang_pos = dcs_pos;
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+#define DCU_COMPLETE_STATE        1
+#define DCU_COMPLETE_STATE_MASK 0x3
+#define NUM_STATUS_READS         50
+static bool ath9k_hw_detect_mac_hang(struct ath_hw *ah)
+{
+       u32 chain_state, comp_state, dcs_reg = AR_DMADBG_4;
+       u32 i, hang_pos, hang_state, num_state = 6;
+
+       comp_state = REG_READ(ah, AR_DMADBG_6);
+
+       if ((comp_state & DCU_COMPLETE_STATE_MASK) != DCU_COMPLETE_STATE) {
+               ath_dbg(ath9k_hw_common(ah), RESET,
+                       "MAC Hang signature not found at DCU complete\n");
+               return false;
+       }
+
+       chain_state = REG_READ(ah, dcs_reg);
+       if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos))
+               goto hang_check_iter;
+
+       dcs_reg = AR_DMADBG_5;
+       num_state = 4;
+       chain_state = REG_READ(ah, dcs_reg);
+       if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos))
+               goto hang_check_iter;
+
+       ath_dbg(ath9k_hw_common(ah), RESET,
+               "MAC Hang signature 1 not found\n");
+       return false;
+
+hang_check_iter:
+       ath_dbg(ath9k_hw_common(ah), RESET,
+               "DCU registers: chain %08x complete %08x Hang: state %d pos %d\n",
+               chain_state, comp_state, hang_state, hang_pos);
+
+       for (i = 0; i < NUM_STATUS_READS; i++) {
+               chain_state = REG_READ(ah, dcs_reg);
+               chain_state = (chain_state >> (5 * hang_pos)) & 0x1f;
+               comp_state = REG_READ(ah, AR_DMADBG_6);
+
+               if (((comp_state & DCU_COMPLETE_STATE_MASK) !=
+                                       DCU_COMPLETE_STATE) ||
+                   (chain_state != hang_state))
+                       return false;
+       }
+
+       ath_dbg(ath9k_hw_common(ah), RESET, "MAC Hang signature 1 found\n");
+
+       return true;
+}
+
 bool ath9k_hw_check_alive(struct ath_hw *ah)
 {
        int count = 50;
        u32 reg;
 
+       if (AR_SREV_9300(ah))
+               return !ath9k_hw_detect_mac_hang(ah);
+
        if (AR_SREV_9285_12_OR_LATER(ah))
                return true;
 
@@ -1546,6 +1687,10 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
        if (chan->channel == ah->curchan->channel)
                goto fail;
 
+       if ((ah->curchan->channelFlags | chan->channelFlags) &
+           (CHANNEL_HALF | CHANNEL_QUARTER))
+               goto fail;
+
        if ((chan->channelFlags & CHANNEL_ALL) !=
            (ah->curchan->channelFlags & CHANNEL_ALL))
                goto fail;