1 /* 2 * Copyright (c) 2009-2011 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 <linux/export.h> 18 #include "hw.h" 19 20 enum ath_bt_mode { 21 ATH_BT_COEX_MODE_LEGACY, /* legacy rx_clear mode */ 22 ATH_BT_COEX_MODE_UNSLOTTED, /* untimed/unslotted mode */ 23 ATH_BT_COEX_MODE_SLOTTED, /* slotted mode */ 24 ATH_BT_COEX_MODE_DISABLED, /* coexistence disabled */ 25 }; 26 27 struct ath_btcoex_config { 28 u8 bt_time_extend; 29 bool bt_txstate_extend; 30 bool bt_txframe_extend; 31 enum ath_bt_mode bt_mode; /* coexistence mode */ 32 bool bt_quiet_collision; 33 bool bt_rxclear_polarity; /* invert rx_clear as WLAN_ACTIVE*/ 34 u8 bt_priority_time; 35 u8 bt_first_slot_time; 36 bool bt_hold_rx_clear; 37 }; 38 39 static const u32 ar9003_wlan_weights[ATH_BTCOEX_STOMP_MAX] 40 [AR9300_NUM_WLAN_WEIGHTS] = { 41 { 0xfffffff0, 0xfffffff0, 0xfffffff0, 0xfffffff0 }, /* STOMP_ALL */ 42 { 0x88888880, 0x88888880, 0x88888880, 0x88888880 }, /* STOMP_LOW */ 43 { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* STOMP_NONE */ 44 }; 45 46 static const u32 mci_wlan_weights[ATH_BTCOEX_STOMP_MAX] 47 [AR9300_NUM_WLAN_WEIGHTS] = { 48 { 0x01017d01, 0x41414101, 0x41414101, 0x41414141 }, /* STOMP_ALL */ 49 { 0x01017d01, 0x3b3b3b01, 0x3b3b3b01, 0x3b3b3b3b }, /* STOMP_LOW */ 50 { 0x01017d01, 0x01010101, 0x01010101, 0x01010101 }, /* STOMP_NONE */ 51 { 0x01017d01, 0x013b0101, 0x3b3b0101, 0x3b3b013b }, /* STOMP_LOW_FTP */ 52 { 0xffffff01, 0xffffffff, 0xffffff01, 0xffffffff }, /* STOMP_AUDIO */ 53 }; 54 55 void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum) 56 { 57 struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; 58 const struct ath_btcoex_config ath_bt_config = { 59 .bt_time_extend = 0, 60 .bt_txstate_extend = true, 61 .bt_txframe_extend = true, 62 .bt_mode = ATH_BT_COEX_MODE_SLOTTED, 63 .bt_quiet_collision = true, 64 .bt_rxclear_polarity = true, 65 .bt_priority_time = 2, 66 .bt_first_slot_time = 5, 67 .bt_hold_rx_clear = true, 68 }; 69 u32 i, idx; 70 bool rxclear_polarity = ath_bt_config.bt_rxclear_polarity; 71 72 if (AR_SREV_9300_20_OR_LATER(ah)) 73 rxclear_polarity = !ath_bt_config.bt_rxclear_polarity; 74 75 btcoex_hw->bt_coex_mode = 76 (btcoex_hw->bt_coex_mode & AR_BT_QCU_THRESH) | 77 SM(ath_bt_config.bt_time_extend, AR_BT_TIME_EXTEND) | 78 SM(ath_bt_config.bt_txstate_extend, AR_BT_TXSTATE_EXTEND) | 79 SM(ath_bt_config.bt_txframe_extend, AR_BT_TX_FRAME_EXTEND) | 80 SM(ath_bt_config.bt_mode, AR_BT_MODE) | 81 SM(ath_bt_config.bt_quiet_collision, AR_BT_QUIET) | 82 SM(rxclear_polarity, AR_BT_RX_CLEAR_POLARITY) | 83 SM(ath_bt_config.bt_priority_time, AR_BT_PRIORITY_TIME) | 84 SM(ath_bt_config.bt_first_slot_time, AR_BT_FIRST_SLOT_TIME) | 85 SM(qnum, AR_BT_QCU_THRESH); 86 87 btcoex_hw->bt_coex_mode2 = 88 SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) | 89 SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) | 90 AR_BT_DISABLE_BT_ANT; 91 92 for (i = 0; i < 32; i++) { 93 idx = (debruijn32 << i) >> 27; 94 ah->hw_gen_timers.gen_timer_index[idx] = i; 95 } 96 } 97 EXPORT_SYMBOL(ath9k_hw_init_btcoex_hw); 98 99 void ath9k_hw_btcoex_init_scheme(struct ath_hw *ah) 100 { 101 struct ath_common *common = ath9k_hw_common(ah); 102 struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; 103 104 /* 105 * Check if BTCOEX is globally disabled. 106 */ 107 if (!common->btcoex_enabled) { 108 btcoex_hw->scheme = ATH_BTCOEX_CFG_NONE; 109 return; 110 } 111 112 if (AR_SREV_9300_20_OR_LATER(ah)) { 113 btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE; 114 btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9300; 115 btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9300; 116 btcoex_hw->btpriority_gpio = ATH_BTPRIORITY_GPIO_9300; 117 } else if (AR_SREV_9280_20_OR_LATER(ah)) { 118 btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9280; 119 btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9280; 120 121 if (AR_SREV_9285(ah)) { 122 btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE; 123 btcoex_hw->btpriority_gpio = ATH_BTPRIORITY_GPIO_9285; 124 } else { 125 btcoex_hw->scheme = ATH_BTCOEX_CFG_2WIRE; 126 } 127 } 128 } 129 EXPORT_SYMBOL(ath9k_hw_btcoex_init_scheme); 130 131 void ath9k_hw_btcoex_init_2wire(struct ath_hw *ah) 132 { 133 struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; 134 135 /* connect bt_active to baseband */ 136 REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, 137 (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF | 138 AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF)); 139 140 REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, 141 AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB); 142 143 /* Set input mux for bt_active to gpio pin */ 144 REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, 145 AR_GPIO_INPUT_MUX1_BT_ACTIVE, 146 btcoex_hw->btactive_gpio); 147 148 /* Configure the desired gpio port for input */ 149 ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btactive_gpio); 150 } 151 EXPORT_SYMBOL(ath9k_hw_btcoex_init_2wire); 152 153 void ath9k_hw_btcoex_init_3wire(struct ath_hw *ah) 154 { 155 struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; 156 157 /* btcoex 3-wire */ 158 REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, 159 (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB | 160 AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB)); 161 162 /* Set input mux for bt_prority_async and 163 * bt_active_async to GPIO pins */ 164 REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, 165 AR_GPIO_INPUT_MUX1_BT_ACTIVE, 166 btcoex_hw->btactive_gpio); 167 168 REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, 169 AR_GPIO_INPUT_MUX1_BT_PRIORITY, 170 btcoex_hw->btpriority_gpio); 171 172 /* Configure the desired GPIO ports for input */ 173 174 ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btactive_gpio); 175 ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btpriority_gpio); 176 } 177 EXPORT_SYMBOL(ath9k_hw_btcoex_init_3wire); 178 179 void ath9k_hw_btcoex_init_mci(struct ath_hw *ah) 180 { 181 ah->btcoex_hw.mci.ready = false; 182 ah->btcoex_hw.mci.bt_state = 0; 183 ah->btcoex_hw.mci.bt_ver_major = 3; 184 ah->btcoex_hw.mci.bt_ver_minor = 0; 185 ah->btcoex_hw.mci.bt_version_known = false; 186 ah->btcoex_hw.mci.update_2g5g = true; 187 ah->btcoex_hw.mci.is_2g = true; 188 ah->btcoex_hw.mci.wlan_channels_update = false; 189 ah->btcoex_hw.mci.wlan_channels[0] = 0x00000000; 190 ah->btcoex_hw.mci.wlan_channels[1] = 0xffffffff; 191 ah->btcoex_hw.mci.wlan_channels[2] = 0xffffffff; 192 ah->btcoex_hw.mci.wlan_channels[3] = 0x7fffffff; 193 ah->btcoex_hw.mci.query_bt = true; 194 ah->btcoex_hw.mci.unhalt_bt_gpm = true; 195 ah->btcoex_hw.mci.halted_bt_gpm = false; 196 ah->btcoex_hw.mci.need_flush_btinfo = false; 197 ah->btcoex_hw.mci.wlan_cal_seq = 0; 198 ah->btcoex_hw.mci.wlan_cal_done = 0; 199 ah->btcoex_hw.mci.config = (AR_SREV_9462(ah)) ? 0x2201 : 0xa4c1; 200 } 201 EXPORT_SYMBOL(ath9k_hw_btcoex_init_mci); 202 203 static void ath9k_hw_btcoex_enable_2wire(struct ath_hw *ah) 204 { 205 struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; 206 207 /* Configure the desired GPIO port for TX_FRAME output */ 208 ath9k_hw_cfg_output(ah, btcoex_hw->wlanactive_gpio, 209 AR_GPIO_OUTPUT_MUX_AS_TX_FRAME); 210 } 211 212 /* 213 * For AR9002, bt_weight/wlan_weight are used. 214 * For AR9003 and above, stomp_type is used. 215 */ 216 void ath9k_hw_btcoex_set_weight(struct ath_hw *ah, 217 u32 bt_weight, 218 u32 wlan_weight, 219 enum ath_stomp_type stomp_type) 220 { 221 struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; 222 struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; 223 u8 txprio_shift[] = { 24, 16, 16, 0 }; /* tx priority weight */ 224 bool concur_tx = (mci_hw->concur_tx && btcoex_hw->tx_prio[stomp_type]); 225 const u32 *weight = ar9003_wlan_weights[stomp_type]; 226 int i; 227 228 if (!AR_SREV_9300_20_OR_LATER(ah)) { 229 btcoex_hw->bt_coex_weights = 230 SM(bt_weight, AR_BTCOEX_BT_WGHT) | 231 SM(wlan_weight, AR_BTCOEX_WL_WGHT); 232 return; 233 } 234 235 if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { 236 enum ath_stomp_type stype = 237 ((stomp_type == ATH_BTCOEX_STOMP_LOW) && 238 btcoex_hw->mci.stomp_ftp) ? 239 ATH_BTCOEX_STOMP_LOW_FTP : stomp_type; 240 weight = mci_wlan_weights[stype]; 241 } 242 243 for (i = 0; i < AR9300_NUM_WLAN_WEIGHTS; i++) { 244 btcoex_hw->bt_weight[i] = AR9300_BT_WGHT; 245 btcoex_hw->wlan_weight[i] = weight[i]; 246 if (concur_tx && i) { 247 btcoex_hw->wlan_weight[i] &= 248 ~(0xff << txprio_shift[i-1]); 249 btcoex_hw->wlan_weight[i] |= 250 (btcoex_hw->tx_prio[stomp_type] << 251 txprio_shift[i-1]); 252 } 253 } 254 /* Last WLAN weight has to be adjusted wrt tx priority */ 255 if (concur_tx) { 256 btcoex_hw->wlan_weight[i-1] &= ~(0xff << txprio_shift[i-1]); 257 btcoex_hw->wlan_weight[i-1] |= (btcoex_hw->tx_prio[stomp_type] 258 << txprio_shift[i-1]); 259 } 260 261 } 262 EXPORT_SYMBOL(ath9k_hw_btcoex_set_weight); 263 264 265 static void ath9k_hw_btcoex_enable_3wire(struct ath_hw *ah) 266 { 267 struct ath_btcoex_hw *btcoex = &ah->btcoex_hw; 268 u32 val; 269 int i; 270 271 /* 272 * Program coex mode and weight registers to 273 * enable coex 3-wire 274 */ 275 REG_WRITE(ah, AR_BT_COEX_MODE, btcoex->bt_coex_mode); 276 REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex->bt_coex_mode2); 277 278 279 if (AR_SREV_9300_20_OR_LATER(ah)) { 280 REG_WRITE(ah, AR_BT_COEX_WL_WEIGHTS0, btcoex->wlan_weight[0]); 281 REG_WRITE(ah, AR_BT_COEX_WL_WEIGHTS1, btcoex->wlan_weight[1]); 282 for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) 283 REG_WRITE(ah, AR_BT_COEX_BT_WEIGHTS(i), 284 btcoex->bt_weight[i]); 285 } else 286 REG_WRITE(ah, AR_BT_COEX_WEIGHT, btcoex->bt_coex_weights); 287 288 289 290 if (AR_SREV_9271(ah)) { 291 val = REG_READ(ah, 0x50040); 292 val &= 0xFFFFFEFF; 293 REG_WRITE(ah, 0x50040, val); 294 } 295 296 REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); 297 REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); 298 299 ath9k_hw_cfg_output(ah, btcoex->wlanactive_gpio, 300 AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL); 301 } 302 303 static void ath9k_hw_btcoex_enable_mci(struct ath_hw *ah) 304 { 305 struct ath_btcoex_hw *btcoex = &ah->btcoex_hw; 306 int i; 307 308 for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) 309 REG_WRITE(ah, AR_MCI_COEX_WL_WEIGHTS(i), 310 btcoex->wlan_weight[i]); 311 312 REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); 313 btcoex->enabled = true; 314 } 315 316 void ath9k_hw_btcoex_enable(struct ath_hw *ah) 317 { 318 struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; 319 320 switch (ath9k_hw_get_btcoex_scheme(ah)) { 321 case ATH_BTCOEX_CFG_NONE: 322 return; 323 case ATH_BTCOEX_CFG_2WIRE: 324 ath9k_hw_btcoex_enable_2wire(ah); 325 break; 326 case ATH_BTCOEX_CFG_3WIRE: 327 if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { 328 ath9k_hw_btcoex_enable_mci(ah); 329 return; 330 } 331 ath9k_hw_btcoex_enable_3wire(ah); 332 break; 333 } 334 335 REG_RMW(ah, AR_GPIO_PDPU, 336 (0x2 << (btcoex_hw->btactive_gpio * 2)), 337 (0x3 << (btcoex_hw->btactive_gpio * 2))); 338 339 ah->btcoex_hw.enabled = true; 340 } 341 EXPORT_SYMBOL(ath9k_hw_btcoex_enable); 342 343 void ath9k_hw_btcoex_disable(struct ath_hw *ah) 344 { 345 struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; 346 int i; 347 348 btcoex_hw->enabled = false; 349 if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { 350 ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); 351 for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) 352 REG_WRITE(ah, AR_MCI_COEX_WL_WEIGHTS(i), 353 btcoex_hw->wlan_weight[i]); 354 return; 355 } 356 ath9k_hw_set_gpio(ah, btcoex_hw->wlanactive_gpio, 0); 357 358 ath9k_hw_cfg_output(ah, btcoex_hw->wlanactive_gpio, 359 AR_GPIO_OUTPUT_MUX_AS_OUTPUT); 360 361 if (btcoex_hw->scheme == ATH_BTCOEX_CFG_3WIRE) { 362 REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE); 363 REG_WRITE(ah, AR_BT_COEX_MODE2, 0); 364 365 if (AR_SREV_9300_20_OR_LATER(ah)) { 366 REG_WRITE(ah, AR_BT_COEX_WL_WEIGHTS0, 0); 367 REG_WRITE(ah, AR_BT_COEX_WL_WEIGHTS1, 0); 368 for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) 369 REG_WRITE(ah, AR_BT_COEX_BT_WEIGHTS(i), 0); 370 } else 371 REG_WRITE(ah, AR_BT_COEX_WEIGHT, 0); 372 373 } 374 } 375 EXPORT_SYMBOL(ath9k_hw_btcoex_disable); 376 377 /* 378 * Configures appropriate weight based on stomp type. 379 */ 380 void ath9k_hw_btcoex_bt_stomp(struct ath_hw *ah, 381 enum ath_stomp_type stomp_type) 382 { 383 if (AR_SREV_9300_20_OR_LATER(ah)) { 384 ath9k_hw_btcoex_set_weight(ah, 0, 0, stomp_type); 385 return; 386 } 387 388 switch (stomp_type) { 389 case ATH_BTCOEX_STOMP_ALL: 390 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, 391 AR_STOMP_ALL_WLAN_WGHT, 0); 392 break; 393 case ATH_BTCOEX_STOMP_LOW: 394 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, 395 AR_STOMP_LOW_WLAN_WGHT, 0); 396 break; 397 case ATH_BTCOEX_STOMP_NONE: 398 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, 399 AR_STOMP_NONE_WLAN_WGHT, 0); 400 break; 401 default: 402 ath_dbg(ath9k_hw_common(ah), BTCOEX, "Invalid Stomptype\n"); 403 break; 404 } 405 } 406 EXPORT_SYMBOL(ath9k_hw_btcoex_bt_stomp); 407 408 void ath9k_hw_btcoex_set_concur_txprio(struct ath_hw *ah, u8 *stomp_txprio) 409 { 410 struct ath_btcoex_hw *btcoex = &ah->btcoex_hw; 411 int i; 412 413 for (i = 0; i < ATH_BTCOEX_STOMP_MAX; i++) 414 btcoex->tx_prio[i] = stomp_txprio[i]; 415 } 416 EXPORT_SYMBOL(ath9k_hw_btcoex_set_concur_txprio); 417