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_prep_interface(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 saved_mci_int_en; u32 mci_timeout = 150; mci->bt_state = MCI_BT_SLEEP; saved_mci_int_en = REG_READ(ah, AR_MCI_INTERRUPT_EN); REG_WRITE(ah, AR_MCI_INTERRUPT_EN, 0); REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_RAW)); REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, REG_READ(ah, AR_MCI_INTERRUPT_RAW)); ar9003_mci_remote_reset(ah, true); ar9003_mci_send_req_wake(ah, true); if (!ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING, 500)) goto clear_redunt; mci->bt_state = MCI_BT_AWAKE; /* * we don't need to send more remote_reset at this moment. * If BT receive first remote_reset, then BT HW will * be cleaned up and will be able to receive req_wake * and BT HW will respond sys_waking. * In this case, WLAN will receive BT's HW sys_waking. * Otherwise, if BT SW missed initial remote_reset, * that remote_reset will still clean up BT MCI RX, * and the req_wake will wake BT up, * and BT SW will respond this req_wake with a remote_reset and * sys_waking. In this case, WLAN will receive BT's SW * sys_waking. In either case, BT's RX is cleaned up. So we * don't need to reply BT's remote_reset now, if any. * Similarly, if in any case, WLAN can receive BT's sys_waking, * that means WLAN's RX is also fine. */ ar9003_mci_send_sys_waking(ah, true); udelay(10); /* * Set BT priority interrupt value to be 0xff to * avoid having too many BT PRIORITY interrupts. */ REG_WRITE(ah, AR_MCI_BT_PRI0, 0xFFFFFFFF); REG_WRITE(ah, AR_MCI_BT_PRI1, 0xFFFFFFFF); REG_WRITE(ah, AR_MCI_BT_PRI2, 0xFFFFFFFF); REG_WRITE(ah, AR_MCI_BT_PRI3, 0xFFFFFFFF); REG_WRITE(ah, AR_MCI_BT_PRI, 0X000000FF); /* * A contention reset will be received after send out * sys_waking. Also BT priority interrupt bits will be set. * Clear those bits before the next step. */ REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_CONT_RST); REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_BT_PRI); if (mci->is_2g) { ar9003_mci_send_lna_transfer(ah, true); udelay(5); } if ((mci->is_2g && !mci->update_2g5g)) { if (ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_LNA_INFO, mci_timeout)) ath_dbg(common, MCI, "MCI WLAN has control over the LNA & BT obeys it\n"); else ath_dbg(common, MCI, "MCI BT didn't respond to LNA_TRANS\n"); } clear_redunt: /* Clear the extra redundant SYS_WAKING from BT */ if ((mci->bt_state == MCI_BT_AWAKE) && (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) && (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) == 0)) { REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING); REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE); } REG_WRITE(ah, AR_MCI_INTERRUPT_EN, saved_mci_int_en); }
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; }
bool ar9003_mci_send_message(struct ath_hw *ah, u8 header, u32 flag, u32 *payload, u8 len, bool wait_done, bool check_bt) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; bool msg_sent = false; u32 regval; u32 saved_mci_int_en; int i; saved_mci_int_en = REG_READ(ah, AR_MCI_INTERRUPT_EN); regval = REG_READ(ah, AR_BTCOEX_CTRL); if ((regval == 0xdeadbeef) || !(regval & AR_BTCOEX_CTRL_MCI_MODE_EN)) { ath_dbg(common, MCI, "MCI Not sending 0x%x. MCI is not enabled. full_sleep = %d\n", header, (ah->power_mode == ATH9K_PM_FULL_SLEEP) ? 1 : 0); ar9003_mci_queue_unsent_gpm(ah, header, payload, true); return false; } else if (check_bt && (mci->bt_state == MCI_BT_SLEEP)) { ath_dbg(common, MCI, "MCI Don't send message 0x%x. BT is in sleep state\n", header); ar9003_mci_queue_unsent_gpm(ah, header, payload, true); return false; } if (wait_done) REG_WRITE(ah, AR_MCI_INTERRUPT_EN, 0); /* Need to clear SW_MSG_DONE raw bit before wait */ REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, (AR_MCI_INTERRUPT_SW_MSG_DONE | AR_MCI_INTERRUPT_MSG_FAIL_MASK)); if (payload) { for (i = 0; (i * 4) < len; i++) REG_WRITE(ah, (AR_MCI_TX_PAYLOAD0 + i * 4), *(payload + i)); } REG_WRITE(ah, AR_MCI_COMMAND0, (SM((flag & MCI_FLAG_DISABLE_TIMESTAMP), AR_MCI_COMMAND0_DISABLE_TIMESTAMP) | SM(len, AR_MCI_COMMAND0_LEN) | SM(header, AR_MCI_COMMAND0_HEADER))); if (wait_done && !(ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_SW_MSG_DONE, 500))) ar9003_mci_queue_unsent_gpm(ah, header, payload, true); else { ar9003_mci_queue_unsent_gpm(ah, header, payload, false); msg_sent = true; } if (wait_done) REG_WRITE(ah, AR_MCI_INTERRUPT_EN, saved_mci_int_en); return msg_sent; }
static void ar9003_mci_prep_interface(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 saved_mci_int_en; u32 mci_timeout = 150; mci->bt_state = MCI_BT_SLEEP; saved_mci_int_en = REG_READ(ah, AR_MCI_INTERRUPT_EN); REG_WRITE(ah, AR_MCI_INTERRUPT_EN, 0); REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_RAW)); REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, REG_READ(ah, AR_MCI_INTERRUPT_RAW)); ar9003_mci_remote_reset(ah, true); ar9003_mci_send_req_wake(ah, true); if (ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING, 500)) { mci->bt_state = MCI_BT_AWAKE; ar9003_mci_send_sys_waking(ah, true); udelay(10); REG_WRITE(ah, AR_MCI_BT_PRI0, 0xFFFFFFFF); REG_WRITE(ah, AR_MCI_BT_PRI1, 0xFFFFFFFF); REG_WRITE(ah, AR_MCI_BT_PRI2, 0xFFFFFFFF); REG_WRITE(ah, AR_MCI_BT_PRI3, 0xFFFFFFFF); REG_WRITE(ah, AR_MCI_BT_PRI, 0X000000FF); REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_CONT_RST); REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_BT_PRI); if (mci->is_2g) { ar9003_mci_send_lna_transfer(ah, true); udelay(5); } if ((mci->is_2g && !mci->update_2g5g)) { if (ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_LNA_INFO, mci_timeout)) ath_dbg(common, MCI, "MCI WLAN has control over the LNA & BT obeys it\n"); else ath_dbg(common, MCI, "MCI BT didn't respond to LNA_TRANS\n"); } } if ((mci->bt_state == MCI_BT_AWAKE) && (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) && (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) == 0)) { REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING); REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE); } REG_WRITE(ah, AR_MCI_INTERRUPT_EN, saved_mci_int_en); }