u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, bool first, u32 *more) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 offset, more_gpm = 0, gpm_ptr; if (first) { gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); mci->gpm_idx = gpm_ptr; return gpm_ptr; } /* * This could be useful to avoid new GPM message interrupt which * may lead to spurious interrupt after power sleep, or multiple * entry of ath_mci_intr(). * Adding empty GPM check by returning HAL_MCI_GPM_INVALID can * alleviate this effect, but clearing GPM RX interrupt bit is * safe, because whether this is called from hw or driver code * there must be an interrupt bit set/triggered initially */ REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_GPM); gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); offset = gpm_ptr; if (!offset) offset = mci->gpm_len - 1; else if (offset >= mci->gpm_len) { if (offset != 0xFFFF) offset = 0; } else { offset--; } if ((offset == 0xFFFF) || (gpm_ptr == mci->gpm_idx)) { offset = MCI_GPM_INVALID; more_gpm = MCI_GPM_NOMORE; goto out; } for (;;) { u32 temp_index; /* skip reserved GPM if any */ if (offset != mci->gpm_idx) more_gpm = MCI_GPM_MORE; else more_gpm = MCI_GPM_NOMORE; temp_index = mci->gpm_idx; mci->gpm_idx++; if (mci->gpm_idx >= mci->gpm_len) mci->gpm_idx = 0; if (ar9003_mci_is_gpm_valid(ah, temp_index)) { offset = temp_index; break; } if (more_gpm == MCI_GPM_NOMORE) { offset = MCI_GPM_INVALID; break; } } if (offset != MCI_GPM_INVALID) offset <<= 4; out: if (more) *more = more_gpm; return offset; }
u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 value = 0, more_gpm = 0, gpm_ptr; u8 query_type; switch (state_type) { case MCI_STATE_ENABLE: if (mci->ready) { value = REG_READ(ah, AR_BTCOEX_CTRL); if ((value == 0xdeadbeef) || (value == 0xffffffff)) value = 0; } value &= AR_BTCOEX_CTRL_MCI_MODE_EN; break; case MCI_STATE_INIT_GPM_OFFSET: value = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); mci->gpm_idx = value; break; case MCI_STATE_NEXT_GPM_OFFSET: case MCI_STATE_LAST_GPM_OFFSET: REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_GPM); gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); value = gpm_ptr; if (value == 0) value = mci->gpm_len - 1; else if (value >= mci->gpm_len) { if (value != 0xFFFF) value = 0; } else { value--; } if (value == 0xFFFF) { value = MCI_GPM_INVALID; more_gpm = MCI_GPM_NOMORE; } else if (state_type == MCI_STATE_NEXT_GPM_OFFSET) { if (gpm_ptr == mci->gpm_idx) { value = MCI_GPM_INVALID; more_gpm = MCI_GPM_NOMORE; } else { for (;;) { u32 temp_index; if (value != mci->gpm_idx) more_gpm = MCI_GPM_MORE; else more_gpm = MCI_GPM_NOMORE; temp_index = mci->gpm_idx; mci->gpm_idx++; if (mci->gpm_idx >= mci->gpm_len) mci->gpm_idx = 0; if (ar9003_mci_is_gpm_valid(ah, temp_index)) { value = temp_index; break; } if (more_gpm == MCI_GPM_NOMORE) { value = MCI_GPM_INVALID; break; } } } if (p_data) *p_data = more_gpm; } if (value != MCI_GPM_INVALID) value <<= 4; break; case MCI_STATE_LAST_SCHD_MSG_OFFSET: value = MS(REG_READ(ah, AR_MCI_RX_STATUS), AR_MCI_RX_LAST_SCHD_MSG_INDEX); value <<= 4; break; case MCI_STATE_REMOTE_SLEEP: value = MS(REG_READ(ah, AR_MCI_RX_STATUS), AR_MCI_RX_REMOTE_SLEEP) ? MCI_BT_SLEEP : MCI_BT_AWAKE; break; case MCI_STATE_CONT_RSSI_POWER: value = MS(mci->cont_status, AR_MCI_CONT_RSSI_POWER); break; case MCI_STATE_CONT_PRIORITY: value = MS(mci->cont_status, AR_MCI_CONT_RRIORITY); break; case MCI_STATE_CONT_TXRX: value = MS(mci->cont_status, AR_MCI_CONT_TXRX); break; case MCI_STATE_BT: value = mci->bt_state; break; case MCI_STATE_SET_BT_SLEEP: mci->bt_state = MCI_BT_SLEEP; break; case MCI_STATE_SET_BT_AWAKE: mci->bt_state = MCI_BT_AWAKE; ar9003_mci_send_coex_version_query(ah, true); ar9003_mci_send_coex_wlan_channels(ah, true); if (mci->unhalt_bt_gpm) ar9003_mci_send_coex_halt_bt_gpm(ah, false, true); ar9003_mci_2g5g_switch(ah, true); break; case MCI_STATE_SET_BT_CAL_START: mci->bt_state = MCI_BT_CAL_START; break; case MCI_STATE_SET_BT_CAL: mci->bt_state = MCI_BT_CAL; break; case MCI_STATE_RESET_REQ_WAKE: ar9003_mci_reset_req_wakeup(ah); mci->update_2g5g = true; if (mci->config & ATH_MCI_CONFIG_MCI_OBS_MASK) { if ((REG_READ(ah, AR_GLB_GPIO_CONTROL) & ATH_MCI_CONFIG_MCI_OBS_GPIO) != ATH_MCI_CONFIG_MCI_OBS_GPIO) { ar9003_mci_observation_set_up(ah); } } break; case MCI_STATE_SEND_WLAN_COEX_VERSION: ar9003_mci_send_coex_version_response(ah, true); break; case MCI_STATE_SET_BT_COEX_VERSION: if (!p_data) ath_dbg(common, MCI, "MCI Set BT Coex version with NULL data!!\n"); else { mci->bt_ver_major = (*p_data >> 8) & 0xff; mci->bt_ver_minor = (*p_data) & 0xff; mci->bt_version_known = true; ath_dbg(common, MCI, "MCI BT version set: %d.%d\n", mci->bt_ver_major, mci->bt_ver_minor); } break; case MCI_STATE_SEND_WLAN_CHANNELS: if (p_data) { if (((mci->wlan_channels[1] & 0xffff0000) == (*(p_data + 1) & 0xffff0000)) && (mci->wlan_channels[2] == *(p_data + 2)) && (mci->wlan_channels[3] == *(p_data + 3))) break; mci->wlan_channels[0] = *p_data++; mci->wlan_channels[1] = *p_data++; mci->wlan_channels[2] = *p_data++; mci->wlan_channels[3] = *p_data++; } mci->wlan_channels_update = true; ar9003_mci_send_coex_wlan_channels(ah, true); break; case MCI_STATE_SEND_VERSION_QUERY: ar9003_mci_send_coex_version_query(ah, true); break; case MCI_STATE_SEND_STATUS_QUERY: query_type = MCI_GPM_COEX_QUERY_BT_TOPOLOGY; ar9003_mci_send_coex_bt_status_query(ah, true, query_type); break; case MCI_STATE_NEED_FLUSH_BT_INFO: value = (!mci->unhalt_bt_gpm && mci->need_flush_btinfo) ? 1 : 0; if (p_data) mci->need_flush_btinfo = (*p_data != 0) ? true : false; break; case MCI_STATE_RECOVER_RX: ar9003_mci_prep_interface(ah); mci->query_bt = true; mci->need_flush_btinfo = true; ar9003_mci_send_coex_wlan_channels(ah, true); ar9003_mci_2g5g_switch(ah, true); break; case MCI_STATE_NEED_FTP_STOMP: value = !(mci->config & ATH_MCI_CONFIG_DISABLE_FTP_STOMP); break; case MCI_STATE_NEED_TUNING: value = !(mci->config & ATH_MCI_CONFIG_DISABLE_TUNING); break; default: break; } return value; }