ath9k: Add Bluetooth Coexistence 3-wire support
authorVasanthakumar Thiagarajan <vasanth@atheros.com>
Wed, 26 Aug 2009 15:38:50 +0000 (21:08 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 28 Aug 2009 18:40:51 +0000 (14:40 -0400)
This patch adds 3-wire bluetooth coex support for AR9285.
This support can be enabled through btcoex_enable modparam.

Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/btcoex.c
drivers/net/wireless/ath/ath9k/btcoex.h
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/reg.h
drivers/net/wireless/ath/ath9k/xmit.c

index 83f2c8f..1c68a9d 100644 (file)
@@ -523,6 +523,7 @@ struct ath_led {
 #define SC_OP_WAIT_FOR_TX_ACK   BIT(18)
 #define SC_OP_BEACON_SYNC       BIT(19)
 #define SC_OP_BTCOEX_ENABLED    BIT(20)
+#define SC_OP_BT_PRIORITY_DETECTED BIT(21)
 
 struct ath_bus_ops {
        void            (*read_cachesize)(struct ath_softc *sc, int *csz);
@@ -708,4 +709,5 @@ bool ath9k_all_wiphys_idle(struct ath_softc *sc);
 void ath9k_iowrite32(struct ath_hw *ah, u32 reg_offset, u32 val);
 unsigned int ath9k_ioread32(struct ath_hw *ah, u32 reg_offset);
 
+int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype);
 #endif /* ATH9K_H */
index 9f19cd1..8fb3567 100644 (file)
 
 #include "ath9k.h"
 
-void ath9k_hw_btcoex_init(struct ath_hw *ah)
+static const struct ath_btcoex_config ath_bt_config = { 0, true, true,
+                       ATH_BT_COEX_MODE_SLOTTED, true, true, 2, 5, true };
+
+
+/*
+ * Detects if there is any priority bt traffic
+ */
+static void ath_detect_bt_priority(struct ath_softc *sc)
+{
+       struct ath_btcoex_info *btinfo = &sc->btcoex_info;
+
+       if (ath9k_hw_gpio_get(sc->sc_ah, btinfo->btpriority_gpio))
+               btinfo->bt_priority_cnt++;
+
+       if (time_after(jiffies, btinfo->bt_priority_time +
+                       msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
+               if (btinfo->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
+                       DPRINTF(sc, ATH_DBG_BTCOEX,
+                               "BT priority traffic detected");
+                       sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED;
+               } else {
+                       sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED;
+               }
+
+               btinfo->bt_priority_cnt = 0;
+               btinfo->bt_priority_time = jiffies;
+       }
+}
+
+/*
+ * Configures appropriate weight based on stomp type.
+ */
+static void ath_btcoex_bt_stomp(struct ath_softc *sc,
+                               struct ath_btcoex_info *btinfo,
+                               int stomp_type)
+{
+
+       switch (stomp_type) {
+       case ATH_BTCOEX_STOMP_ALL:
+               ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT,
+                                     AR_STOMP_ALL_WLAN_WGHT);
+               break;
+       case ATH_BTCOEX_STOMP_LOW:
+               ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT,
+                                     AR_STOMP_LOW_WLAN_WGHT);
+               break;
+       case ATH_BTCOEX_STOMP_NONE:
+               ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT,
+                                     AR_STOMP_NONE_WLAN_WGHT);
+               break;
+       default:
+               DPRINTF(sc, ATH_DBG_BTCOEX, "Invalid Stomptype\n");
+               break;
+       }
+
+       ath9k_hw_btcoex_enable(sc->sc_ah);
+}
+
+/*
+ * This is the master bt coex timer which runs for every
+ * 45ms, bt traffic will be given priority during 55% of this
+ * period while wlan gets remaining 45%
+ */
+
+static void ath_btcoex_period_timer(unsigned long data)
+{
+       struct ath_softc *sc = (struct ath_softc *) data;
+       struct ath_btcoex_info *btinfo = &sc->btcoex_info;
+       unsigned long flags;
+
+       ath_detect_bt_priority(sc);
+
+       spin_lock_irqsave(&btinfo->btcoex_lock, flags);
+
+       ath_btcoex_bt_stomp(sc, btinfo, btinfo->bt_stomp_type);
+
+       spin_unlock_irqrestore(&btinfo->btcoex_lock, flags);
+
+       if (btinfo->btcoex_period != btinfo->btcoex_no_stomp) {
+               if (btinfo->hw_timer_enabled)
+                       ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer);
+
+               ath_gen_timer_start(sc->sc_ah,
+                       btinfo->no_stomp_timer,
+                       (ath9k_hw_gettsf32(sc->sc_ah) +
+                               btinfo->btcoex_no_stomp),
+                               btinfo->btcoex_no_stomp * 10);
+               btinfo->hw_timer_enabled = true;
+       }
+
+       mod_timer(&btinfo->period_timer, jiffies +
+                                 msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD));
+}
+
+/*
+ * Generic tsf based hw timer which configures weight
+ * registers to time slice between wlan and bt traffic
+ */
+
+static void ath_btcoex_no_stomp_timer(void *arg)
+{
+       struct ath_softc *sc = (struct ath_softc *)arg;
+       struct ath_btcoex_info *btinfo = &sc->btcoex_info;
+       unsigned long flags;
+
+       DPRINTF(sc, ATH_DBG_BTCOEX, "no stomp timer running \n");
+
+       spin_lock_irqsave(&btinfo->btcoex_lock, flags);
+
+       if (btinfo->bt_stomp_type == ATH_BTCOEX_STOMP_LOW)
+               ath_btcoex_bt_stomp(sc, btinfo, ATH_BTCOEX_STOMP_NONE);
+        else if (btinfo->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
+               ath_btcoex_bt_stomp(sc, btinfo, ATH_BTCOEX_STOMP_LOW);
+
+       spin_unlock_irqrestore(&btinfo->btcoex_lock, flags);
+}
+
+static int ath_init_btcoex_info(struct ath_hw *hw,
+                               struct ath_btcoex_info *btcoex_info)
+{
+       u32 i;
+       int qnum;
+
+       qnum = ath_tx_get_qnum(hw->ah_sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE);
+
+       btcoex_info->bt_coex_mode =
+               (btcoex_info->bt_coex_mode & AR_BT_QCU_THRESH) |
+               SM(ath_bt_config.bt_time_extend, AR_BT_TIME_EXTEND) |
+               SM(ath_bt_config.bt_txstate_extend, AR_BT_TXSTATE_EXTEND) |
+               SM(ath_bt_config.bt_txframe_extend, AR_BT_TX_FRAME_EXTEND) |
+               SM(ath_bt_config.bt_mode, AR_BT_MODE) |
+               SM(ath_bt_config.bt_quiet_collision, AR_BT_QUIET) |
+               SM(ath_bt_config.bt_rxclear_polarity, AR_BT_RX_CLEAR_POLARITY) |
+               SM(ath_bt_config.bt_priority_time, AR_BT_PRIORITY_TIME) |
+               SM(ath_bt_config.bt_first_slot_time, AR_BT_FIRST_SLOT_TIME) |
+               SM(qnum, AR_BT_QCU_THRESH);
+
+       btcoex_info->bt_coex_mode2 =
+               SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) |
+               SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) |
+               AR_BT_DISABLE_BT_ANT;
+
+       btcoex_info->bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
+
+       btcoex_info->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000;
+
+       btcoex_info->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
+               btcoex_info->btcoex_period / 100;
+
+       for (i = 0; i < 32; i++)
+               hw->hw_gen_timers.gen_timer_index[(debruijn32 << i) >> 27] = i;
+
+       setup_timer(&btcoex_info->period_timer, ath_btcoex_period_timer,
+                       (unsigned long) hw->ah_sc);
+
+       btcoex_info->no_stomp_timer = ath_gen_timer_alloc(hw,
+                       ath_btcoex_no_stomp_timer,
+                       ath_btcoex_no_stomp_timer,
+                       (void *)hw->ah_sc, AR_FIRST_NDP_TIMER);
+
+       if (btcoex_info->no_stomp_timer == NULL)
+               return -ENOMEM;
+
+       spin_lock_init(&btcoex_info->btcoex_lock);
+
+       return 0;
+}
+
+int ath9k_hw_btcoex_init(struct ath_hw *ah)
 {
        struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info;
+       int ret = 0;
+
+       if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) {
+               /* connect bt_active to baseband */
+               REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL,
+                               (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF |
+                                AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF));
+
+               REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
+                               AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB);
+
+               /* Set input mux for bt_active to gpio pin */
+               REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
+                               AR_GPIO_INPUT_MUX1_BT_ACTIVE,
+                               btcoex_info->btactive_gpio);
 
