1 /* 2 * Copyright (c) 2009 Atheros Communications Inc. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include "ath9k.h" 18 19 static const struct ath_btcoex_config ath_bt_config = { 0, true, true, 20 ATH_BT_COEX_MODE_SLOTTED, true, true, 2, 5, true }; 21 22 static const u16 ath_subsysid_tbl[] = { 23 AR9280_COEX2WIRE_SUBSYSID, 24 AT9285_COEX3WIRE_SA_SUBSYSID, 25 AT9285_COEX3WIRE_DA_SUBSYSID 26 }; 27 28 /* 29 * Checks the subsystem id of the device to see if it 30 * supports btcoex 31 */ 32 bool ath_btcoex_supported(u16 subsysid) 33 { 34 int i; 35 36 if (!subsysid) 37 return false; 38 39 for (i = 0; i < ARRAY_SIZE(ath_subsysid_tbl); i++) 40 if (subsysid == ath_subsysid_tbl[i]) 41 return true; 42 43 return false; 44 } 45 46 /* 47 * Detects if there is any priority bt traffic 48 */ 49 static void ath_detect_bt_priority(struct ath_softc *sc) 50 { 51 struct ath_btcoex_info *btinfo = &sc->btcoex_info; 52 53 if (ath9k_hw_gpio_get(sc->sc_ah, btinfo->btpriority_gpio)) 54 btinfo->bt_priority_cnt++; 55 56 if (time_after(jiffies, btinfo->bt_priority_time + 57 msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { 58 if (btinfo->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { 59 DPRINTF(sc, ATH_DBG_BTCOEX, 60 "BT priority traffic detected"); 61 sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED; 62 } else { 63 sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; 64 } 65 66 btinfo->bt_priority_cnt = 0; 67 btinfo->bt_priority_time = jiffies; 68 } 69 } 70 71 /* 72 * Configures appropriate weight based on stomp type. 73 */ 74 static void ath_btcoex_bt_stomp(struct ath_softc *sc, 75 struct ath_btcoex_info *btinfo, 76 int stomp_type) 77 { 78 79 switch (stomp_type) { 80 case ATH_BTCOEX_STOMP_ALL: 81 ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT, 82 AR_STOMP_ALL_WLAN_WGHT); 83 break; 84 case ATH_BTCOEX_STOMP_LOW: 85 ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT, 86 AR_STOMP_LOW_WLAN_WGHT); 87 break; 88 case ATH_BTCOEX_STOMP_NONE: 89 ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT, 90 AR_STOMP_NONE_WLAN_WGHT); 91 break; 92 default: 93 DPRINTF(sc, ATH_DBG_BTCOEX, "Invalid Stomptype\n"); 94 break; 95 } 96 97 ath9k_hw_btcoex_enable(sc->sc_ah); 98 } 99 100 /* 101 * This is the master bt coex timer which runs for every 102 * 45ms, bt traffic will be given priority during 55% of this 103 * period while wlan gets remaining 45% 104 */ 105 106 static void ath_btcoex_period_timer(unsigned long data) 107 { 108 struct ath_softc *sc = (struct ath_softc *) data; 109 struct ath_btcoex_info *btinfo = &sc->btcoex_info; 110 111 ath_detect_bt_priority(sc); 112 113 spin_lock_bh(&btinfo->btcoex_lock); 114 115 ath_btcoex_bt_stomp(sc, btinfo, btinfo->bt_stomp_type); 116 117 spin_unlock_bh(&btinfo->btcoex_lock); 118 119 if (btinfo->btcoex_period != btinfo->btcoex_no_stomp) { 120 if (btinfo->hw_timer_enabled) 121 ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer); 122 123 ath_gen_timer_start(sc->sc_ah, 124 btinfo->no_stomp_timer, 125 (ath9k_hw_gettsf32(sc->sc_ah) + 126 btinfo->btcoex_no_stomp), 127 btinfo->btcoex_no_stomp * 10); 128 btinfo->hw_timer_enabled = true; 129 } 130 131 mod_timer(&btinfo->period_timer, jiffies + 132 msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD)); 133 } 134 135 /* 136 * Generic tsf based hw timer which configures weight 137 * registers to time slice between wlan and bt traffic 138 */ 139 140 static void ath_btcoex_no_stomp_timer(void *arg) 141 { 142 struct ath_softc *sc = (struct ath_softc *)arg; 143 struct ath_btcoex_info *btinfo = &sc->btcoex_info; 144 145 DPRINTF(sc, ATH_DBG_BTCOEX, "no stomp timer running \n"); 146 147 spin_lock_bh(&btinfo->btcoex_lock); 148 149 if (btinfo->bt_stomp_type == ATH_BTCOEX_STOMP_LOW) 150 ath_btcoex_bt_stomp(sc, btinfo, ATH_BTCOEX_STOMP_NONE); 151 else if (btinfo->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) 152 ath_btcoex_bt_stomp(sc, btinfo, ATH_BTCOEX_STOMP_LOW); 153 154 spin_unlock_bh(&btinfo->btcoex_lock); 155 } 156 157 static int ath_init_btcoex_info(struct ath_hw *hw, 158 struct ath_btcoex_info *btcoex_info) 159 { 160 u32 i; 161 int qnum; 162 163 qnum = ath_tx_get_qnum(hw->ah_sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE); 164 165 btcoex_info->bt_coex_mode = 166 (btcoex_info->bt_coex_mode & AR_BT_QCU_THRESH) | 167 SM(ath_bt_config.bt_time_extend, AR_BT_TIME_EXTEND) | 168 SM(ath_bt_config.bt_txstate_extend, AR_BT_TXSTATE_EXTEND) | 169 SM(ath_bt_config.bt_txframe_extend, AR_BT_TX_FRAME_EXTEND) | 170 SM(ath_bt_config.bt_mode, AR_BT_MODE) | 171 SM(ath_bt_config.bt_quiet_collision, AR_BT_QUIET) | 172 SM(ath_bt_config.bt_rxclear_polarity, AR_BT_RX_CLEAR_POLARITY) | 173 SM(ath_bt_config.bt_priority_time, AR_BT_PRIORITY_TIME) | 174 SM(ath_bt_config.bt_first_slot_time, AR_BT_FIRST_SLOT_TIME) | 175 SM(qnum, AR_BT_QCU_THRESH); 176 177 btcoex_info->bt_coex_mode2 = 178 SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) | 179 SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) | 180 AR_BT_DISABLE_BT_ANT; 181 182 btcoex_info->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; 183 184 btcoex_info->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000; 185 186 btcoex_info->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * 187 btcoex_info->btcoex_period / 100; 188 189 for (i = 0; i < 32; i++) 190 hw->hw_gen_timers.gen_timer_index[(debruijn32 << i) >> 27] = i; 191 192 setup_timer(&btcoex_info->period_timer, ath_btcoex_period_timer, 193 (unsigned long) hw->ah_sc); 194 195 btcoex_info->no_stomp_timer = ath_gen_timer_alloc(hw, 196 ath_btcoex_no_stomp_timer, 197 ath_btcoex_no_stomp_timer, 198 (void *)hw->ah_sc, AR_FIRST_NDP_TIMER); 199 200 if (btcoex_info->no_stomp_timer == NULL) 201 return -ENOMEM; 202 203 spin_lock_init(&btcoex_info->btcoex_lock); 204 205 return 0; 206 } 207 208 int ath9k_hw_btcoex_init(struct ath_hw *ah) 209 { 210 struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; 211 int ret = 0; 212 213 if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) { 214 /* connect bt_active to baseband */ 215 REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, 216 (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF | 217 AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF)); 218 219 REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, 220 AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB); 221 222 /* Set input mux for bt_active to gpio pin */ 223 REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, 224 AR_GPIO_INPUT_MUX1_BT_ACTIVE, 225 btcoex_info->btactive_gpio); 226 227 /* Configure the desired gpio port for input */ 228 ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio); 229 } else { 230 /* btcoex 3-wire */ 231 REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, 232 (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB | 233 AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB)); 234 235 /* Set input mux for bt_prority_async and 236 * bt_active_async to GPIO pins */ 237 REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, 238 AR_GPIO_INPUT_MUX1_BT_ACTIVE, 239 btcoex_info->btactive_gpio); 240 241 REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, 242 AR_GPIO_INPUT_MUX1_BT_PRIORITY, 243 btcoex_info->btpriority_gpio); 244 245 /* Configure the desired GPIO ports for input */ 246 247 ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio); 248 ath9k_hw_cfg_gpio_input(ah, btcoex_info->btpriority_gpio); 249 250 ret = ath_init_btcoex_info(ah, btcoex_info); 251 } 252 253 return ret; 254 } 255 256 void ath9k_hw_btcoex_enable(struct ath_hw *ah) 257 { 258 struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; 259 260 if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) { 261 /* Configure the desired GPIO port for TX_FRAME output */ 262 ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio, 263 AR_GPIO_OUTPUT_MUX_AS_TX_FRAME); 264 } else { 265 /* 266 * Program coex mode and weight registers to 267 * enable coex 3-wire 268 */ 269 REG_WRITE(ah, AR_BT_COEX_MODE, btcoex_info->bt_coex_mode); 270 REG_WRITE(ah, AR_BT_COEX_WEIGHT, btcoex_info->bt_coex_weights); 271 REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex_info->bt_coex_mode2); 272 273 REG_RMW_FIELD(ah, AR_QUIET1, 274 AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); 275 REG_RMW_FIELD(ah, AR_PCU_MISC, 276 AR_PCU_BT_ANT_PREVENT_RX, 0); 277 278 ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio, 279 AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL); 280 } 281 282 REG_RMW(ah, AR_GPIO_PDPU, 283 (0x2 << (btcoex_info->btactive_gpio * 2)), 284 (0x3 << (btcoex_info->btactive_gpio * 2))); 285 286 ah->ah_sc->sc_flags |= SC_OP_BTCOEX_ENABLED; 287 } 288 289 void ath9k_hw_btcoex_disable(struct ath_hw *ah) 290 { 291 struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; 292 293 ath9k_hw_set_gpio(ah, btcoex_info->wlanactive_gpio, 0); 294 295 ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio, 296 AR_GPIO_OUTPUT_MUX_AS_OUTPUT); 297 298 if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) { 299 REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE); 300 REG_WRITE(ah, AR_BT_COEX_WEIGHT, 0); 301 REG_WRITE(ah, AR_BT_COEX_MODE2, 0); 302 } 303 304 ah->ah_sc->sc_flags &= ~SC_OP_BTCOEX_ENABLED; 305 } 306 307 /* 308 * Pause btcoex timer and bt duty cycle timer 309 */ 310 void ath_btcoex_timer_pause(struct ath_softc *sc, 311 struct ath_btcoex_info *btinfo) 312 { 313 314 del_timer_sync(&btinfo->period_timer); 315 316 if (btinfo->hw_timer_enabled) 317 ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer); 318 319 btinfo->hw_timer_enabled = false; 320 } 321 322 /* 323 * (Re)start btcoex timers 324 */ 325 void ath_btcoex_timer_resume(struct ath_softc *sc, 326 struct ath_btcoex_info *btinfo) 327 { 328 329 DPRINTF(sc, ATH_DBG_BTCOEX, "Starting btcoex timers"); 330 331 /* make sure duty cycle timer is also stopped when resuming */ 332 if (btinfo->hw_timer_enabled) 333 ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer); 334 335 btinfo->bt_priority_cnt = 0; 336 btinfo->bt_priority_time = jiffies; 337 sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; 338 339 mod_timer(&btinfo->period_timer, jiffies); 340 } 341