1301181Sadrian/*- 2301181Sadrian * Copyright (c) 2014 Qualcomm Atheros, Inc. 3301181Sadrian * Copyright (c) 2016 Adrian Chadd <adrian@FreeBSD.org> 4301181Sadrian * All rights reserved. 5301181Sadrian * 6301181Sadrian * Redistribution and use in source and binary forms, with or without 7301181Sadrian * modification, are permitted provided that the following conditions 8301181Sadrian * are met: 9301181Sadrian * 1. Redistributions of source code must retain the above copyright 10301181Sadrian * notice, this list of conditions and the following disclaimer, 11301181Sadrian * without modification. 12301181Sadrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer 13301181Sadrian * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 14301181Sadrian * redistribution must be conditioned upon including a substantially 15301181Sadrian * similar Disclaimer requirement for further binary redistribution. 16301181Sadrian * 17301181Sadrian * NO WARRANTY 18301181Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19301181Sadrian * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20301181Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 21301181Sadrian * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 22301181Sadrian * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 23301181Sadrian * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24301181Sadrian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25301181Sadrian * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 26301181Sadrian * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27301181Sadrian * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28301181Sadrian * THE POSSIBILITY OF SUCH DAMAGES. 29301181Sadrian * 30301181Sadrian * $FreeBSD$ 31301181Sadrian */ 32301181Sadrian#include <sys/cdefs.h> 33301181Sadrian__FBSDID("$FreeBSD$"); 34301181Sadrian 35301181Sadrian/* 36301181Sadrian * This implements the MCI bluetooth coexistence handling. 37301181Sadrian */ 38301181Sadrian#include "opt_ath.h" 39301181Sadrian#include "opt_inet.h" 40301181Sadrian#include "opt_wlan.h" 41301181Sadrian 42301181Sadrian#include <sys/param.h> 43301181Sadrian#include <sys/systm.h> 44301181Sadrian#include <sys/sysctl.h> 45301181Sadrian#include <sys/kernel.h> 46301181Sadrian#include <sys/lock.h> 47301181Sadrian#include <sys/malloc.h> 48301181Sadrian#include <sys/mutex.h> 49301181Sadrian#include <sys/errno.h> 50301181Sadrian 51301181Sadrian#include <machine/bus.h> 52301181Sadrian#include <machine/resource.h> 53301181Sadrian 54301181Sadrian#include <sys/bus.h> 55301181Sadrian 56301181Sadrian#include <sys/socket.h> 57301181Sadrian 58301181Sadrian#include <net/if.h> 59301181Sadrian#include <net/if_var.h> 60301181Sadrian#include <net/if_media.h> 61301181Sadrian#include <net/if_arp.h> 62301181Sadrian#include <net/ethernet.h> /* XXX for ether_sprintf */ 63301181Sadrian 64301181Sadrian#include <net80211/ieee80211_var.h> 65301181Sadrian 66301181Sadrian#include <net/bpf.h> 67301181Sadrian 68301181Sadrian#ifdef INET 69301181Sadrian#include <netinet/in.h> 70301181Sadrian#include <netinet/if_ether.h> 71301181Sadrian#endif 72301181Sadrian 73301181Sadrian#include <dev/ath/if_athvar.h> 74301181Sadrian#include <dev/ath/if_ath_debug.h> 75301181Sadrian#include <dev/ath/if_ath_descdma.h> 76301181Sadrian#include <dev/ath/if_ath_btcoex.h> 77301181Sadrian 78301181Sadrian#include <dev/ath/if_ath_btcoex_mci.h> 79301181Sadrian 80301181SadrianMALLOC_DECLARE(M_ATHDEV); 81301181Sadrian 82301181Sadrian#define ATH_MCI_GPM_MAX_ENTRY 16 83301181Sadrian#define ATH_MCI_GPM_BUF_SIZE (ATH_MCI_GPM_MAX_ENTRY * 16) 84301181Sadrian#define ATH_MCI_SCHED_BUF_SIZE (16 * 16) /* 16 entries, 4 dword each */ 85301181Sadrian 86301181Sadrianstatic void ath_btcoex_mci_update_wlan_channels(struct ath_softc *sc); 87301181Sadrian 88301181Sadrianint 89301181Sadrianath_btcoex_mci_attach(struct ath_softc *sc) 90301181Sadrian{ 91301181Sadrian int buflen, error; 92301181Sadrian 93301181Sadrian buflen = ATH_MCI_GPM_BUF_SIZE + ATH_MCI_SCHED_BUF_SIZE; 94301181Sadrian error = ath_descdma_alloc_desc(sc, &sc->sc_btcoex.buf, NULL, 95301181Sadrian "MCI bufs", buflen, 1); 96301181Sadrian if (error != 0) { 97301181Sadrian device_printf(sc->sc_dev, "%s: failed to alloc MCI RAM\n", 98301181Sadrian __func__); 99301181Sadrian return (error); 100301181Sadrian } 101301181Sadrian 102301181Sadrian /* Yes, we're going to do bluetooth MCI coex */ 103301181Sadrian sc->sc_btcoex_mci = 1; 104301181Sadrian 105301181Sadrian /* Initialise the wlan channel mapping */ 106301181Sadrian sc->sc_btcoex.wlan_channels[0] = 0x00000000; 107301181Sadrian sc->sc_btcoex.wlan_channels[1] = 0xffffffff; 108301181Sadrian sc->sc_btcoex.wlan_channels[2] = 0xffffffff; 109301181Sadrian sc->sc_btcoex.wlan_channels[3] = 0x7fffffff; 110301181Sadrian 111301181Sadrian /* 112301181Sadrian * Ok, so the API is a bit odd. It assumes sched_addr is 113301181Sadrian * after gpm_addr, and it does math to figure out the right 114301181Sadrian * sched_buf pointer. 115301181Sadrian * 116301181Sadrian * So, set gpm_addr to buf, sched_addr to gpm_addr + ATH_MCI_GPM_BUF_SIZE, 117301181Sadrian * the HAL call with do (gpm_buf + (sched_addr - gpm_addr)) to 118301181Sadrian * set sched_buf, and we're "golden". 119301181Sadrian * 120301181Sadrian * Note, it passes in 'len' here (gpm_len) as 121301181Sadrian * ATH_MCI_GPM_BUF_SIZE >> 4. My guess is that it's 16 122301181Sadrian * bytes per entry and we're storing 16 entries. 123301181Sadrian */ 124301181Sadrian sc->sc_btcoex.gpm_buf = (void *) sc->sc_btcoex.buf.dd_desc; 125301181Sadrian sc->sc_btcoex.sched_buf = sc->sc_btcoex.gpm_buf + 126301181Sadrian ATH_MCI_GPM_BUF_SIZE; 127301181Sadrian 128301181Sadrian sc->sc_btcoex.gpm_paddr = sc->sc_btcoex.buf.dd_desc_paddr; 129301181Sadrian sc->sc_btcoex.sched_paddr = sc->sc_btcoex.gpm_paddr + 130301181Sadrian ATH_MCI_GPM_BUF_SIZE; 131301181Sadrian 132301181Sadrian /* memset the gpm buffer with MCI_GPM_RSVD_PATTERN */ 133301181Sadrian memset(sc->sc_btcoex.gpm_buf, 0xfe, buflen); 134301181Sadrian 135301181Sadrian /* 136301181Sadrian * This is an unfortunate x86'ism in the HAL - the 137301181Sadrian * HAL code expects the passed in buffer to be 138301181Sadrian * coherent, and doesn't implement /any/ kind 139301181Sadrian * of buffer sync operations at all. 140301181Sadrian * 141301181Sadrian * So, this code will only work on dma coherent buffers 142301181Sadrian * and will behave poorly on non-coherent systems. 143301181Sadrian * Fixing this would require some HAL surgery so it 144301181Sadrian * actually /did/ the buffer flushing as appropriate. 145301181Sadrian */ 146301181Sadrian ath_hal_btcoex_mci_setup(sc->sc_ah, 147301181Sadrian sc->sc_btcoex.gpm_paddr, 148301181Sadrian sc->sc_btcoex.gpm_buf, 149301181Sadrian ATH_MCI_GPM_BUF_SIZE >> 4, 150301181Sadrian sc->sc_btcoex.sched_paddr); 151301181Sadrian 152301181Sadrian return (0); 153301181Sadrian} 154301181Sadrian 155301181Sadrian/* 156301181Sadrian * Detach btcoex from the given interface 157301181Sadrian */ 158301181Sadrianint 159301181Sadrianath_btcoex_mci_detach(struct ath_softc *sc) 160301181Sadrian{ 161301181Sadrian 162301181Sadrian ath_hal_btcoex_mci_detach(sc->sc_ah); 163301181Sadrian ath_descdma_cleanup(sc, &sc->sc_btcoex.buf, NULL); 164301181Sadrian return (0); 165301181Sadrian} 166301181Sadrian 167301181Sadrian/* 168301181Sadrian * Configure or disable bluetooth coexistence on the given channel. 169301181Sadrian * 170301181Sadrian * For MCI, we just use the top-level enable/disable flag, and 171301181Sadrian * then the MCI reset / channel update path will configure things 172301181Sadrian * appropriately based on the current band. 173301181Sadrian */ 174301181Sadrianint 175301181Sadrianath_btcoex_mci_enable(struct ath_softc *sc, 176301181Sadrian const struct ieee80211_channel *chan) 177301181Sadrian{ 178301181Sadrian 179301181Sadrian /* 180301181Sadrian * Always reconfigure stomp-all for now, so wlan wins. 181301181Sadrian * 182301181Sadrian * The default weights still don't allow beacons to win, 183301181Sadrian * so unless you set net.wlan.X.bmiss_max to something higher, 184301181Sadrian * net80211 will disconnect you during a HCI INQUIRY command. 185301181Sadrian * 186301181Sadrian * The longer-term solution is to dynamically adjust whether 187301181Sadrian * bmiss happens based on bluetooth requirements, and look at 188301181Sadrian * making the individual stomp bits configurable. 189301181Sadrian */ 190301181Sadrian ath_hal_btcoex_set_weights(sc->sc_ah, HAL_BT_COEX_STOMP_ALL); 191301181Sadrian 192301181Sadrian /* 193301181Sadrian * update wlan channels so the firmware knows what channels it 194301181Sadrian * can/can't use. 195301181Sadrian */ 196301181Sadrian ath_btcoex_mci_update_wlan_channels(sc); 197301181Sadrian 198301181Sadrian return (0); 199301181Sadrian} 200301181Sadrian 201301181Sadrian/* 202301181Sadrian * XXX TODO: turn into general btcoex, and then make this 203301181Sadrian * the MCI specific bits. 204301181Sadrian */ 205301181Sadrianstatic void 206301181Sadrianath_btcoex_mci_event(struct ath_softc *sc, ATH_BT_COEX_EVENT nevent, 207301181Sadrian void *param) 208301181Sadrian{ 209301181Sadrian 210301181Sadrian if (! sc->sc_btcoex_mci) 211301181Sadrian return; 212301181Sadrian 213301181Sadrian /* 214301181Sadrian * Check whether we need to flush our local profile cache. 215301181Sadrian * If we do, then at (XXX TODO) we should flush our state, 216301181Sadrian * then wait for the MCI response with the updated profile list. 217301181Sadrian */ 218301181Sadrian if (ath_hal_btcoex_mci_state(sc->sc_ah, 219301181Sadrian HAL_MCI_STATE_NEED_FLUSH_BT_INFO, NULL) != 0) { 220301181Sadrian uint32_t data = 0; 221301181Sadrian 222301181Sadrian if (ath_hal_btcoex_mci_state(sc->sc_ah, 223301181Sadrian HAL_MCI_STATE_ENABLE, NULL) != 0) { 224301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 225301181Sadrian "(MCI) Flush BT profile\n"); 226301181Sadrian /* 227301181Sadrian * XXX TODO: flush profile state on the ath(4) 228301181Sadrian * driver side; subsequent messages will come 229301181Sadrian * through with the current list of active 230301181Sadrian * profiles. 231301181Sadrian */ 232301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, 233301181Sadrian HAL_MCI_STATE_NEED_FLUSH_BT_INFO, &data); 234301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, 235301181Sadrian HAL_MCI_STATE_SEND_STATUS_QUERY, NULL); 236301181Sadrian } 237301181Sadrian } 238301181Sadrian if (nevent == ATH_COEX_EVENT_BT_NOOP) { 239301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) BT_NOOP\n"); 240301181Sadrian return; 241301181Sadrian } 242301181Sadrian} 243301181Sadrian 244301181Sadrianstatic void 245301181Sadrianath_btcoex_mci_send_gpm(struct ath_softc *sc, uint32_t *payload) 246301181Sadrian{ 247301181Sadrian 248301181Sadrian ath_hal_btcoex_mci_send_message(sc->sc_ah, MCI_GPM, 0, payload, 16, 249301181Sadrian AH_FALSE, AH_TRUE); 250301181Sadrian} 251301181Sadrian 252301181Sadrian/* 253301181Sadrian * This starts a BT calibration. It requires a chip reset. 254301181Sadrian */ 255301181Sadrianstatic int 256301181Sadrianath_btcoex_mci_bt_cal_do(struct ath_softc *sc, int tx_timeout, int rx_timeout) 257301181Sadrian{ 258301181Sadrian 259301181Sadrian device_printf(sc->sc_dev, "%s: TODO!\n", __func__); 260301181Sadrian return (0); 261301181Sadrian} 262301181Sadrian 263301181Sadrianstatic void 264301181Sadrianath_btcoex_mci_cal_msg(struct ath_softc *sc, uint8_t opcode, 265301181Sadrian uint8_t *rx_payload) 266301181Sadrian{ 267301181Sadrian uint32_t payload[4] = {0, 0, 0, 0}; 268301181Sadrian 269301181Sadrian switch (opcode) { 270301181Sadrian case MCI_GPM_BT_CAL_REQ: 271301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_REQ\n"); 272301181Sadrian if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT, 273301181Sadrian NULL) == MCI_BT_AWAKE) { 274301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, 275301181Sadrian HAL_MCI_STATE_SET_BT_CAL_START, NULL); 276301181Sadrian ath_btcoex_mci_bt_cal_do(sc, 1000, 1000); 277301181Sadrian } else { 278301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 279301181Sadrian "(MCI) State mismatches: %d\n", 280301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, 281301181Sadrian HAL_MCI_STATE_BT, NULL)); 282301181Sadrian } 283301181Sadrian break; 284301181Sadrian case MCI_GPM_BT_CAL_DONE: 285301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_DONE\n"); 286301181Sadrian if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT, 287301181Sadrian NULL) == MCI_BT_CAL) { 288301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 289301181Sadrian "(MCI) ERROR ILLEGAL!\n"); 290301181Sadrian } else { 291301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 292301181Sadrian "(MCI) BT not in CAL state.\n"); 293301181Sadrian } 294301181Sadrian break; 295301181Sadrian case MCI_GPM_BT_CAL_GRANT: 296301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_GRANT\n"); 297301181Sadrian /* Send WLAN_CAL_DONE for now */ 298301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) Send WLAN_CAL_DONE\n"); 299301181Sadrian MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE); 300301181Sadrian ath_btcoex_mci_send_gpm(sc, &payload[0]); 301301181Sadrian break; 302301181Sadrian default: 303301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 304301181Sadrian "(MCI) Unknown GPM CAL message.\n"); 305301181Sadrian break; 306301181Sadrian } 307301181Sadrian} 308301181Sadrian 309301181Sadrian/* 310301181Sadrian * Update the bluetooth channel map. 311301181Sadrian * 312301181Sadrian * This map tells the bluetooth device which bluetooth channels 313301181Sadrian * are available for data. 314301181Sadrian * 315301181Sadrian * For 5GHz, all channels are available. 316301181Sadrian * For 2GHz, the current wifi channel range is blocked out, 317301181Sadrian * and the rest are available. 318301181Sadrian * 319301181Sadrian * This narrows which frequencies are used by the device when 320301181Sadrian * it initiates a transfer, thus hopefully reducing the chances 321301181Sadrian * of collisions (both hopefully on the current device and 322301181Sadrian * other devices in the same channel.) 323301181Sadrian */ 324301181Sadrianstatic void 325301181Sadrianath_btcoex_mci_update_wlan_channels(struct ath_softc *sc) 326301181Sadrian{ 327301181Sadrian struct ieee80211com *ic = &sc->sc_ic; 328301181Sadrian struct ieee80211_channel *chan = ic->ic_curchan; 329301181Sadrian uint32_t channel_info[4] = 330301181Sadrian { 0x00000000, 0xffffffff, 0xffffffff, 0x7fffffff }; 331301181Sadrian int32_t wl_chan, bt_chan, bt_start = 0, bt_end = 79; 332301181Sadrian 333301181Sadrian /* BT channel frequency is 2402 + k, k = 0 ~ 78 */ 334301181Sadrian if (IEEE80211_IS_CHAN_2GHZ(chan)) { 335301181Sadrian wl_chan = chan->ic_freq - 2402; 336301181Sadrian if (IEEE80211_IS_CHAN_HT40U(chan)) { 337301181Sadrian bt_start = wl_chan - 10; 338301181Sadrian bt_end = wl_chan + 30; 339301181Sadrian } else if (IEEE80211_IS_CHAN_HT40D(chan)) { 340301181Sadrian bt_start = wl_chan - 30; 341301181Sadrian bt_end = wl_chan + 10; 342301181Sadrian } else { 343301181Sadrian /* Assume 20MHz */ 344301181Sadrian bt_start = wl_chan - 10; 345301181Sadrian bt_end = wl_chan + 10; 346301181Sadrian } 347301181Sadrian 348301181Sadrian bt_start -= 7; 349301181Sadrian bt_end += 7; 350301181Sadrian 351301181Sadrian if (bt_start < 0) { 352301181Sadrian bt_start = 0; 353301181Sadrian } 354301181Sadrian if (bt_end > MCI_NUM_BT_CHANNELS) { 355301181Sadrian bt_end = MCI_NUM_BT_CHANNELS; 356301181Sadrian } 357301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) WLAN use channel %d\n", 358301181Sadrian chan->ic_freq); 359301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 360301181Sadrian "(MCI) mask BT channel %d - %d\n", bt_start, bt_end); 361301181Sadrian for (bt_chan = bt_start; bt_chan < bt_end; bt_chan++) { 362301181Sadrian MCI_GPM_CLR_CHANNEL_BIT(&channel_info[0], bt_chan); 363301181Sadrian } 364301181Sadrian } else { 365301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 366301181Sadrian "(MCI) WLAN not use any 2G channel, unmask all for BT\n"); 367301181Sadrian } 368301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_SEND_WLAN_CHANNELS, 369301181Sadrian &channel_info[0]); 370301181Sadrian} 371301181Sadrian 372301181Sadrianstatic void 373301181Sadrianath_btcoex_mci_coex_msg(struct ath_softc *sc, uint8_t opcode, 374301181Sadrian uint8_t *rx_payload) 375301181Sadrian{ 376301181Sadrian uint32_t version; 377301181Sadrian uint8_t major; 378301181Sadrian uint8_t minor; 379301181Sadrian uint32_t seq_num; 380301181Sadrian 381301181Sadrian switch (opcode) { 382301181Sadrian case MCI_GPM_COEX_VERSION_QUERY: 383301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 384301181Sadrian "(MCI) Recv GPM COEX Version Query.\n"); 385301181Sadrian version = ath_hal_btcoex_mci_state(sc->sc_ah, 386301181Sadrian HAL_MCI_STATE_SEND_WLAN_COEX_VERSION, NULL); 387301181Sadrian break; 388301181Sadrian 389301181Sadrian case MCI_GPM_COEX_VERSION_RESPONSE: 390301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 391301181Sadrian "(MCI) Recv GPM COEX Version Response.\n"); 392301181Sadrian major = *(rx_payload + MCI_GPM_COEX_B_MAJOR_VERSION); 393301181Sadrian minor = *(rx_payload + MCI_GPM_COEX_B_MINOR_VERSION); 394301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 395301181Sadrian "(MCI) BT Coex version: %d.%d\n", major, minor); 396301181Sadrian version = (major << 8) + minor; 397301181Sadrian version = ath_hal_btcoex_mci_state(sc->sc_ah, 398301181Sadrian HAL_MCI_STATE_SET_BT_COEX_VERSION, &version); 399301181Sadrian break; 400301181Sadrian 401301181Sadrian case MCI_GPM_COEX_STATUS_QUERY: 402301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 403301181Sadrian "(MCI) Recv GPM COEX Status Query = 0x%02x.\n", 404301181Sadrian *(rx_payload + MCI_GPM_COEX_B_WLAN_BITMAP)); 405301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, 406301181Sadrian HAL_MCI_STATE_SEND_WLAN_CHANNELS, NULL); 407301181Sadrian break; 408301181Sadrian 409301181Sadrian case MCI_GPM_COEX_BT_PROFILE_INFO: 410301181Sadrian /* 411301181Sadrian * XXX TODO: here is where we'd parse active profile 412301181Sadrian * info and make driver/stack choices as appropriate. 413301181Sadrian */ 414301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 415301181Sadrian "(MCI) TODO: Recv GPM COEX BT_Profile_Info.\n"); 416301181Sadrian break; 417301181Sadrian 418301181Sadrian case MCI_GPM_COEX_BT_STATUS_UPDATE: 419301181Sadrian seq_num = *((uint32_t *)(rx_payload + 12)); 420301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 421301181Sadrian "(MCI) Recv GPM COEX BT_Status_Update: SEQ=%d\n", 422301181Sadrian seq_num); 423301181Sadrian break; 424301181Sadrian 425301181Sadrian default: 426301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 427301181Sadrian "(MCI) Unknown GPM COEX message = 0x%02x\n", opcode); 428301181Sadrian break; 429301181Sadrian } 430301181Sadrian} 431301181Sadrian 432301181Sadrianvoid 433301181Sadrianath_btcoex_mci_intr(struct ath_softc *sc) 434301181Sadrian{ 435301181Sadrian uint32_t mciInt, mciIntRxMsg; 436301181Sadrian uint32_t offset, subtype, opcode; 437301181Sadrian uint32_t *pGpm; 438301181Sadrian uint32_t more_data = HAL_MCI_GPM_MORE; 439301182Sgnn int8_t value_dbm; 440301181Sadrian bool skip_gpm = false; 441301181Sadrian 442301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, "%s: called\n", __func__); 443301181Sadrian 444301181Sadrian ath_hal_btcoex_mci_get_interrupt(sc->sc_ah, &mciInt, &mciIntRxMsg); 445301181Sadrian 446301181Sadrian if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_ENABLE, 447301181Sadrian NULL) == 0) { 448301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, 449301181Sadrian HAL_MCI_STATE_INIT_GPM_OFFSET, NULL); 450301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 451301181Sadrian "(MCI) INTR but MCI_disabled\n"); 452301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 453301181Sadrian "(MCI) MCI interrupt: mciInt = 0x%x, mciIntRxMsg = 0x%x\n", 454301181Sadrian mciInt, mciIntRxMsg); 455301181Sadrian return; 456301181Sadrian } 457301181Sadrian 458301181Sadrian if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_REQ_WAKE) { 459301181Sadrian uint32_t payload4[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 460301181Sadrian 0xffffff00}; 461301181Sadrian 462301181Sadrian /* 463301181Sadrian * The following REMOTE_RESET and SYS_WAKING used to sent 464301181Sadrian * only when BT wake up. Now they are always sent, as a 465301181Sadrian * recovery method to reset BT MCI's RX alignment. 466301181Sadrian */ 467301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 468301181Sadrian "(MCI) 1. INTR Send REMOTE_RESET\n"); 469301181Sadrian ath_hal_btcoex_mci_send_message(sc->sc_ah, 470301181Sadrian MCI_REMOTE_RESET, 0, payload4, 16, AH_TRUE, AH_FALSE); 471301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 472301181Sadrian "(MCI) 1. INTR Send SYS_WAKING\n"); 473301181Sadrian ath_hal_btcoex_mci_send_message(sc->sc_ah, 474301181Sadrian MCI_SYS_WAKING, 0, NULL, 0, AH_TRUE, AH_FALSE); 475301181Sadrian 476301181Sadrian mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_REQ_WAKE; 477301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, 478301181Sadrian HAL_MCI_STATE_RESET_REQ_WAKE, NULL); 479301181Sadrian 480301181Sadrian /* always do this for recovery and 2G/5G toggling and LNA_TRANS */ 481301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 482301181Sadrian "(MCI) 1. Set BT state to AWAKE.\n"); 483301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, 484301181Sadrian HAL_MCI_STATE_SET_BT_AWAKE, NULL); 485301181Sadrian } 486301181Sadrian 487301181Sadrian /* Processing SYS_WAKING/SYS_SLEEPING */ 488301181Sadrian if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SYS_WAKING) { 489301181Sadrian mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SYS_WAKING; 490301181Sadrian if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT, 491301181Sadrian NULL) == MCI_BT_SLEEP) { 492301181Sadrian if (ath_hal_btcoex_mci_state(sc->sc_ah, 493301181Sadrian HAL_MCI_STATE_REMOTE_SLEEP, NULL) == MCI_BT_SLEEP) { 494301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 495301181Sadrian "(MCI) 2. BT stays in SLEEP mode.\n"); 496301181Sadrian } else { 497301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 498301181Sadrian "(MCI) 2. Set BT state to AWAKE.\n"); 499301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, 500301181Sadrian HAL_MCI_STATE_SET_BT_AWAKE, NULL); 501301181Sadrian } 502301181Sadrian } else { 503301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 504301181Sadrian "(MCI) 2. BT stays in AWAKE mode.\n"); 505301181Sadrian } 506301181Sadrian } 507301181Sadrian 508301181Sadrian if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) { 509301181Sadrian mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING; 510301181Sadrian if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT, 511301181Sadrian NULL) == MCI_BT_AWAKE) { 512301181Sadrian if (ath_hal_btcoex_mci_state(sc->sc_ah, 513301181Sadrian HAL_MCI_STATE_REMOTE_SLEEP, NULL) == MCI_BT_AWAKE) { 514301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 515301181Sadrian "(MCI) 3. BT stays in AWAKE mode.\n"); 516301181Sadrian } else { 517301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 518301181Sadrian "(MCI) 3. Set BT state to SLEEP.\n"); 519301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, 520301181Sadrian HAL_MCI_STATE_SET_BT_SLEEP, NULL); 521301181Sadrian } 522301181Sadrian } else { 523301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 524301181Sadrian "(MCI) 3. BT stays in SLEEP mode.\n"); 525301181Sadrian } 526301181Sadrian } 527301181Sadrian 528301181Sadrian /* 529301181Sadrian * Recover from out-of-order / wrong-offset GPM messages. 530301181Sadrian */ 531301181Sadrian if ((mciInt & HAL_MCI_INTERRUPT_RX_INVALID_HDR) || 532301181Sadrian (mciInt & HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) { 533301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 534301181Sadrian "(MCI) MCI RX broken, skip GPM messages\n"); 535301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, 536301181Sadrian HAL_MCI_STATE_RECOVER_RX, NULL); 537301181Sadrian skip_gpm = true; 538301181Sadrian } 539301181Sadrian 540301181Sadrian if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SCHD_INFO) { 541301181Sadrian mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SCHD_INFO; 542301181Sadrian offset = ath_hal_btcoex_mci_state(sc->sc_ah, 543301181Sadrian HAL_MCI_STATE_LAST_SCHD_MSG_OFFSET, NULL); 544301181Sadrian } 545301181Sadrian 546301181Sadrian /* 547301181Sadrian * Parse GPM messages. 548301181Sadrian */ 549301181Sadrian if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_GPM) { 550301181Sadrian mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_GPM; 551301181Sadrian 552301181Sadrian while (more_data == HAL_MCI_GPM_MORE) { 553301181Sadrian pGpm = (void *) sc->sc_btcoex.gpm_buf; 554301181Sadrian offset = ath_hal_btcoex_mci_state(sc->sc_ah, 555301181Sadrian HAL_MCI_STATE_NEXT_GPM_OFFSET, &more_data); 556301181Sadrian 557301181Sadrian if (offset == HAL_MCI_GPM_INVALID) 558301181Sadrian break; 559301181Sadrian pGpm += (offset >> 2); 560301181Sadrian /* 561301181Sadrian * The first DWORD is a timer. 562301181Sadrian * The real data starts from the second DWORD. 563301181Sadrian */ 564301181Sadrian subtype = MCI_GPM_TYPE(pGpm); 565301181Sadrian opcode = MCI_GPM_OPCODE(pGpm); 566301181Sadrian 567301181Sadrian if (!skip_gpm) { 568301181Sadrian if (MCI_GPM_IS_CAL_TYPE(subtype)) { 569301181Sadrian ath_btcoex_mci_cal_msg(sc, subtype, 570301181Sadrian (uint8_t*) pGpm); 571301181Sadrian } else { 572301181Sadrian switch (subtype) { 573301181Sadrian case MCI_GPM_COEX_AGENT: 574301181Sadrian ath_btcoex_mci_coex_msg(sc, 575301181Sadrian opcode, (uint8_t*) pGpm); 576301181Sadrian break; 577301181Sadrian case MCI_GPM_BT_DEBUG: 578301181Sadrian device_printf(sc->sc_dev, 579301181Sadrian "(MCI) TODO: GPM_BT_DEBUG!\n"); 580301181Sadrian break; 581301181Sadrian default: 582301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 583301181Sadrian "(MCI) Unknown GPM message.\n"); 584301181Sadrian break; 585301181Sadrian } 586301181Sadrian } 587301181Sadrian } 588301181Sadrian MCI_GPM_RECYCLE(pGpm); 589301181Sadrian } 590301181Sadrian } 591301181Sadrian 592301181Sadrian /* 593301181Sadrian * This is monitoring/management information messages, so the driver 594301181Sadrian * layer can hook in and dynamically adjust things like aggregation 595301181Sadrian * size, expected bluetooth/wifi traffic throughput, etc. 596301181Sadrian * 597301181Sadrian * None of that is done right now; it just passes off the values 598301181Sadrian * to the HAL so it can update its internal state as appropriate. 599301181Sadrian * This code just prints out the values for debugging purposes. 600301181Sadrian */ 601301181Sadrian if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_MONITOR) { 602301181Sadrian if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL) { 603301181Sadrian mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL; 604301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) LNA_CONTROL\n"); 605301181Sadrian } 606301181Sadrian if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO) { 607301181Sadrian mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO; 608301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) LNA_INFO\n"); 609301181Sadrian } 610301181Sadrian if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO) { 611301182Sgnn value_dbm = ath_hal_btcoex_mci_state(sc->sc_ah, 612301181Sadrian HAL_MCI_STATE_CONT_RSSI_POWER, NULL); 613301181Sadrian 614301181Sadrian mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO; 615301181Sadrian if (ath_hal_btcoex_mci_state(sc->sc_ah, 616301181Sadrian HAL_MCI_STATE_CONT_TXRX, NULL)) { 617301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 618301181Sadrian "(MCI) CONT_INFO: (tx) pri = %d, pwr = %d dBm\n", 619301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, 620301181Sadrian HAL_MCI_STATE_CONT_PRIORITY, NULL), 621301181Sadrian value_dbm); 622301181Sadrian } else { 623301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 624301181Sadrian "(MCI) CONT_INFO: (rx) pri = %d, rssi = %d dBm\n", 625301181Sadrian ath_hal_btcoex_mci_state(sc->sc_ah, 626301181Sadrian HAL_MCI_STATE_CONT_PRIORITY, NULL), 627301181Sadrian value_dbm); 628301181Sadrian } 629301181Sadrian } 630301181Sadrian if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK) { 631301181Sadrian mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK; 632301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) CONT_NACK\n"); 633301181Sadrian } 634301181Sadrian if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_RST) { 635301181Sadrian mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_RST; 636301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) CONT_RST\n"); 637301181Sadrian } 638301181Sadrian } 639301181Sadrian 640301181Sadrian /* 641301181Sadrian * Recover the state engine if we hit an invalid header/timeout. 642301181Sadrian * This is the final part of GPT out-of-sync recovery. 643301181Sadrian */ 644301181Sadrian if ((mciInt & HAL_MCI_INTERRUPT_RX_INVALID_HDR) || 645301181Sadrian (mciInt & HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) { 646301181Sadrian ath_btcoex_mci_event(sc, ATH_COEX_EVENT_BT_NOOP, NULL); 647301181Sadrian mciInt &= ~(HAL_MCI_INTERRUPT_RX_INVALID_HDR | 648301181Sadrian HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT); 649301181Sadrian } 650301181Sadrian 651301181Sadrian if (mciIntRxMsg & 0xfffffffe) { 652301181Sadrian DPRINTF(sc, ATH_DEBUG_BTCOEX, 653301181Sadrian "(MCI) Not processed IntRxMsg = 0x%x\n", mciIntRxMsg); 654301181Sadrian } 655301181Sadrian} 656