-       /* connect bt_active to baseband */
-       REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL,
-                       (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF |
-                        AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF));
+               /* Configure the desired gpio port for input */
+               ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio);
+       } else {
+               /* btcoex 3-wire */
+               REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
+                               (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB |
+                                AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB));
 
-       REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
-                       AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB);
+               /* Set input mux for bt_prority_async and
+                *                  bt_active_async to GPIO pins */
+               REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
+                               AR_GPIO_INPUT_MUX1_BT_ACTIVE,
+                               btcoex_info->btactive_gpio);
 
-       /* Set input mux for bt_active to gpio pin */
-       REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
-                       AR_GPIO_INPUT_MUX1_BT_ACTIVE,
-                       btcoex_info->btactive_gpio);
+               REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
+                               AR_GPIO_INPUT_MUX1_BT_PRIORITY,
+                               btcoex_info->btpriority_gpio);
 
-       /* Configure the desired gpio port for input */
-       ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio);
+               /* Configure the desired GPIO ports for input */
+
+               ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio);
+               ath9k_hw_cfg_gpio_input(ah, btcoex_info->btpriority_gpio);
+
+               ret = ath_init_btcoex_info(ah, btcoex_info);
+       }
+
+       return ret;
 }
 
 void ath9k_hw_btcoex_enable(struct ath_hw *ah)
 {
        struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info;
 
-       /* Configure the desired GPIO port for TX_FRAME output */
-       ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio,
-                           AR_GPIO_OUTPUT_MUX_AS_TX_FRAME);
+       if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) {
+               /* Configure the desired GPIO port for TX_FRAME output */
+               ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio,
+                               AR_GPIO_OUTPUT_MUX_AS_TX_FRAME);
+       } else {
+               /*
+                * Program coex mode and weight registers to
+                * enable coex 3-wire
+                */
+               REG_WRITE(ah, AR_BT_COEX_MODE, btcoex_info->bt_coex_mode);
+               REG_WRITE(ah, AR_BT_COEX_WEIGHT, btcoex_info->bt_coex_weights);
+               REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex_info->bt_coex_mode2);
+
+               REG_RMW_FIELD(ah, AR_QUIET1,
+                               AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1);
+               REG_RMW_FIELD(ah, AR_PCU_MISC,
+                               AR_PCU_BT_ANT_PREVENT_RX, 0);
+
+               ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio,
+                               AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL);
+       }
+
+       REG_RMW(ah, AR_GPIO_PDPU,
+               (0x2 << (btcoex_info->btactive_gpio * 2)),
+               (0x3 << (btcoex_info->btactive_gpio * 2)));
 
        ah->ah_sc->sc_flags |= SC_OP_BTCOEX_ENABLED;
 }
