0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #include <linux/dma-mapping.h>
0018 #include <linux/slab.h>
0019
0020 #include "ath9k.h"
0021 #include "mci.h"
0022
0023 static const u8 ath_mci_duty_cycle[] = { 55, 50, 60, 70, 80, 85, 90, 95, 98 };
0024
0025 static struct ath_mci_profile_info*
0026 ath_mci_find_profile(struct ath_mci_profile *mci,
0027 struct ath_mci_profile_info *info)
0028 {
0029 struct ath_mci_profile_info *entry;
0030
0031 if (list_empty(&mci->info))
0032 return NULL;
0033
0034 list_for_each_entry(entry, &mci->info, list) {
0035 if (entry->conn_handle == info->conn_handle)
0036 return entry;
0037 }
0038 return NULL;
0039 }
0040
0041 static bool ath_mci_add_profile(struct ath_common *common,
0042 struct ath_mci_profile *mci,
0043 struct ath_mci_profile_info *info)
0044 {
0045 struct ath_mci_profile_info *entry;
0046 static const u8 voice_priority[] = { 110, 110, 110, 112, 110, 110, 114, 116, 118 };
0047
0048 if ((mci->num_sco == ATH_MCI_MAX_SCO_PROFILE) &&
0049 (info->type == MCI_GPM_COEX_PROFILE_VOICE))
0050 return false;
0051
0052 if (((NUM_PROF(mci) - mci->num_sco) == ATH_MCI_MAX_ACL_PROFILE) &&
0053 (info->type != MCI_GPM_COEX_PROFILE_VOICE))
0054 return false;
0055
0056 entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
0057 if (!entry)
0058 return false;
0059
0060 memcpy(entry, info, 10);
0061 INC_PROF(mci, info);
0062 list_add_tail(&entry->list, &mci->info);
0063 if (info->type == MCI_GPM_COEX_PROFILE_VOICE) {
0064 if (info->voice_type < sizeof(voice_priority))
0065 mci->voice_priority = voice_priority[info->voice_type];
0066 else
0067 mci->voice_priority = 110;
0068 }
0069
0070 return true;
0071 }
0072
0073 static void ath_mci_del_profile(struct ath_common *common,
0074 struct ath_mci_profile *mci,
0075 struct ath_mci_profile_info *entry)
0076 {
0077 if (!entry)
0078 return;
0079
0080 DEC_PROF(mci, entry);
0081 list_del(&entry->list);
0082 kfree(entry);
0083 }
0084
0085 void ath_mci_flush_profile(struct ath_mci_profile *mci)
0086 {
0087 struct ath_mci_profile_info *info, *tinfo;
0088
0089 mci->aggr_limit = 0;
0090 mci->num_mgmt = 0;
0091
0092 if (list_empty(&mci->info))
0093 return;
0094
0095 list_for_each_entry_safe(info, tinfo, &mci->info, list) {
0096 list_del(&info->list);
0097 DEC_PROF(mci, info);
0098 kfree(info);
0099 }
0100 }
0101
0102 static void ath_mci_adjust_aggr_limit(struct ath_btcoex *btcoex)
0103 {
0104 struct ath_mci_profile *mci = &btcoex->mci;
0105 u32 wlan_airtime = btcoex->btcoex_period *
0106 (100 - btcoex->duty_cycle) / 100;
0107
0108
0109
0110
0111
0112
0113
0114 if ((wlan_airtime <= 4) &&
0115 (!mci->aggr_limit || (mci->aggr_limit > (2 * wlan_airtime))))
0116 mci->aggr_limit = 2 * wlan_airtime;
0117 }
0118
0119 static void ath_mci_update_scheme(struct ath_softc *sc)
0120 {
0121 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
0122 struct ath_btcoex *btcoex = &sc->btcoex;
0123 struct ath_mci_profile *mci = &btcoex->mci;
0124 struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci;
0125 struct ath_mci_profile_info *info;
0126 u32 num_profile = NUM_PROF(mci);
0127
0128 if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_TUNING)
0129 goto skip_tuning;
0130
0131 mci->aggr_limit = 0;
0132 btcoex->duty_cycle = ath_mci_duty_cycle[num_profile];
0133 btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD;
0134 if (NUM_PROF(mci))
0135 btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
0136 else
0137 btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL :
0138 ATH_BTCOEX_STOMP_LOW;
0139
0140 if (num_profile == 1) {
0141 info = list_first_entry(&mci->info,
0142 struct ath_mci_profile_info,
0143 list);
0144 if (mci->num_sco) {
0145 if (info->T == 12)
0146 mci->aggr_limit = 8;
0147 else if (info->T == 6) {
0148 mci->aggr_limit = 6;
0149 btcoex->duty_cycle = 30;
0150 } else
0151 mci->aggr_limit = 6;
0152 ath_dbg(common, MCI,
0153 "Single SCO, aggregation limit %d 1/4 ms\n",
0154 mci->aggr_limit);
0155 } else if (mci->num_pan || mci->num_other_acl) {
0156
0157
0158
0159
0160 btcoex->duty_cycle = AR_SREV_9565(sc->sc_ah) ? 40 : 35;
0161 btcoex->btcoex_period = 53;
0162 ath_dbg(common, MCI,
0163 "Single PAN/FTP bt period %d ms dutycycle %d\n",
0164 btcoex->duty_cycle, btcoex->btcoex_period);
0165 } else if (mci->num_hid) {
0166 btcoex->duty_cycle = 30;
0167 mci->aggr_limit = 6;
0168 ath_dbg(common, MCI,
0169 "Multiple attempt/timeout single HID "
0170 "aggregation limit 1.5 ms dutycycle 30%%\n");
0171 }
0172 } else if (num_profile == 2) {
0173 if (mci->num_hid == 2)
0174 btcoex->duty_cycle = 30;
0175 mci->aggr_limit = 6;
0176 ath_dbg(common, MCI,
0177 "Two BT profiles aggr limit 1.5 ms dutycycle %d%%\n",
0178 btcoex->duty_cycle);
0179 } else if (num_profile >= 3) {
0180 mci->aggr_limit = 4;
0181 ath_dbg(common, MCI,
0182 "Three or more profiles aggregation limit 1 ms\n");
0183 }
0184
0185 skip_tuning:
0186 if (IS_CHAN_2GHZ(sc->sc_ah->curchan)) {
0187 if (IS_CHAN_HT(sc->sc_ah->curchan))
0188 ath_mci_adjust_aggr_limit(btcoex);
0189 else
0190 btcoex->btcoex_period >>= 1;
0191 }
0192
0193 ath9k_btcoex_timer_pause(sc);
0194 ath9k_hw_btcoex_disable(sc->sc_ah);
0195
0196 if (IS_CHAN_5GHZ(sc->sc_ah->curchan))
0197 return;
0198
0199 btcoex->duty_cycle += (mci->num_bdr ? ATH_MCI_BDR_DUTY_CYCLE : 0);
0200 if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE)
0201 btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE;
0202
0203 btcoex->btcoex_no_stomp = btcoex->btcoex_period *
0204 (100 - btcoex->duty_cycle) / 100;
0205
0206 ath9k_hw_btcoex_enable(sc->sc_ah);
0207 ath9k_btcoex_timer_resume(sc);
0208 }
0209
0210 static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload)
0211 {
0212 struct ath_hw *ah = sc->sc_ah;
0213 struct ath_common *common = ath9k_hw_common(ah);
0214 struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci;
0215 u32 payload[4] = {0, 0, 0, 0};
0216
0217 switch (opcode) {
0218 case MCI_GPM_BT_CAL_REQ:
0219 if (mci_hw->bt_state == MCI_BT_AWAKE) {
0220 mci_hw->bt_state = MCI_BT_CAL_START;
0221 ath9k_queue_reset(sc, RESET_TYPE_MCI);
0222 }
0223 ath_dbg(common, MCI, "MCI State : %d\n", mci_hw->bt_state);
0224 break;
0225 case MCI_GPM_BT_CAL_GRANT:
0226 MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE);
0227 ar9003_mci_send_message(sc->sc_ah, MCI_GPM, 0, payload,
0228 16, false, true);
0229 break;
0230 default:
0231 ath_dbg(common, MCI, "Unknown GPM CAL message\n");
0232 break;
0233 }
0234 }
0235
0236 static void ath9k_mci_work(struct work_struct *work)
0237 {
0238 struct ath_softc *sc = container_of(work, struct ath_softc, mci_work);
0239
0240 ath_mci_update_scheme(sc);
0241 }
0242
0243 static void ath_mci_update_stomp_txprio(u8 cur_txprio, u8 *stomp_prio)
0244 {
0245 if (cur_txprio < stomp_prio[ATH_BTCOEX_STOMP_NONE])
0246 stomp_prio[ATH_BTCOEX_STOMP_NONE] = cur_txprio;
0247
0248 if (cur_txprio > stomp_prio[ATH_BTCOEX_STOMP_ALL])
0249 stomp_prio[ATH_BTCOEX_STOMP_ALL] = cur_txprio;
0250
0251 if ((cur_txprio > ATH_MCI_HI_PRIO) &&
0252 (cur_txprio < stomp_prio[ATH_BTCOEX_STOMP_LOW]))
0253 stomp_prio[ATH_BTCOEX_STOMP_LOW] = cur_txprio;
0254 }
0255
0256 static void ath_mci_set_concur_txprio(struct ath_softc *sc)
0257 {
0258 struct ath_btcoex *btcoex = &sc->btcoex;
0259 struct ath_mci_profile *mci = &btcoex->mci;
0260 u8 stomp_txprio[ATH_BTCOEX_STOMP_MAX];
0261
0262 memset(stomp_txprio, 0, sizeof(stomp_txprio));
0263 if (mci->num_mgmt) {
0264 stomp_txprio[ATH_BTCOEX_STOMP_ALL] = ATH_MCI_INQUIRY_PRIO;
0265 if (!mci->num_pan && !mci->num_other_acl)
0266 stomp_txprio[ATH_BTCOEX_STOMP_NONE] =
0267 ATH_MCI_INQUIRY_PRIO;
0268 } else {
0269 u8 prof_prio[] = { 50, 90, 94, 52 };
0270
0271 stomp_txprio[ATH_BTCOEX_STOMP_LOW] =
0272 stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 0xff;
0273
0274 if (mci->num_sco)
0275 ath_mci_update_stomp_txprio(mci->voice_priority,
0276 stomp_txprio);
0277 if (mci->num_other_acl)
0278 ath_mci_update_stomp_txprio(prof_prio[0], stomp_txprio);
0279 if (mci->num_a2dp)
0280 ath_mci_update_stomp_txprio(prof_prio[1], stomp_txprio);
0281 if (mci->num_hid)
0282 ath_mci_update_stomp_txprio(prof_prio[2], stomp_txprio);
0283 if (mci->num_pan)
0284 ath_mci_update_stomp_txprio(prof_prio[3], stomp_txprio);
0285
0286 if (stomp_txprio[ATH_BTCOEX_STOMP_NONE] == 0xff)
0287 stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 0;
0288
0289 if (stomp_txprio[ATH_BTCOEX_STOMP_LOW] == 0xff)
0290 stomp_txprio[ATH_BTCOEX_STOMP_LOW] = 0;
0291 }
0292 ath9k_hw_btcoex_set_concur_txprio(sc->sc_ah, stomp_txprio);
0293 }
0294
0295 static u8 ath_mci_process_profile(struct ath_softc *sc,
0296 struct ath_mci_profile_info *info)
0297 {
0298 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
0299 struct ath_btcoex *btcoex = &sc->btcoex;
0300 struct ath_mci_profile *mci = &btcoex->mci;
0301 struct ath_mci_profile_info *entry = NULL;
0302
0303 entry = ath_mci_find_profile(mci, info);
0304 if (entry) {
0305
0306
0307
0308
0309
0310
0311
0312
0313 if (entry->type != info->type) {
0314 DEC_PROF(mci, entry);
0315 INC_PROF(mci, info);
0316 }
0317 memcpy(entry, info, 10);
0318 }
0319
0320 if (info->start) {
0321 if (!entry && !ath_mci_add_profile(common, mci, info))
0322 return 0;
0323 } else
0324 ath_mci_del_profile(common, mci, entry);
0325
0326 ath_mci_set_concur_txprio(sc);
0327 return 1;
0328 }
0329
0330 static u8 ath_mci_process_status(struct ath_softc *sc,
0331 struct ath_mci_profile_status *status)
0332 {
0333 struct ath_btcoex *btcoex = &sc->btcoex;
0334 struct ath_mci_profile *mci = &btcoex->mci;
0335 struct ath_mci_profile_info info;
0336 int i = 0, old_num_mgmt = mci->num_mgmt;
0337
0338
0339 if (status->is_link)
0340 return 0;
0341
0342 info.conn_handle = status->conn_handle;
0343 if (ath_mci_find_profile(mci, &info))
0344 return 0;
0345
0346 if (status->conn_handle >= ATH_MCI_MAX_PROFILE)
0347 return 0;
0348
0349 if (status->is_critical)
0350 __set_bit(status->conn_handle, mci->status);
0351 else
0352 __clear_bit(status->conn_handle, mci->status);
0353
0354 mci->num_mgmt = 0;
0355 do {
0356 if (test_bit(i, mci->status))
0357 mci->num_mgmt++;
0358 } while (++i < ATH_MCI_MAX_PROFILE);
0359
0360 ath_mci_set_concur_txprio(sc);
0361 if (old_num_mgmt != mci->num_mgmt)
0362 return 1;
0363
0364 return 0;
0365 }
0366
0367 static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload)
0368 {
0369 struct ath_hw *ah = sc->sc_ah;
0370 struct ath_mci_profile_info profile_info;
0371 struct ath_mci_profile_status profile_status;
0372 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
0373 u8 major, minor, update_scheme = 0;
0374 u32 seq_num;
0375
0376 if (ar9003_mci_state(ah, MCI_STATE_NEED_FLUSH_BT_INFO) &&
0377 ar9003_mci_state(ah, MCI_STATE_ENABLE)) {
0378 ath_dbg(common, MCI, "(MCI) Need to flush BT profiles\n");
0379 ath_mci_flush_profile(&sc->btcoex.mci);
0380 ar9003_mci_state(ah, MCI_STATE_SEND_STATUS_QUERY);
0381 }
0382
0383 switch (opcode) {
0384 case MCI_GPM_COEX_VERSION_QUERY:
0385 ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_COEX_VERSION);
0386 break;
0387 case MCI_GPM_COEX_VERSION_RESPONSE:
0388 major = *(rx_payload + MCI_GPM_COEX_B_MAJOR_VERSION);
0389 minor = *(rx_payload + MCI_GPM_COEX_B_MINOR_VERSION);
0390 ar9003_mci_set_bt_version(ah, major, minor);
0391 break;
0392 case MCI_GPM_COEX_STATUS_QUERY:
0393 ar9003_mci_send_wlan_channels(ah);
0394 break;
0395 case MCI_GPM_COEX_BT_PROFILE_INFO:
0396 memcpy(&profile_info,
0397 (rx_payload + MCI_GPM_COEX_B_PROFILE_TYPE), 10);
0398
0399 if ((profile_info.type == MCI_GPM_COEX_PROFILE_UNKNOWN) ||
0400 (profile_info.type >= MCI_GPM_COEX_PROFILE_MAX)) {
0401 ath_dbg(common, MCI,
0402 "Illegal profile type = %d, state = %d\n",
0403 profile_info.type,
0404 profile_info.start);
0405 break;
0406 }
0407
0408 update_scheme += ath_mci_process_profile(sc, &profile_info);
0409 break;
0410 case MCI_GPM_COEX_BT_STATUS_UPDATE:
0411 profile_status.is_link = *(rx_payload +
0412 MCI_GPM_COEX_B_STATUS_TYPE);
0413 profile_status.conn_handle = *(rx_payload +
0414 MCI_GPM_COEX_B_STATUS_LINKID);
0415 profile_status.is_critical = *(rx_payload +
0416 MCI_GPM_COEX_B_STATUS_STATE);
0417
0418 seq_num = *((u32 *)(rx_payload + 12));
0419 ath_dbg(common, MCI,
0420 "BT_Status_Update: is_link=%d, linkId=%d, state=%d, SEQ=%u\n",
0421 profile_status.is_link, profile_status.conn_handle,
0422 profile_status.is_critical, seq_num);
0423
0424 update_scheme += ath_mci_process_status(sc, &profile_status);
0425 break;
0426 default:
0427 ath_dbg(common, MCI, "Unknown GPM COEX message = 0x%02x\n", opcode);
0428 break;
0429 }
0430 if (update_scheme)
0431 ieee80211_queue_work(sc->hw, &sc->mci_work);
0432 }
0433
0434 int ath_mci_setup(struct ath_softc *sc)
0435 {
0436 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
0437 struct ath_mci_coex *mci = &sc->mci_coex;
0438 struct ath_mci_buf *buf = &mci->sched_buf;
0439 int ret;
0440
0441 buf->bf_addr = dmam_alloc_coherent(sc->dev,
0442 ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE,
0443 &buf->bf_paddr, GFP_KERNEL);
0444
0445 if (buf->bf_addr == NULL) {
0446 ath_dbg(common, FATAL, "MCI buffer alloc failed\n");
0447 return -ENOMEM;
0448 }
0449
0450 memset(buf->bf_addr, MCI_GPM_RSVD_PATTERN,
0451 ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE);
0452
0453 mci->sched_buf.bf_len = ATH_MCI_SCHED_BUF_SIZE;
0454
0455 mci->gpm_buf.bf_len = ATH_MCI_GPM_BUF_SIZE;
0456 mci->gpm_buf.bf_addr = mci->sched_buf.bf_addr + mci->sched_buf.bf_len;
0457 mci->gpm_buf.bf_paddr = mci->sched_buf.bf_paddr + mci->sched_buf.bf_len;
0458
0459 ret = ar9003_mci_setup(sc->sc_ah, mci->gpm_buf.bf_paddr,
0460 mci->gpm_buf.bf_addr, (mci->gpm_buf.bf_len >> 4),
0461 mci->sched_buf.bf_paddr);
0462 if (ret) {
0463 ath_err(common, "Failed to initialize MCI\n");
0464 return ret;
0465 }
0466
0467 INIT_WORK(&sc->mci_work, ath9k_mci_work);
0468 ath_dbg(common, MCI, "MCI Initialized\n");
0469
0470 return 0;
0471 }
0472
0473 void ath_mci_cleanup(struct ath_softc *sc)
0474 {
0475 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
0476 struct ath_hw *ah = sc->sc_ah;
0477
0478 ar9003_mci_cleanup(ah);
0479
0480 ath_dbg(common, MCI, "MCI De-Initialized\n");
0481 }
0482
0483 void ath_mci_intr(struct ath_softc *sc)
0484 {
0485 struct ath_mci_coex *mci = &sc->mci_coex;
0486 struct ath_hw *ah = sc->sc_ah;
0487 struct ath_common *common = ath9k_hw_common(ah);
0488 struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci;
0489 u32 mci_int, mci_int_rxmsg;
0490 u32 offset, subtype, opcode;
0491 u32 *pgpm;
0492 u32 more_data = MCI_GPM_MORE;
0493 bool skip_gpm = false;
0494
0495 ar9003_mci_get_interrupt(sc->sc_ah, &mci_int, &mci_int_rxmsg);
0496
0497 if (ar9003_mci_state(ah, MCI_STATE_ENABLE) == 0) {
0498 ar9003_mci_state(ah, MCI_STATE_INIT_GPM_OFFSET);
0499 return;
0500 }
0501
0502 if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE) {
0503 u32 payload[4] = { 0xffffffff, 0xffffffff,
0504 0xffffffff, 0xffffff00};
0505
0506
0507
0508
0509
0510
0511 ar9003_mci_send_message(ah, MCI_REMOTE_RESET, 0,
0512 payload, 16, true, false);
0513 ar9003_mci_send_message(ah, MCI_SYS_WAKING, 0,
0514 NULL, 0, true, false);
0515
0516 mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE;
0517 ar9003_mci_state(ah, MCI_STATE_RESET_REQ_WAKE);
0518
0519
0520
0521
0522 ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE);
0523 }
0524
0525 if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING) {
0526 mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING;
0527
0528 if ((mci_hw->bt_state == MCI_BT_SLEEP) &&
0529 (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP) !=
0530 MCI_BT_SLEEP))
0531 ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE);
0532 }
0533
0534 if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) {
0535 mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING;
0536
0537 if ((mci_hw->bt_state == MCI_BT_AWAKE) &&
0538 (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP) !=
0539 MCI_BT_AWAKE))
0540 mci_hw->bt_state = MCI_BT_SLEEP;
0541 }
0542
0543 if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) ||
0544 (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) {
0545 ar9003_mci_state(ah, MCI_STATE_RECOVER_RX);
0546 skip_gpm = true;
0547 }
0548
0549 if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO) {
0550 mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO;
0551 ar9003_mci_state(ah, MCI_STATE_LAST_SCHD_MSG_OFFSET);
0552 }
0553
0554 if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_GPM) {
0555 mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_GPM;
0556
0557 while (more_data == MCI_GPM_MORE) {
0558 if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
0559 return;
0560
0561 pgpm = mci->gpm_buf.bf_addr;
0562 offset = ar9003_mci_get_next_gpm_offset(ah, &more_data);
0563
0564 if (offset == MCI_GPM_INVALID)
0565 break;
0566
0567 pgpm += (offset >> 2);
0568
0569
0570
0571
0572
0573 subtype = MCI_GPM_TYPE(pgpm);
0574 opcode = MCI_GPM_OPCODE(pgpm);
0575
0576 if (skip_gpm)
0577 goto recycle;
0578
0579 if (MCI_GPM_IS_CAL_TYPE(subtype)) {
0580 ath_mci_cal_msg(sc, subtype, (u8 *)pgpm);
0581 } else {
0582 switch (subtype) {
0583 case MCI_GPM_COEX_AGENT:
0584 ath_mci_msg(sc, opcode, (u8 *)pgpm);
0585 break;
0586 default:
0587 break;
0588 }
0589 }
0590 recycle:
0591 MCI_GPM_RECYCLE(pgpm);
0592 }
0593 }
0594
0595 if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_HW_MSG_MASK) {
0596 if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL)
0597 mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_LNA_CONTROL;
0598
0599 if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_LNA_INFO)
0600 mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_LNA_INFO;
0601
0602 if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_INFO) {
0603 int value_dbm = MS(mci_hw->cont_status,
0604 AR_MCI_CONT_RSSI_POWER);
0605
0606 mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_INFO;
0607
0608 ath_dbg(common, MCI,
0609 "MCI CONT_INFO: (%s) pri = %d pwr = %d dBm\n",
0610 MS(mci_hw->cont_status, AR_MCI_CONT_TXRX) ?
0611 "tx" : "rx",
0612 MS(mci_hw->cont_status, AR_MCI_CONT_PRIORITY),
0613 value_dbm);
0614 }
0615
0616 if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_NACK)
0617 mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_NACK;
0618
0619 if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_RST)
0620 mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_RST;
0621 }
0622
0623 if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) ||
0624 (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) {
0625 mci_int &= ~(AR_MCI_INTERRUPT_RX_INVALID_HDR |
0626 AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT);
0627 ath_mci_msg(sc, MCI_GPM_COEX_NOOP, NULL);
0628 }
0629 }
0630
0631 void ath_mci_enable(struct ath_softc *sc)
0632 {
0633 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
0634
0635 if (!common->btcoex_enabled)
0636 return;
0637
0638 if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
0639 sc->sc_ah->imask |= ATH9K_INT_MCI;
0640 }
0641
0642 void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all)
0643 {
0644 struct ath_hw *ah = sc->sc_ah;
0645 struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
0646 struct ath9k_channel *chan = ah->curchan;
0647 u32 channelmap[] = {0x00000000, 0xffff0000, 0xffffffff, 0x7fffffff};
0648 int i;
0649 s16 chan_start, chan_end;
0650 u16 wlan_chan;
0651
0652 if (!chan || !IS_CHAN_2GHZ(chan))
0653 return;
0654
0655 if (allow_all)
0656 goto send_wlan_chan;
0657
0658 wlan_chan = chan->channel - 2402;
0659
0660 chan_start = wlan_chan - 10;
0661 chan_end = wlan_chan + 10;
0662
0663 if (IS_CHAN_HT40PLUS(chan))
0664 chan_end += 20;
0665 else if (IS_CHAN_HT40MINUS(chan))
0666 chan_start -= 20;
0667
0668
0669 chan_start -= 7;
0670 chan_end += 7;
0671
0672 if (chan_start <= 0)
0673 chan_start = 0;
0674 if (chan_end >= ATH_MCI_NUM_BT_CHANNELS)
0675 chan_end = ATH_MCI_NUM_BT_CHANNELS - 1;
0676
0677 ath_dbg(ath9k_hw_common(ah), MCI,
0678 "WLAN current channel %d mask BT channel %d - %d\n",
0679 wlan_chan, chan_start, chan_end);
0680
0681 for (i = chan_start; i < chan_end; i++)
0682 MCI_GPM_CLR_CHANNEL_BIT(&channelmap, i);
0683
0684 send_wlan_chan:
0685
0686 for (i = 0; i < 4; i++)
0687 mci->wlan_channels[i] = channelmap[i];
0688 ar9003_mci_send_wlan_channels(ah);
0689 ar9003_mci_state(ah, MCI_STATE_SEND_VERSION_QUERY);
0690 }
0691
0692 void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
0693 bool concur_tx)
0694 {
0695 struct ath_hw *ah = sc->sc_ah;
0696 struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci;
0697 bool old_concur_tx = mci_hw->concur_tx;
0698
0699 if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX)) {
0700 mci_hw->concur_tx = false;
0701 return;
0702 }
0703
0704 if (!IS_CHAN_2GHZ(ah->curchan))
0705 return;
0706
0707 if (setchannel) {
0708 struct ath9k_hw_cal_data *caldata = &sc->cur_chan->caldata;
0709 if (IS_CHAN_HT40PLUS(ah->curchan) &&
0710 (ah->curchan->channel > caldata->channel) &&
0711 (ah->curchan->channel <= caldata->channel + 20))
0712 return;
0713 if (IS_CHAN_HT40MINUS(ah->curchan) &&
0714 (ah->curchan->channel < caldata->channel) &&
0715 (ah->curchan->channel >= caldata->channel - 20))
0716 return;
0717 mci_hw->concur_tx = false;
0718 } else
0719 mci_hw->concur_tx = concur_tx;
0720
0721 if (old_concur_tx != mci_hw->concur_tx)
0722 ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
0723 }
0724
0725 static void ath9k_mci_stomp_audio(struct ath_softc *sc)
0726 {
0727 struct ath_hw *ah = sc->sc_ah;
0728 struct ath_btcoex *btcoex = &sc->btcoex;
0729 struct ath_mci_profile *mci = &btcoex->mci;
0730
0731 if (!mci->num_sco && !mci->num_a2dp)
0732 return;
0733
0734 if (ah->stats.avgbrssi > 25) {
0735 btcoex->stomp_audio = 0;
0736 return;
0737 }
0738
0739 btcoex->stomp_audio++;
0740 }
0741 void ath9k_mci_update_rssi(struct ath_softc *sc)
0742 {
0743 struct ath_hw *ah = sc->sc_ah;
0744 struct ath_btcoex *btcoex = &sc->btcoex;
0745 struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci;
0746
0747 ath9k_mci_stomp_audio(sc);
0748
0749 if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX))
0750 return;
0751
0752 if (ah->stats.avgbrssi >= 40) {
0753 if (btcoex->rssi_count < 0)
0754 btcoex->rssi_count = 0;
0755 if (++btcoex->rssi_count >= ATH_MCI_CONCUR_TX_SWITCH) {
0756 btcoex->rssi_count = 0;
0757 ath9k_mci_set_txpower(sc, false, true);
0758 }
0759 } else {
0760 if (btcoex->rssi_count > 0)
0761 btcoex->rssi_count = 0;
0762 if (--btcoex->rssi_count <= -ATH_MCI_CONCUR_TX_SWITCH) {
0763 btcoex->rssi_count = 0;
0764 ath9k_mci_set_txpower(sc, false, false);
0765 }
0766 }
0767 }