static void ar9003_mci_send_coex_bt_status_query(struct ath_hw *ah, bool wait_done, u8 query_type) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; bool query_btinfo; if (mci->bt_state == MCI_BT_SLEEP) return; query_btinfo = !!(query_type & (MCI_GPM_COEX_QUERY_BT_ALL_INFO | MCI_GPM_COEX_QUERY_BT_TOPOLOGY)); MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, MCI_GPM_COEX_STATUS_QUERY); *(((u8 *)payload) + MCI_GPM_COEX_B_BT_BITMAP) = query_type; /* * If bt_status_query message is not sent successfully, * then need_flush_btinfo should be set again. */ if (!ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true)) { if (query_btinfo) mci->need_flush_btinfo = true; } if (query_btinfo) mci->query_bt = false; }
static void ar9003_mci_send_lna_take(struct ath_hw *ah, bool wait_done) { u32 payload = 0x70000000; ar9003_mci_send_message(ah, MCI_LNA_TAKE, 0, &payload, 1, wait_done, false); }
bool ar9003_mci_start_reset(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; ar9003_mci_2g5g_changed(ah, IS_CHAN_2GHZ(chan)); if (mci_hw->bt_state != MCI_BT_CAL_START) return false; mci_hw->bt_state = MCI_BT_CAL; ar9003_mci_disable_interrupt(ah); MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_GRANT); ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, true, false); if (ar9003_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_DONE, 0, 25000)) ath_dbg(common, MCI, "MCI BT_CAL_DONE received\n"); else ath_dbg(common, MCI, "MCI BT_CAL_DONE not received\n"); mci_hw->bt_state = MCI_BT_AWAKE; ar9003_mci_enable_interrupt(ah); return true; }
static void ar9003_mci_send_lna_transfer(struct ath_hw *ah, bool wait_done) { u32 payload = 0x00000000; ar9003_mci_send_message(ah, MCI_LNA_TRANS, 0, &payload, 1, wait_done, false); }
static void ar9003_mci_remote_reset(struct ath_hw *ah, bool wait_done) { u32 payload[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffff00}; ar9003_mci_send_message(ah, MCI_REMOTE_RESET, 0, payload, 16, wait_done, false); udelay(5); }
void ar9003_mci_init_cal_done(struct ath_hw *ah) { struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; u32 pld[4] = {0, 0, 0, 0}; if ((mci_hw->bt_state != MCI_BT_AWAKE) || (mci_hw->config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) return; MCI_GPM_SET_CAL_TYPE(pld, MCI_GPM_WLAN_CAL_DONE); pld[MCI_GPM_WLAN_CAL_W_SEQUENCE] = mci_hw->wlan_cal_done++; ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, true, false); }
static void ar9003_mci_send_coex_version_response(struct ath_hw *ah, bool wait_done) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, MCI_GPM_COEX_VERSION_RESPONSE); *(((u8 *)payload) + MCI_GPM_COEX_B_MAJOR_VERSION) = mci->wlan_ver_major; *(((u8 *)payload) + MCI_GPM_COEX_B_MINOR_VERSION) = mci->wlan_ver_minor; ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); }
static void ar9003_mci_send_coex_version_query(struct ath_hw *ah, bool wait_done) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; if (mci->bt_version_known || (mci->bt_state == MCI_BT_SLEEP)) return; MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, MCI_GPM_COEX_VERSION_QUERY); ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); }
static void ar9003_mci_send_coex_wlan_channels(struct ath_hw *ah, bool wait_done) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 *payload = &mci->wlan_channels[0]; if (!mci->wlan_channels_update || (mci->bt_state == MCI_BT_SLEEP)) return; MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, MCI_GPM_COEX_WLAN_CHANNELS); ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); MCI_GPM_SET_TYPE_OPCODE(payload, 0xff, 0xff); }
static bool ar9003_mci_send_coex_bt_flags(struct ath_hw *ah, bool wait_done, u8 opcode, u32 bt_flags) { u32 pld[4] = {0, 0, 0, 0}; MCI_GPM_SET_TYPE_OPCODE(pld, MCI_GPM_COEX_AGENT, MCI_GPM_COEX_BT_UPDATE_FLAGS); *(((u8 *)pld) + MCI_GPM_COEX_B_BT_FLAGS_OP) = opcode; *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 0) = bt_flags & 0xFF; *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 1) = (bt_flags >> 8) & 0xFF; *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 2) = (bt_flags >> 16) & 0xFF; *(((u8 *)pld) + MCI_GPM_COEX_W_BT_FLAGS + 3) = (bt_flags >> 24) & 0xFF; return ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, wait_done, true); }
bool ar9003_mci_start_reset(struct ath_hw *ah, struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; ar9003_mci_2g5g_changed(ah, IS_CHAN_2GHZ(chan)); if (mci_hw->bt_state != MCI_BT_CAL_START) return false; mci_hw->bt_state = MCI_BT_CAL; /* * MCI FIX: disable mci interrupt here. This is to avoid * SW_MSG_DONE or RX_MSG bits to trigger MCI_INT and * lead to mci_intr reentry. */ ar9003_mci_disable_interrupt(ah); MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_GRANT); ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, true, false); /* Wait BT calibration to be completed for 25ms */ if (ar9003_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_DONE, 0, 25000)) ath_dbg(common, MCI, "MCI BT_CAL_DONE received\n"); else ath_dbg(common, MCI, "MCI BT_CAL_DONE not received\n"); mci_hw->bt_state = MCI_BT_AWAKE; /* MCI FIX: enable mci interrupt here */ ar9003_mci_enable_interrupt(ah); return true; }
static void ar9003_mci_send_coex_halt_bt_gpm(struct ath_hw *ah, bool halt, bool wait_done) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, MCI_GPM_COEX_HALT_BT_GPM); if (halt) { mci->query_bt = true; /* Send next unhalt no matter halt sent or not */ mci->unhalt_bt_gpm = true; mci->need_flush_btinfo = true; *(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) = MCI_GPM_COEX_BT_GPM_HALT; } else *(((u8 *)payload) + MCI_GPM_COEX_B_HALT_STATE) = MCI_GPM_COEX_BT_GPM_UNHALT; ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); }
void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; u32 pld[4] = {0, 0, 0, 0}; if ((mci_hw->bt_state != MCI_BT_AWAKE) || (mci_hw->config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) return; MCI_GPM_SET_CAL_TYPE(pld, MCI_GPM_WLAN_CAL_REQ); pld[MCI_GPM_WLAN_CAL_W_SEQUENCE] = mci_hw->wlan_cal_seq++; ar9003_mci_send_message(ah, MCI_GPM, 0, pld, 16, true, false); if (ar9003_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_GRANT, 0, 50000)) { ath_dbg(common, MCI, "MCI BT_CAL_GRANT received\n"); } else { *is_reusable = false; ath_dbg(common, MCI, "MCI BT_CAL_GRANT not received\n"); } }
static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type, u8 gpm_opcode, int time_out) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 *p_gpm = NULL, mismatch = 0, more_data; u32 offset; u8 recv_type = 0, recv_opcode = 0; bool b_is_bt_cal_done = (gpm_type == MCI_GPM_BT_CAL_DONE); more_data = time_out ? MCI_GPM_NOMORE : MCI_GPM_MORE; while (time_out > 0) { if (p_gpm) { MCI_GPM_RECYCLE(p_gpm); p_gpm = NULL; } if (more_data != MCI_GPM_MORE) time_out = ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_GPM, time_out); if (!time_out) break; offset = ar9003_mci_get_next_gpm_offset(ah, false, &more_data); if (offset == MCI_GPM_INVALID) continue; p_gpm = (u32 *) (mci->gpm_buf + offset); recv_type = MCI_GPM_TYPE(p_gpm); recv_opcode = MCI_GPM_OPCODE(p_gpm); if (MCI_GPM_IS_CAL_TYPE(recv_type)) { if (recv_type == gpm_type) { if ((gpm_type == MCI_GPM_BT_CAL_DONE) && !b_is_bt_cal_done) { gpm_type = MCI_GPM_BT_CAL_GRANT; continue; } break; } } else if ((recv_type == gpm_type) && (recv_opcode == gpm_opcode)) break; /* * check if it's cal_grant * * When we're waiting for cal_grant in reset routine, * it's possible that BT sends out cal_request at the * same time. Since BT's calibration doesn't happen * that often, we'll let BT completes calibration then * we continue to wait for cal_grant from BT. * Orginal: Wait BT_CAL_GRANT. * New: Receive BT_CAL_REQ -> send WLAN_CAL_GRANT->wait * BT_CAL_DONE -> Wait BT_CAL_GRANT. */ if ((gpm_type == MCI_GPM_BT_CAL_GRANT) && (recv_type == MCI_GPM_BT_CAL_REQ)) { u32 payload[4] = {0, 0, 0, 0}; gpm_type = MCI_GPM_BT_CAL_DONE; MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_GRANT); ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, false, false); continue; } else { ath_dbg(common, MCI, "MCI GPM subtype not match 0x%x\n", *(p_gpm + 1)); mismatch++; ar9003_mci_process_gpm_extra(ah, recv_type, recv_opcode, p_gpm); } } if (p_gpm) { MCI_GPM_RECYCLE(p_gpm); p_gpm = NULL; } if (time_out <= 0) time_out = 0; while (more_data == MCI_GPM_MORE) { offset = ar9003_mci_get_next_gpm_offset(ah, false, &more_data); if (offset == MCI_GPM_INVALID) break; p_gpm = (u32 *) (mci->gpm_buf + offset); recv_type = MCI_GPM_TYPE(p_gpm); recv_opcode = MCI_GPM_OPCODE(p_gpm); if (!MCI_GPM_IS_CAL_TYPE(recv_type)) ar9003_mci_process_gpm_extra(ah, recv_type, recv_opcode, p_gpm); MCI_GPM_RECYCLE(p_gpm); } return time_out; }
static void ar9003_mci_send_sys_sleeping(struct ath_hw *ah, bool wait_done) { ar9003_mci_send_message(ah, MCI_SYS_SLEEPING, MCI_FLAG_DISABLE_TIMESTAMP, NULL, 0, wait_done, false); }
static void ar9003_mci_send_req_wake(struct ath_hw *ah, bool wait_done) { ar9003_mci_send_message(ah, MCI_REQ_WAKE, MCI_FLAG_DISABLE_TIMESTAMP, NULL, 0, wait_done, false); udelay(5); }
static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type, u8 gpm_opcode, int time_out) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 *p_gpm = NULL, mismatch = 0, more_data; u32 offset; u8 recv_type = 0, recv_opcode = 0; bool b_is_bt_cal_done = (gpm_type == MCI_GPM_BT_CAL_DONE); more_data = time_out ? MCI_GPM_NOMORE : MCI_GPM_MORE; while (time_out > 0) { if (p_gpm) { MCI_GPM_RECYCLE(p_gpm); p_gpm = NULL; } if (more_data != MCI_GPM_MORE) time_out = ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_GPM, time_out); if (!time_out) break; offset = ar9003_mci_state(ah, MCI_STATE_NEXT_GPM_OFFSET, &more_data); if (offset == MCI_GPM_INVALID) continue; p_gpm = (u32 *) (mci->gpm_buf + offset); recv_type = MCI_GPM_TYPE(p_gpm); recv_opcode = MCI_GPM_OPCODE(p_gpm); if (MCI_GPM_IS_CAL_TYPE(recv_type)) { if (recv_type == gpm_type) { if ((gpm_type == MCI_GPM_BT_CAL_DONE) && !b_is_bt_cal_done) { gpm_type = MCI_GPM_BT_CAL_GRANT; continue; } break; } } else if ((recv_type == gpm_type) && (recv_opcode == gpm_opcode)) { break; } if ((gpm_type == MCI_GPM_BT_CAL_GRANT) && (recv_type == MCI_GPM_BT_CAL_REQ)) { u32 payload[4] = {0, 0, 0, 0}; gpm_type = MCI_GPM_BT_CAL_DONE; MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_GRANT); ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, false, false); continue; } else { ath_dbg(common, MCI, "MCI GPM subtype not match 0x%x\n", *(p_gpm + 1)); mismatch++; ar9003_mci_process_gpm_extra(ah, recv_type, recv_opcode, p_gpm); } } if (p_gpm) { MCI_GPM_RECYCLE(p_gpm); p_gpm = NULL; } if (time_out <= 0) time_out = 0; while (more_data == MCI_GPM_MORE) { offset = ar9003_mci_state(ah, MCI_STATE_NEXT_GPM_OFFSET, &more_data); if (offset == MCI_GPM_INVALID) break; p_gpm = (u32 *) (mci->gpm_buf + offset); recv_type = MCI_GPM_TYPE(p_gpm); recv_opcode = MCI_GPM_OPCODE(p_gpm); if (!MCI_GPM_IS_CAL_TYPE(recv_type)) ar9003_mci_process_gpm_extra(ah, recv_type, recv_opcode, p_gpm); MCI_GPM_RECYCLE(p_gpm); } return time_out; }