@@ -57,5 +274,46 @@ void ath9k_hw_btcoex_disable(struct ath_hw *ah)
        ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio,
                        AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
 
+       if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) {
+               REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE);
+               REG_WRITE(ah, AR_BT_COEX_WEIGHT, 0);
+               REG_WRITE(ah, AR_BT_COEX_MODE2, 0);
+       }
+
        ah->ah_sc->sc_flags &= ~SC_OP_BTCOEX_ENABLED;
 }
+
+/*
+ * Pause btcoex timer and bt duty cycle timer
+ */
+void ath_btcoex_timer_pause(struct ath_softc *sc,
+                           struct ath_btcoex_info *btinfo)
+{
+
+       del_timer_sync(&btinfo->period_timer);
+
+       if (btinfo->hw_timer_enabled)
+               ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer);
+
+       btinfo->hw_timer_enabled = false;
+}
+
+/*
+ * (Re)start btcoex timers
+ */
+void ath_btcoex_timer_resume(struct ath_softc *sc,
+                            struct ath_btcoex_info *btinfo)
+{
+
+       DPRINTF(sc, ATH_DBG_BTCOEX, "Starting btcoex timers");
+
+       /* make sure duty cycle timer is also stopped when resuming */
+       if (btinfo->hw_timer_enabled)
+               ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer);
+
+       btinfo->bt_priority_cnt = 0;
+       btinfo->bt_priority_time = jiffies;
+       sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED;
+
+       mod_timer(&btinfo->period_timer, jiffies);
+}
index cdfa80d..4556819 100644 (file)
 #define ATH_WLANACTIVE_GPIO    5
 #define ATH_BTACTIVE_GPIO      6
 
