diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/mci.c')
| -rw-r--r-- | drivers/net/wireless/ath/ath9k/mci.c | 278 |
1 files changed, 239 insertions, 39 deletions
diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index fb536e7e661..a0dbcc41238 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -43,6 +43,7 @@ static bool ath_mci_add_profile(struct ath_common *common, struct ath_mci_profile_info *info) { struct ath_mci_profile_info *entry; + u8 voice_priority[] = { 110, 110, 110, 112, 110, 110, 114, 116, 118 }; if ((mci->num_sco == ATH_MCI_MAX_SCO_PROFILE) && (info->type == MCI_GPM_COEX_PROFILE_VOICE)) @@ -59,6 +60,12 @@ static bool ath_mci_add_profile(struct ath_common *common, memcpy(entry, info, 10); INC_PROF(mci, info); list_add_tail(&entry->list, &mci->info); + if (info->type == MCI_GPM_COEX_PROFILE_VOICE) { + if (info->voice_type < sizeof(voice_priority)) + mci->voice_priority = voice_priority[info->voice_type]; + else + mci->voice_priority = 110; + } return true; } @@ -80,6 +87,7 @@ void ath_mci_flush_profile(struct ath_mci_profile *mci) struct ath_mci_profile_info *info, *tinfo; mci->aggr_limit = 0; + mci->num_mgmt = 0; if (list_empty(&mci->info)) return; @@ -120,7 +128,14 @@ static void ath_mci_update_scheme(struct ath_softc *sc) if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_TUNING) goto skip_tuning; + mci->aggr_limit = 0; btcoex->duty_cycle = ath_mci_duty_cycle[num_profile]; + btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD; + if (NUM_PROF(mci)) + btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; + else + btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL : + ATH_BTCOEX_STOMP_LOW; if (num_profile == 1) { info = list_first_entry(&mci->info, @@ -132,7 +147,8 @@ static void ath_mci_update_scheme(struct ath_softc *sc) else if (info->T == 6) { mci->aggr_limit = 6; btcoex->duty_cycle = 30; - } + } else + mci->aggr_limit = 6; ath_dbg(common, MCI, "Single SCO, aggregation limit %d 1/4 ms\n", mci->aggr_limit); @@ -141,7 +157,7 @@ static void ath_mci_update_scheme(struct ath_softc *sc) * For single PAN/FTP profile, allocate 35% for BT * to improve WLAN throughput. */ - btcoex->duty_cycle = 35; + btcoex->duty_cycle = AR_SREV_9565(sc->sc_ah) ? 40 : 35; btcoex->btcoex_period = 53; ath_dbg(common, MCI, "Single PAN/FTP bt period %d ms dutycycle %d\n", @@ -184,7 +200,7 @@ skip_tuning: if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE) btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE; - btcoex->btcoex_no_stomp = btcoex->btcoex_period * 1000 * + btcoex->btcoex_no_stomp = btcoex->btcoex_period * (100 - btcoex->duty_cycle) / 100; ath9k_hw_btcoex_enable(sc->sc_ah); @@ -201,7 +217,7 @@ static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) switch (opcode) { case MCI_GPM_BT_CAL_REQ: if (mci_hw->bt_state == MCI_BT_AWAKE) { - ar9003_mci_state(ah, MCI_STATE_SET_BT_CAL_START); + mci_hw->bt_state = MCI_BT_CAL_START; ath9k_queue_reset(sc, RESET_TYPE_MCI); } ath_dbg(common, MCI, "MCI State : %d\n", mci_hw->bt_state); @@ -224,8 +240,60 @@ static void ath9k_mci_work(struct work_struct *work) ath_mci_update_scheme(sc); } -static void ath_mci_process_profile(struct ath_softc *sc, - struct ath_mci_profile_info *info) +static void ath_mci_update_stomp_txprio(u8 cur_txprio, u8 *stomp_prio) +{ + if (cur_txprio < stomp_prio[ATH_BTCOEX_STOMP_NONE]) + stomp_prio[ATH_BTCOEX_STOMP_NONE] = cur_txprio; + + if (cur_txprio > stomp_prio[ATH_BTCOEX_STOMP_ALL]) + stomp_prio[ATH_BTCOEX_STOMP_ALL] = cur_txprio; + + if ((cur_txprio > ATH_MCI_HI_PRIO) && + (cur_txprio < stomp_prio[ATH_BTCOEX_STOMP_LOW])) + stomp_prio[ATH_BTCOEX_STOMP_LOW] = cur_txprio; +} + +static void ath_mci_set_concur_txprio(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_mci_profile *mci = &btcoex->mci; + u8 stomp_txprio[ATH_BTCOEX_STOMP_MAX]; + + memset(stomp_txprio, 0, sizeof(stomp_txprio)); + if (mci->num_mgmt) { + stomp_txprio[ATH_BTCOEX_STOMP_ALL] = ATH_MCI_INQUIRY_PRIO; + if (!mci->num_pan && !mci->num_other_acl) + stomp_txprio[ATH_BTCOEX_STOMP_NONE] = + ATH_MCI_INQUIRY_PRIO; + } else { + u8 prof_prio[] = { 50, 90, 94, 52 };/* RFCOMM, A2DP, HID, PAN */ + + stomp_txprio[ATH_BTCOEX_STOMP_LOW] = + stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 0xff; + + if (mci->num_sco) + ath_mci_update_stomp_txprio(mci->voice_priority, + stomp_txprio); + if (mci->num_other_acl) + ath_mci_update_stomp_txprio(prof_prio[0], stomp_txprio); + if (mci->num_a2dp) + ath_mci_update_stomp_txprio(prof_prio[1], stomp_txprio); + if (mci->num_hid) + ath_mci_update_stomp_txprio(prof_prio[2], stomp_txprio); + if (mci->num_pan) + ath_mci_update_stomp_txprio(prof_prio[3], stomp_txprio); + + if (stomp_txprio[ATH_BTCOEX_STOMP_NONE] == 0xff) + stomp_txprio[ATH_BTCOEX_STOMP_NONE] = 0; + + if (stomp_txprio[ATH_BTCOEX_STOMP_LOW] == 0xff) + stomp_txprio[ATH_BTCOEX_STOMP_LOW] = 0; + } + ath9k_hw_btcoex_set_concur_txprio(sc->sc_ah, stomp_txprio); +} + +static u8 ath_mci_process_profile(struct ath_softc *sc, + struct ath_mci_profile_info *info) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_btcoex *btcoex = &sc->btcoex; @@ -251,25 +319,16 @@ static void ath_mci_process_profile(struct ath_softc *sc, if (info->start) { if (!entry && !ath_mci_add_profile(common, mci, info)) - return; + return 0; } else ath_mci_del_profile(common, mci, entry); - btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD; - mci->aggr_limit = mci->num_sco ? 6 : 0; - - btcoex->duty_cycle = ath_mci_duty_cycle[NUM_PROF(mci)]; - if (NUM_PROF(mci)) - btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; - else - btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL : - ATH_BTCOEX_STOMP_LOW; - - ieee80211_queue_work(sc->hw, &sc->mci_work); + ath_mci_set_concur_txprio(sc); + return 1; } -static void ath_mci_process_status(struct ath_softc *sc, - struct ath_mci_profile_status *status) +static u8 ath_mci_process_status(struct ath_softc *sc, + struct ath_mci_profile_status *status) { struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &btcoex->mci; @@ -278,14 +337,14 @@ static void ath_mci_process_status(struct ath_softc *sc, /* Link status type are not handled */ if (status->is_link) - return; + return 0; info.conn_handle = status->conn_handle; if (ath_mci_find_profile(mci, &info)) - return; + return 0; if (status->conn_handle >= ATH_MCI_MAX_PROFILE) - return; + return 0; if (status->is_critical) __set_bit(status->conn_handle, mci->status); @@ -298,8 +357,11 @@ static void ath_mci_process_status(struct ath_softc *sc, mci->num_mgmt++; } while (++i < ATH_MCI_MAX_PROFILE); + ath_mci_set_concur_txprio(sc); if (old_num_mgmt != mci->num_mgmt) - ieee80211_queue_work(sc->hw, &sc->mci_work); + return 1; + + return 0; } static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) @@ -308,9 +370,16 @@ static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) struct ath_mci_profile_info profile_info; struct ath_mci_profile_status profile_status; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - u8 major, minor; + u8 major, minor, update_scheme = 0; u32 seq_num; + if (ar9003_mci_state(ah, MCI_STATE_NEED_FLUSH_BT_INFO) && + ar9003_mci_state(ah, MCI_STATE_ENABLE)) { + ath_dbg(common, MCI, "(MCI) Need to flush BT profiles\n"); + ath_mci_flush_profile(&sc->btcoex.mci); + ar9003_mci_state(ah, MCI_STATE_SEND_STATUS_QUERY); + } + switch (opcode) { case MCI_GPM_COEX_VERSION_QUERY: ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_COEX_VERSION); @@ -336,7 +405,7 @@ static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) break; } - ath_mci_process_profile(sc, &profile_info); + update_scheme += ath_mci_process_profile(sc, &profile_info); break; case MCI_GPM_COEX_BT_STATUS_UPDATE: profile_status.is_link = *(rx_payload + @@ -352,12 +421,14 @@ static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) profile_status.is_link, profile_status.conn_handle, profile_status.is_critical, seq_num); - ath_mci_process_status(sc, &profile_status); + update_scheme += ath_mci_process_status(sc, &profile_status); break; default: ath_dbg(common, MCI, "Unknown GPM COEX message = 0x%02x\n", opcode); break; } + if (update_scheme) + ieee80211_queue_work(sc->hw, &sc->mci_work); } int ath_mci_setup(struct ath_softc *sc) @@ -365,8 +436,9 @@ int ath_mci_setup(struct ath_softc *sc) struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_mci_coex *mci = &sc->mci_coex; struct ath_mci_buf *buf = &mci->sched_buf; + int ret; - buf->bf_addr = dma_alloc_coherent(sc->dev, + buf->bf_addr = dmam_alloc_coherent(sc->dev, ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE, &buf->bf_paddr, GFP_KERNEL); @@ -384,9 +456,13 @@ int ath_mci_setup(struct ath_softc *sc) mci->gpm_buf.bf_addr = (u8 *)mci->sched_buf.bf_addr + mci->sched_buf.bf_len; mci->gpm_buf.bf_paddr = mci->sched_buf.bf_paddr + mci->sched_buf.bf_len; - ar9003_mci_setup(sc->sc_ah, mci->gpm_buf.bf_paddr, - mci->gpm_buf.bf_addr, (mci->gpm_buf.bf_len >> 4), - mci->sched_buf.bf_paddr); + ret = ar9003_mci_setup(sc->sc_ah, mci->gpm_buf.bf_paddr, + mci->gpm_buf.bf_addr, (mci->gpm_buf.bf_len >> 4), + mci->sched_buf.bf_paddr); + if (ret) { + ath_err(common, "Failed to initialize MCI\n"); + return ret; + } INIT_WORK(&sc->mci_work, ath9k_mci_work); ath_dbg(common, MCI, "MCI Initialized\n"); @@ -398,13 +474,6 @@ void ath_mci_cleanup(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_hw *ah = sc->sc_ah; - struct ath_mci_coex *mci = &sc->mci_coex; - struct ath_mci_buf *buf = &mci->sched_buf; - - if (buf->bf_addr) - dma_free_coherent(sc->dev, - ATH_MCI_SCHED_BUF_SIZE + ATH_MCI_GPM_BUF_SIZE, - buf->bf_addr, buf->bf_paddr); ar9003_mci_cleanup(ah); @@ -486,6 +555,8 @@ void ath_mci_intr(struct ath_softc *sc) mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_GPM; while (more_data == MCI_GPM_MORE) { + if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) + return; pgpm = mci->gpm_buf.bf_addr; offset = ar9003_mci_get_next_gpm_offset(ah, false, @@ -551,9 +622,11 @@ void ath_mci_intr(struct ath_softc *sc) } if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) || - (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) + (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) { mci_int &= ~(AR_MCI_INTERRUPT_RX_INVALID_HDR | AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT); + ath_mci_msg(sc, MCI_GPM_COEX_NOOP, NULL); + } } void ath_mci_enable(struct ath_softc *sc) @@ -566,3 +639,130 @@ void ath_mci_enable(struct ath_softc *sc) if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) sc->sc_ah->imask |= ATH9K_INT_MCI; } + +void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; + struct ath9k_channel *chan = ah->curchan; + u32 channelmap[] = {0x00000000, 0xffff0000, 0xffffffff, 0x7fffffff}; + int i; + s16 chan_start, chan_end; + u16 wlan_chan; + + if (!chan || !IS_CHAN_2GHZ(chan)) + return; + + if (allow_all) + goto send_wlan_chan; + + wlan_chan = chan->channel - 2402; + + chan_start = wlan_chan - 10; + chan_end = wlan_chan + 10; + + if (IS_CHAN_HT40PLUS(chan)) + chan_end += 20; + else if (IS_CHAN_HT40MINUS(chan)) + chan_start -= 20; + + /* adjust side band */ + chan_start -= 7; + chan_end += 7; + + if (chan_start <= 0) + chan_start = 0; + if (chan_end >= ATH_MCI_NUM_BT_CHANNELS) + chan_end = ATH_MCI_NUM_BT_CHANNELS - 1; + + ath_dbg(ath9k_hw_common(ah), MCI, + "WLAN current channel %d mask BT channel %d - %d\n", + wlan_chan, chan_start, chan_end); + + for (i = chan_start; i < chan_end; i++) + MCI_GPM_CLR_CHANNEL_BIT(&channelmap, i); + +send_wlan_chan: + /* update and send wlan channels info to BT */ + for (i = 0; i < 4; i++) + mci->wlan_channels[i] = channelmap[i]; + ar9003_mci_send_wlan_channels(ah); + ar9003_mci_state(ah, MCI_STATE_SEND_VERSION_QUERY); +} + +void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, + bool concur_tx) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci; + bool old_concur_tx = mci_hw->concur_tx; + + if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX)) { + mci_hw->concur_tx = false; + return; + } + + if (!IS_CHAN_2GHZ(ah->curchan)) + return; + + if (setchannel) { + struct ath9k_hw_cal_data *caldata = &sc->caldata; + if (IS_CHAN_HT40PLUS(ah->curchan) && + (ah->curchan->channel > caldata->channel) && + (ah->curchan->channel <= caldata->channel + 20)) + return; + if (IS_CHAN_HT40MINUS(ah->curchan) && + (ah->curchan->channel < caldata->channel) && + (ah->curchan->channel >= caldata->channel - 20)) + return; + mci_hw->concur_tx = false; + } else + mci_hw->concur_tx = concur_tx; + + if (old_concur_tx != mci_hw->concur_tx) + ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false); +} + +static void ath9k_mci_stomp_audio(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_mci_profile *mci = &btcoex->mci; + + if (!mci->num_sco && !mci->num_a2dp) + return; + + if (ah->stats.avgbrssi > 25) { + btcoex->stomp_audio = 0; + return; + } + + btcoex->stomp_audio++; +} +void ath9k_mci_update_rssi(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci; + + ath9k_mci_stomp_audio(sc); + + if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX)) + return; + + if (ah->stats.avgbrssi >= 40) { + if (btcoex->rssi_count < 0) + btcoex->rssi_count = 0; + if (++btcoex->rssi_count >= ATH_MCI_CONCUR_TX_SWITCH) { + btcoex->rssi_count = 0; + ath9k_mci_set_txpower(sc, false, true); + } + } else { + if (btcoex->rssi_count > 0) + btcoex->rssi_count = 0; + if (--btcoex->rssi_count <= -ATH_MCI_CONCUR_TX_SWITCH) { + btcoex->rssi_count = 0; + ath9k_mci_set_txpower(sc, false, false); + } + } +} |