+#define ATH_BTCOEX_DEF_BT_PERIOD  45
+#define ATH_BTCOEX_DEF_DUTY_CYCLE 55
+#define ATH_BTCOEX_BMISS_THRESH   50
+
+#define ATH_BT_PRIORITY_TIME_THRESHOLD 1000 /* ms */
+#define ATH_BT_CNT_THRESHOLD          3
+
 enum ath_btcoex_scheme {
        ATH_BTCOEX_CFG_NONE,
        ATH_BTCOEX_CFG_2WIRE,
        ATH_BTCOEX_CFG_3WIRE,
 };
 
+enum ath_stomp_type {
+       ATH_BTCOEX_NO_STOMP,
+       ATH_BTCOEX_STOMP_ALL,
+       ATH_BTCOEX_STOMP_LOW,
+       ATH_BTCOEX_STOMP_NONE
+};
+
+enum ath_bt_mode {
+       ATH_BT_COEX_MODE_LEGACY,        /* legacy rx_clear mode */
+       ATH_BT_COEX_MODE_UNSLOTTED,     /* untimed/unslotted mode */
+       ATH_BT_COEX_MODE_SLOTTED,       /* slotted mode */
+       ATH_BT_COEX_MODE_DISALBED,      /* coexistence disabled */
+};
+
+struct ath_btcoex_config {
+       u8 bt_time_extend;
+       bool bt_txstate_extend;
+       bool bt_txframe_extend;
+       enum ath_bt_mode bt_mode; /* coexistence mode */
+       bool bt_quiet_collision;
+       bool bt_rxclear_polarity; /* invert rx_clear as WLAN_ACTIVE*/
+       u8 bt_priority_time;
+       u8 bt_first_slot_time;
+       bool bt_hold_rx_clear;
+};
+
 struct ath_btcoex_info {
        enum ath_btcoex_scheme btcoex_scheme;
        u8 wlanactive_gpio;
        u8 btactive_gpio;
+       u8 btpriority_gpio;
+       u8 bt_duty_cycle;       /* BT duty cycle in percentage */
+       int bt_stomp_type;      /* Types of BT stomping */
+       u32 bt_coex_mode;       /* Register setting for AR_BT_COEX_MODE */
+       u32 bt_coex_weights;    /* Register setting for AR_BT_COEX_WEIGHT */
+       u32 bt_coex_mode2;      /* Register setting for AR_BT_COEX_MODE2 */
+       u32 btcoex_no_stomp;   /* in usec */
+       u32 btcoex_period;      /* in usec */
+       u32 bt_priority_cnt;
+       unsigned long bt_priority_time;
+       bool hw_timer_enabled;
+       spinlock_t btcoex_lock;
+       struct timer_list period_timer;      /* Timer for BT period */
+       struct ath_gen_timer *no_stomp_timer; /*Timer for no BT stomping*/
 };
 
-void ath9k_hw_btcoex_init(struct ath_hw *ah);
+int ath9k_hw_btcoex_init(struct ath_hw *ah);
 void ath9k_hw_btcoex_enable(struct ath_hw *ah);
 void ath9k_hw_btcoex_disable(struct ath_hw *ah);
+void ath_btcoex_timer_resume(struct ath_softc *sc,
+                            struct ath_btcoex_info *btinfo);
+void ath_btcoex_timer_pause(struct ath_softc *sc,
+                           struct ath_btcoex_info *btinfo);
+
+static inline void ath_btcoex_set_weight(struct ath_btcoex_info *btcoex_info,
+                                        u32 bt_weight,
+                                        u32 wlan_weight)
+{
+       btcoex_info->bt_coex_weights = SM(bt_weight, AR_BTCOEX_BT_WGHT) |
+                                      SM(wlan_weight, AR_BTCOEX_WL_WGHT);
+}
 
 #endif
index ea0dd1e..7241f47 100644 (file)
@@ -31,6 +31,7 @@ enum ATH_DEBUG {
        ATH_DBG_FATAL           = 0x00000400,
        ATH_DBG_PS              = 0x00000800,
        ATH_DBG_HWTIMER         = 0x00001000,
+       ATH_DBG_BTCOEX          = 0x00002000,
        ATH_DBG_ANY             = 0xffffffff
 };
 
index 3afd7a9..e340dac 100644 (file)
@@ -4141,7 +4141,7 @@ static u32 rightmost_index(struct ath_gen_timer_table *timer_table, u32 *mask)
        return timer_table->gen_timer_index[b];
 }
 
-static u32 ath9k_hw_gettsf32(struct ath_hw *ah)
+u32 ath9k_hw_gettsf32(struct ath_hw *ah)
 {
        return REG_READ(ah, AR_TSF_L32);
 }
index 052a9c4..5ca6ffa 100644 (file)
@@ -79,6 +79,7 @@
 #define AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1
 #define AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED     2
 #define AR_GPIO_OUTPUT_MUX_AS_TX_FRAME           3
+#define AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL  4
 #define AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED    5
 #define AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED      6
 
@@ -662,5 +663,6 @@ void ath_gen_timer_start(struct ath_hw *ah, struct ath_gen_timer *timer,
 void ath_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer);
 void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer);
 void ath_gen_timer_isr(struct ath_hw *hw);
+u32 ath9k_hw_gettsf32(struct ath_hw *ah);
 
 #endif
index 215c672..4fae699 100644 (file)
@@ -602,6 +602,10 @@ irqreturn_t ath_isr(int irq, void *dev)
                        sc->sc_flags |= SC_OP_WAIT_FOR_BEACON;
                }
 
+       if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE)
+               if (status & ATH9K_INT_GENTIMER)
+                       ath_gen_timer_isr(ah);
+
 chip_reset:
 
        ath_debug_stat_interrupt(sc, status);
@@ -1279,6 +1283,10 @@ void ath_detach(struct ath_softc *sc)
                if (ATH_TXQ_SETUP(sc, i))
                        ath_tx_cleanupq(sc, &sc->tx.txq[i]);
 
+       if ((sc->btcoex_info.no_stomp_timer) &&
+           sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE)
+               ath_gen_timer_free(sc->sc_ah, sc->btcoex_info.no_stomp_timer);
+
        ath9k_hw_detach(sc->sc_ah);
        sc->sc_ah = NULL;
        ath9k_exit_debug(sc);
@@ -1509,8 +1517,11 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc)
                        ARRAY_SIZE(ath9k_5ghz_chantable);
        }
 
-       if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_2WIRE)
-               ath9k_hw_btcoex_init(ah);
+       if (sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE) {
+               r = ath9k_hw_btcoex_init(ah);
+               if (r)
+                       goto bad2;
+       }
 
        return 0;
 bad2:
@@ -1992,10 +2003,16 @@ static int ath9k_start(struct ieee80211_hw *hw)
 
        ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
 
-       if ((sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) &&
-           !(sc->sc_flags & SC_OP_BTCOEX_ENABLED))
+       if ((sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE) &&
+           !(sc->sc_flags & SC_OP_BTCOEX_ENABLED)) {
+               ath_btcoex_set_weight(&sc->btcoex_info, AR_BT_COEX_WGHT,
+                                     AR_STOMP_LOW_WLAN_WGHT);
                ath9k_hw_btcoex_enable(sc->sc_ah);
 
+               if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE)
+                       ath_btcoex_timer_resume(sc, &sc->btcoex_info);
+       }
+
 mutex_unlock:
        mutex_unlock(&sc->mutex);
 
@@ -2129,6 +2146,12 @@ static void ath9k_stop(struct ieee80211_hw *hw)
                return; /* another wiphy still in use */
        }
 
+       if (sc->sc_flags & SC_OP_BTCOEX_ENABLED) {
+               ath9k_hw_btcoex_disable(sc->sc_ah);
+               if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE)
+                       ath_btcoex_timer_pause(sc, &sc->btcoex_info);
+       }
+
        /* make sure h/w will not generate any interrupt
         * before setting the invalid flag. */
        ath9k_hw_set_interrupts(sc->sc_ah, 0);
@@ -2142,9 +2165,6 @@ static void ath9k_stop(struct ieee80211_hw *hw)
 
        wiphy_rfkill_stop_polling(sc->hw->wiphy);
 
-       if (sc->sc_flags & SC_OP_BTCOEX_ENABLED)
-               ath9k_hw_btcoex_disable(sc->sc_ah);
-
        /* disable HAL and put h/w to sleep */
        ath9k_hw_disable(sc->sc_ah);
        ath9k_hw_configpcipowersave(sc->sc_ah, 1);
index 1d8e0a8..3ddb243 100644 (file)
@@ -970,6 +970,8 @@ enum {
 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF_S      7
 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB        0x00001000
 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB_S      12
+#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB      0x00001000
+#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB_S    1
 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB         0x00008000
 #define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB_S       15
 #define AR_GPIO_RTC_RESET_OVERRIDE_ENABLE        0x00010000
@@ -978,6 +980,8 @@ enum {
 #define AR_GPIO_INPUT_MUX1                       0x4058
 #define AR_GPIO_INPUT_MUX1_BT_ACTIVE             0x000f0000
 #define AR_GPIO_INPUT_MUX1_BT_ACTIVE_S           16
+#define AR_GPIO_INPUT_MUX1_BT_PRIORITY           0x00000f00
+#define AR_GPIO_INPUT_MUX1_BT_PRIORITY_S         8
 
 #define AR_GPIO_INPUT_MUX2                       0x405c
 #define AR_GPIO_INPUT_MUX2_CLK25                 0x0000000f
@@ -1003,6 +1007,8 @@ enum {
 
 #define AR_OBS                  0x4080
 
+#define AR_GPIO_PDPU                             0x4088
+
 #define AR_PCIE_MSI                              0x4094
 #define AR_PCIE_MSI_ENABLE                       0x00000001
 
@@ -1436,6 +1442,7 @@ enum {
 #define AR_QUIET1_NEXT_QUIET_M         0x0000ffff
 #define AR_QUIET1_QUIET_ENABLE         0x00010000
 #define AR_QUIET1_QUIET_ACK_CTS_ENABLE 0x00020000
+#define AR_QUIET1_QUIET_ACK_CTS_ENABLE_S 17
 #define AR_QUIET2          0x8100
 #define AR_QUIET2_QUIET_PERIOD_S       0
 #define AR_QUIET2_QUIET_PERIOD_M       0x0000ffff
@@ -1481,6 +1488,8 @@ enum {
 #define AR_PCU_CLEAR_VMF           0x01000000
 #define AR_PCU_CLEAR_BA_VALID      0x04000000
 
+#define AR_PCU_BT_ANT_PREVENT_RX   0x00100000
+#define AR_PCU_BT_ANT_PREVENT_RX_S 20
 
 #define AR_FILT_OFDM           0x8124
 #define AR_FILT_OFDM_COUNT     0x00FFFFFF
@@ -1508,6 +1517,46 @@ enum {
 #define AR_PHY_ERR_3_COUNT     0x00FFFFFF
 #define AR_PHY_ERR_MASK_3      0x816c
 
+#define AR_BT_COEX_MODE            0x8170
+#define AR_BT_TIME_EXTEND          0x000000ff
+#define AR_BT_TIME_EXTEND_S        0
+#define AR_BT_TXSTATE_EXTEND       0x00000100
+#define AR_BT_TXSTATE_EXTEND_S     8
+#define AR_BT_TX_FRAME_EXTEND      0x00000200
+#define AR_BT_TX_FRAME_EXTEND_S    9
+#define AR_BT_MODE                 0x00000c00
+#define AR_BT_MODE_S               10
+#define AR_BT_QUIET                0x00001000
+#define AR_BT_QUIET_S              12
+#define AR_BT_QCU_THRESH           0x0001e000
+#define AR_BT_QCU_THRESH_S         13
+#define AR_BT_RX_CLEAR_POLARITY    0x00020000
+#define AR_BT_RX_CLEAR_POLARITY_S  17
+#define AR_BT_PRIORITY_TIME        0x00fc0000
+#define AR_BT_PRIORITY_TIME_S      18
+#define AR_BT_FIRST_SLOT_TIME      0xff000000
+#define AR_BT_FIRST_SLOT_TIME_S    24
+
+#define AR_BT_COEX_WEIGHT          0x8174
+#define AR_BT_COEX_WGHT                   0xff55
+#define AR_STOMP_ALL_WLAN_WGHT    0xffcc
+#define AR_STOMP_LOW_WLAN_WGHT    0xaaa8
+#define AR_STOMP_NONE_WLAN_WGHT           0xaa00
+#define AR_BTCOEX_BT_WGHT          0x0000ffff
+#define AR_BTCOEX_BT_WGHT_S        0
+#define AR_BTCOEX_WL_WGHT          0xffff0000
+#define AR_BTCOEX_WL_WGHT_S        16
+
+#define AR_BT_COEX_MODE2           0x817c
+#define AR_BT_BCN_MISS_THRESH      0x000000ff
+#define AR_BT_BCN_MISS_THRESH_S    0
+#define AR_BT_BCN_MISS_CNT         0x0000ff00
+#define AR_BT_BCN_MISS_CNT_S       8
+#define AR_BT_HOLD_RX_CLEAR        0x00010000
+#define AR_BT_HOLD_RX_CLEAR_S      16
+#define AR_BT_DISABLE_BT_ANT       0x00100000
+#define AR_BT_DISABLE_BT_ANT_S     20
+
 #define AR_TXSIFS              0x81d0
 #define AR_TXSIFS_TIME         0x000000FF
 #define AR_TXSIFS_TX_LATENCY   0x00000F00
index 87762da..42551a4 100644 (file)
@@ -493,7 +493,12 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
        if (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE || legacy)
                return 0;
 
-       aggr_limit = min(max_4ms_framelen, (u32)ATH_AMPDU_LIMIT_MAX);
+       if (sc->sc_flags & SC_OP_BT_PRIORITY_DETECTED)
+               aggr_limit = min((max_4ms_framelen * 3) / 8,
+                                (u32)ATH_AMPDU_LIMIT_MAX);
+       else
+               aggr_limit = min(max_4ms_framelen,
+                                (u32)ATH_AMPDU_LIMIT_MAX);
 
        /*
         * h/w can accept aggregates upto 16 bit lengths (65535).
@@ -872,7 +877,7 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
        return &sc->tx.txq[qnum];
 }
 
-static int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype)
+int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype)
 {
        int qnum;