1/*-
2 * Copyright (c) 2014 Qualcomm Atheros, Inc.
3 * Copyright (c) 2016 Adrian Chadd <adrian@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer,
11 *    without modification.
12 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
13 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
14 *    redistribution must be conditioned upon including a substantially
15 *    similar Disclaimer requirement for further binary redistribution.
16 *
17 * NO WARRANTY
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
21 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
23 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28 * THE POSSIBILITY OF SUCH DAMAGES.
29 */
30#include <sys/cdefs.h>
31/*
32 * This implements the MCI bluetooth coexistence handling.
33 */
34#include "opt_ath.h"
35#include "opt_inet.h"
36#include "opt_wlan.h"
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/sysctl.h>
41#include <sys/kernel.h>
42#include <sys/lock.h>
43#include <sys/malloc.h>
44#include <sys/mutex.h>
45#include <sys/errno.h>
46
47#include <machine/bus.h>
48#include <machine/resource.h>
49
50#include <sys/bus.h>
51
52#include <sys/socket.h>
53
54#include <net/if.h>
55#include <net/if_var.h>
56#include <net/if_media.h>
57#include <net/if_arp.h>
58#include <net/ethernet.h>		/* XXX for ether_sprintf */
59
60#include <net80211/ieee80211_var.h>
61
62#include <net/bpf.h>
63
64#ifdef INET
65#include <netinet/in.h>
66#include <netinet/if_ether.h>
67#endif
68
69#include <dev/ath/if_athvar.h>
70#include <dev/ath/if_ath_debug.h>
71#include <dev/ath/if_ath_descdma.h>
72#include <dev/ath/if_ath_btcoex.h>
73
74#include <dev/ath/if_ath_btcoex_mci.h>
75
76MALLOC_DECLARE(M_ATHDEV);
77
78#define	ATH_MCI_GPM_MAX_ENTRY		16
79#define	ATH_MCI_GPM_BUF_SIZE		(ATH_MCI_GPM_MAX_ENTRY * 16)
80#define	ATH_MCI_SCHED_BUF_SIZE		(16 * 16) /* 16 entries, 4 dword each */
81
82static void ath_btcoex_mci_update_wlan_channels(struct ath_softc *sc);
83
84int
85ath_btcoex_mci_attach(struct ath_softc *sc)
86{
87	int buflen, error;
88
89	buflen = ATH_MCI_GPM_BUF_SIZE + ATH_MCI_SCHED_BUF_SIZE;
90	error = ath_descdma_alloc_desc(sc, &sc->sc_btcoex.buf, NULL,
91	    "MCI bufs", buflen, 1);
92	if (error != 0) {
93		device_printf(sc->sc_dev, "%s: failed to alloc MCI RAM\n",
94		    __func__);
95		return (error);
96	}
97
98	/* Yes, we're going to do bluetooth MCI coex */
99	sc->sc_btcoex_mci = 1;
100
101	/* Initialise the wlan channel mapping */
102	sc->sc_btcoex.wlan_channels[0] = 0x00000000;
103	sc->sc_btcoex.wlan_channels[1] = 0xffffffff;
104	sc->sc_btcoex.wlan_channels[2] = 0xffffffff;
105	sc->sc_btcoex.wlan_channels[3] = 0x7fffffff;
106
107	/*
108	 * Ok, so the API is a bit odd. It assumes sched_addr is
109	 * after gpm_addr, and it does math to figure out the right
110	 * sched_buf pointer.
111	 *
112	 * So, set gpm_addr to buf, sched_addr to gpm_addr + ATH_MCI_GPM_BUF_SIZE,
113	 * the HAL call with do (gpm_buf + (sched_addr - gpm_addr)) to
114	 * set sched_buf, and we're "golden".
115	 *
116	 * Note, it passes in 'len' here (gpm_len) as
117	 * ATH_MCI_GPM_BUF_SIZE >> 4.  My guess is that it's 16
118	 * bytes per entry and we're storing 16 entries.
119	 */
120	sc->sc_btcoex.gpm_buf = (void *) sc->sc_btcoex.buf.dd_desc;
121	sc->sc_btcoex.sched_buf = sc->sc_btcoex.gpm_buf +
122	    ATH_MCI_GPM_BUF_SIZE;
123
124	sc->sc_btcoex.gpm_paddr = sc->sc_btcoex.buf.dd_desc_paddr;
125	sc->sc_btcoex.sched_paddr = sc->sc_btcoex.gpm_paddr +
126	    ATH_MCI_GPM_BUF_SIZE;
127
128	/* memset the gpm buffer with MCI_GPM_RSVD_PATTERN */
129	memset(sc->sc_btcoex.gpm_buf, 0xfe, buflen);
130
131	/*
132	 * This is an unfortunate x86'ism in the HAL - the
133	 * HAL code expects the passed in buffer to be
134	 * coherent, and doesn't implement /any/ kind
135	 * of buffer sync operations at all.
136	 *
137	 * So, this code will only work on dma coherent buffers
138	 * and will behave poorly on non-coherent systems.
139	 * Fixing this would require some HAL surgery so it
140	 * actually /did/ the buffer flushing as appropriate.
141	 */
142	ath_hal_btcoex_mci_setup(sc->sc_ah,
143	    sc->sc_btcoex.gpm_paddr,
144	    sc->sc_btcoex.gpm_buf,
145	    ATH_MCI_GPM_BUF_SIZE >> 4,
146	    sc->sc_btcoex.sched_paddr);
147
148	return (0);
149}
150
151/*
152 * Detach btcoex from the given interface
153 */
154int
155ath_btcoex_mci_detach(struct ath_softc *sc)
156{
157
158	ath_hal_btcoex_mci_detach(sc->sc_ah);
159	ath_descdma_cleanup(sc, &sc->sc_btcoex.buf, NULL);
160	return (0);
161}
162
163/*
164 * Configure or disable bluetooth coexistence on the given channel.
165 *
166 * For MCI, we just use the top-level enable/disable flag, and
167 * then the MCI reset / channel update path will configure things
168 * appropriately based on the current band.
169 */
170int
171ath_btcoex_mci_enable(struct ath_softc *sc,
172    const struct ieee80211_channel *chan)
173{
174
175	/*
176	 * Always reconfigure stomp-all for now, so wlan wins.
177	 *
178	 * The default weights still don't allow beacons to win,
179	 * so unless you set net.wlan.X.bmiss_max to something higher,
180	 * net80211 will disconnect you during a HCI INQUIRY command.
181	 *
182	 * The longer-term solution is to dynamically adjust whether
183	 * bmiss happens based on bluetooth requirements, and look at
184	 * making the individual stomp bits configurable.
185	 */
186	ath_hal_btcoex_set_weights(sc->sc_ah, HAL_BT_COEX_STOMP_ALL);
187
188	/*
189	 * update wlan channels so the firmware knows what channels it
190	 * can/can't use.
191	 */
192	ath_btcoex_mci_update_wlan_channels(sc);
193
194	return (0);
195}
196
197/*
198 * XXX TODO: turn into general btcoex, and then make this
199 * the MCI specific bits.
200 */
201static void
202ath_btcoex_mci_event(struct ath_softc *sc, ATH_BT_COEX_EVENT nevent,
203    void *param)
204{
205
206	if (! sc->sc_btcoex_mci)
207		return;
208
209	/*
210	 * Check whether we need to flush our local profile cache.
211	 * If we do, then at (XXX TODO) we should flush our state,
212	 * then wait for the MCI response with the updated profile list.
213	 */
214	if (ath_hal_btcoex_mci_state(sc->sc_ah,
215	    HAL_MCI_STATE_NEED_FLUSH_BT_INFO, NULL) != 0) {
216		uint32_t data = 0;
217
218		if (ath_hal_btcoex_mci_state(sc->sc_ah,
219		    HAL_MCI_STATE_ENABLE, NULL) != 0) {
220			DPRINTF(sc, ATH_DEBUG_BTCOEX,
221			    "(MCI) Flush BT profile\n");
222			/*
223			 * XXX TODO: flush profile state on the ath(4)
224			 * driver side; subsequent messages will come
225			 * through with the current list of active
226			 * profiles.
227			 */
228			ath_hal_btcoex_mci_state(sc->sc_ah,
229			    HAL_MCI_STATE_NEED_FLUSH_BT_INFO, &data);
230			ath_hal_btcoex_mci_state(sc->sc_ah,
231			    HAL_MCI_STATE_SEND_STATUS_QUERY, NULL);
232		}
233	}
234	if (nevent == ATH_COEX_EVENT_BT_NOOP) {
235		DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) BT_NOOP\n");
236		return;
237	}
238}
239
240static void
241ath_btcoex_mci_send_gpm(struct ath_softc *sc, uint32_t *payload)
242{
243
244	ath_hal_btcoex_mci_send_message(sc->sc_ah, MCI_GPM, 0, payload, 16,
245	    AH_FALSE, AH_TRUE);
246}
247
248/*
249 * This starts a BT calibration.  It requires a chip reset.
250 */
251static int
252ath_btcoex_mci_bt_cal_do(struct ath_softc *sc, int tx_timeout, int rx_timeout)
253{
254
255	device_printf(sc->sc_dev, "%s: TODO!\n", __func__);
256	return (0);
257}
258
259static void
260ath_btcoex_mci_cal_msg(struct ath_softc *sc, uint8_t opcode,
261    uint8_t *rx_payload)
262{
263	uint32_t payload[4] = {0, 0, 0, 0};
264
265	switch (opcode) {
266	case MCI_GPM_BT_CAL_REQ:
267		DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_REQ\n");
268		if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT,
269		    NULL) == MCI_BT_AWAKE) {
270			ath_hal_btcoex_mci_state(sc->sc_ah,
271			    HAL_MCI_STATE_SET_BT_CAL_START, NULL);
272			ath_btcoex_mci_bt_cal_do(sc, 1000, 1000);
273		} else {
274			DPRINTF(sc, ATH_DEBUG_BTCOEX,
275			    "(MCI) State mismatches: %d\n",
276			    ath_hal_btcoex_mci_state(sc->sc_ah,
277			    HAL_MCI_STATE_BT, NULL));
278		}
279		break;
280	case MCI_GPM_BT_CAL_DONE:
281		DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_DONE\n");
282		if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT,
283		    NULL) == MCI_BT_CAL) {
284			DPRINTF(sc, ATH_DEBUG_BTCOEX,
285			    "(MCI) ERROR ILLEGAL!\n");
286		} else {
287			DPRINTF(sc, ATH_DEBUG_BTCOEX,
288			    "(MCI) BT not in CAL state.\n");
289		}
290		break;
291	case MCI_GPM_BT_CAL_GRANT:
292		DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) receive BT_CAL_GRANT\n");
293		/* Send WLAN_CAL_DONE for now */
294		DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) Send WLAN_CAL_DONE\n");
295		MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE);
296		ath_btcoex_mci_send_gpm(sc, &payload[0]);
297		break;
298	default:
299		DPRINTF(sc, ATH_DEBUG_BTCOEX,
300		    "(MCI) Unknown GPM CAL message.\n");
301		break;
302	}
303}
304
305/*
306 * Update the bluetooth channel map.
307 *
308 * This map tells the bluetooth device which bluetooth channels
309 * are available for data.
310 *
311 * For 5GHz, all channels are available.
312 * For 2GHz, the current wifi channel range is blocked out,
313 *   and the rest are available.
314 *
315 * This narrows which frequencies are used by the device when
316 * it initiates a transfer, thus hopefully reducing the chances
317 * of collisions (both hopefully on the current device and
318 * other devices in the same channel.)
319 */
320static void
321ath_btcoex_mci_update_wlan_channels(struct ath_softc *sc)
322{
323	struct ieee80211com *ic = &sc->sc_ic;
324	struct ieee80211_channel *chan = ic->ic_curchan;
325	uint32_t channel_info[4] =
326	    { 0x00000000, 0xffffffff, 0xffffffff, 0x7fffffff };
327	int32_t wl_chan, bt_chan, bt_start = 0, bt_end = 79;
328
329	/* BT channel frequency is 2402 + k, k = 0 ~ 78 */
330	if (IEEE80211_IS_CHAN_2GHZ(chan)) {
331		wl_chan = chan->ic_freq - 2402;
332		if (IEEE80211_IS_CHAN_HT40U(chan)) {
333			bt_start = wl_chan - 10;
334			bt_end = wl_chan + 30;
335		} else if (IEEE80211_IS_CHAN_HT40D(chan)) {
336			bt_start = wl_chan - 30;
337			bt_end = wl_chan + 10;
338		} else {
339			/* Assume 20MHz */
340			bt_start = wl_chan - 10;
341			bt_end = wl_chan + 10;
342		}
343
344		bt_start -= 7;
345		bt_end += 7;
346
347		if (bt_start < 0) {
348			bt_start = 0;
349		}
350		if (bt_end > MCI_NUM_BT_CHANNELS) {
351			bt_end = MCI_NUM_BT_CHANNELS;
352		}
353		DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) WLAN use channel %d\n",
354		    chan->ic_freq);
355		DPRINTF(sc, ATH_DEBUG_BTCOEX,
356		    "(MCI) mask BT channel %d - %d\n", bt_start, bt_end);
357		for (bt_chan = bt_start; bt_chan < bt_end; bt_chan++) {
358			MCI_GPM_CLR_CHANNEL_BIT(&channel_info[0], bt_chan);
359		}
360	} else {
361		DPRINTF(sc, ATH_DEBUG_BTCOEX,
362		    "(MCI) WLAN not use any 2G channel, unmask all for BT\n");
363	}
364	ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_SEND_WLAN_CHANNELS,
365	    &channel_info[0]);
366}
367
368static void
369ath_btcoex_mci_coex_msg(struct ath_softc *sc, uint8_t opcode,
370    uint8_t *rx_payload)
371{
372	uint32_t version;
373	uint8_t major;
374	uint8_t minor;
375	uint32_t seq_num;
376
377	switch (opcode) {
378	case MCI_GPM_COEX_VERSION_QUERY:
379		DPRINTF(sc, ATH_DEBUG_BTCOEX,
380		    "(MCI) Recv GPM COEX Version Query.\n");
381		version = ath_hal_btcoex_mci_state(sc->sc_ah,
382		    HAL_MCI_STATE_SEND_WLAN_COEX_VERSION, NULL);
383		break;
384
385	case MCI_GPM_COEX_VERSION_RESPONSE:
386		DPRINTF(sc, ATH_DEBUG_BTCOEX,
387		    "(MCI) Recv GPM COEX Version Response.\n");
388		major = *(rx_payload + MCI_GPM_COEX_B_MAJOR_VERSION);
389		minor = *(rx_payload + MCI_GPM_COEX_B_MINOR_VERSION);
390		DPRINTF(sc, ATH_DEBUG_BTCOEX,
391		    "(MCI) BT Coex version: %d.%d\n", major, minor);
392		version = (major << 8) + minor;
393		version = ath_hal_btcoex_mci_state(sc->sc_ah,
394		    HAL_MCI_STATE_SET_BT_COEX_VERSION, &version);
395		break;
396
397	case MCI_GPM_COEX_STATUS_QUERY:
398		DPRINTF(sc, ATH_DEBUG_BTCOEX,
399		    "(MCI) Recv GPM COEX Status Query = 0x%02x.\n",
400		    *(rx_payload + MCI_GPM_COEX_B_WLAN_BITMAP));
401		ath_hal_btcoex_mci_state(sc->sc_ah,
402		    HAL_MCI_STATE_SEND_WLAN_CHANNELS, NULL);
403		break;
404
405	case MCI_GPM_COEX_BT_PROFILE_INFO:
406		/*
407		 * XXX TODO: here is where we'd parse active profile
408		 * info and make driver/stack choices as appropriate.
409		 */
410		DPRINTF(sc, ATH_DEBUG_BTCOEX,
411		    "(MCI) TODO: Recv GPM COEX BT_Profile_Info.\n");
412		break;
413
414	case MCI_GPM_COEX_BT_STATUS_UPDATE:
415		seq_num = *((uint32_t *)(rx_payload + 12));
416		DPRINTF(sc, ATH_DEBUG_BTCOEX,
417		    "(MCI) Recv GPM COEX BT_Status_Update: SEQ=%d\n",
418		    seq_num);
419		break;
420
421	default:
422		DPRINTF(sc, ATH_DEBUG_BTCOEX,
423		    "(MCI) Unknown GPM COEX message = 0x%02x\n", opcode);
424		break;
425	}
426}
427
428void
429ath_btcoex_mci_intr(struct ath_softc *sc)
430{
431	uint32_t mciInt, mciIntRxMsg;
432	uint32_t offset, subtype, opcode;
433	uint32_t *pGpm;
434	uint32_t more_data = HAL_MCI_GPM_MORE;
435	int8_t value_dbm;
436	bool skip_gpm = false;
437
438	DPRINTF(sc, ATH_DEBUG_BTCOEX, "%s: called\n", __func__);
439
440	ath_hal_btcoex_mci_get_interrupt(sc->sc_ah, &mciInt, &mciIntRxMsg);
441
442	if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_ENABLE,
443	    NULL) == 0) {
444		ath_hal_btcoex_mci_state(sc->sc_ah,
445		    HAL_MCI_STATE_INIT_GPM_OFFSET, NULL);
446		DPRINTF(sc, ATH_DEBUG_BTCOEX,
447		    "(MCI) INTR but MCI_disabled\n");
448		DPRINTF(sc, ATH_DEBUG_BTCOEX,
449		    "(MCI) MCI interrupt: mciInt = 0x%x, mciIntRxMsg = 0x%x\n",
450		    mciInt, mciIntRxMsg);
451		return;
452	}
453
454	if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_REQ_WAKE) {
455		uint32_t payload4[4] = { 0xffffffff, 0xffffffff, 0xffffffff,
456		    0xffffff00};
457
458		/*
459		 * The following REMOTE_RESET and SYS_WAKING used to sent
460		 * only when BT wake up. Now they are always sent, as a
461		 * recovery method to reset BT MCI's RX alignment.
462		 */
463		DPRINTF(sc, ATH_DEBUG_BTCOEX,
464		    "(MCI) 1. INTR Send REMOTE_RESET\n");
465		ath_hal_btcoex_mci_send_message(sc->sc_ah,
466		    MCI_REMOTE_RESET, 0, payload4, 16, AH_TRUE, AH_FALSE);
467		DPRINTF(sc, ATH_DEBUG_BTCOEX,
468		    "(MCI) 1. INTR Send SYS_WAKING\n");
469		ath_hal_btcoex_mci_send_message(sc->sc_ah,
470		    MCI_SYS_WAKING, 0, NULL, 0, AH_TRUE, AH_FALSE);
471
472		mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_REQ_WAKE;
473		ath_hal_btcoex_mci_state(sc->sc_ah,
474		    HAL_MCI_STATE_RESET_REQ_WAKE, NULL);
475
476		/* always do this for recovery and 2G/5G toggling and LNA_TRANS */
477		DPRINTF(sc, ATH_DEBUG_BTCOEX,
478		    "(MCI) 1. Set BT state to AWAKE.\n");
479		ath_hal_btcoex_mci_state(sc->sc_ah,
480		    HAL_MCI_STATE_SET_BT_AWAKE, NULL);
481	}
482
483	/* Processing SYS_WAKING/SYS_SLEEPING */
484	if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SYS_WAKING) {
485		mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SYS_WAKING;
486		if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT,
487		    NULL) == MCI_BT_SLEEP) {
488			if (ath_hal_btcoex_mci_state(sc->sc_ah,
489			    HAL_MCI_STATE_REMOTE_SLEEP, NULL) == MCI_BT_SLEEP) {
490				DPRINTF(sc, ATH_DEBUG_BTCOEX,
491				    "(MCI) 2. BT stays in SLEEP mode.\n");
492			} else {
493				DPRINTF(sc, ATH_DEBUG_BTCOEX,
494				    "(MCI) 2. Set BT state to AWAKE.\n");
495				ath_hal_btcoex_mci_state(sc->sc_ah,
496				    HAL_MCI_STATE_SET_BT_AWAKE, NULL);
497			}
498		} else {
499			DPRINTF(sc, ATH_DEBUG_BTCOEX,
500			    "(MCI) 2. BT stays in AWAKE mode.\n");
501		}
502	}
503
504	if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) {
505		mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING;
506		if (ath_hal_btcoex_mci_state(sc->sc_ah, HAL_MCI_STATE_BT,
507		    NULL) == MCI_BT_AWAKE) {
508			if (ath_hal_btcoex_mci_state(sc->sc_ah,
509			    HAL_MCI_STATE_REMOTE_SLEEP, NULL) == MCI_BT_AWAKE) {
510				DPRINTF(sc, ATH_DEBUG_BTCOEX,
511				    "(MCI) 3. BT stays in AWAKE mode.\n");
512			} else {
513				DPRINTF(sc, ATH_DEBUG_BTCOEX,
514				    "(MCI) 3. Set BT state to SLEEP.\n");
515				ath_hal_btcoex_mci_state(sc->sc_ah,
516				    HAL_MCI_STATE_SET_BT_SLEEP, NULL);
517			}
518		} else {
519			DPRINTF(sc, ATH_DEBUG_BTCOEX,
520			    "(MCI) 3. BT stays in SLEEP mode.\n");
521		}
522	}
523
524	/*
525	 * Recover from out-of-order / wrong-offset GPM messages.
526	 */
527	if ((mciInt & HAL_MCI_INTERRUPT_RX_INVALID_HDR) ||
528	    (mciInt & HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) {
529		DPRINTF(sc, ATH_DEBUG_BTCOEX,
530		    "(MCI) MCI RX broken, skip GPM messages\n");
531		ath_hal_btcoex_mci_state(sc->sc_ah,
532		    HAL_MCI_STATE_RECOVER_RX, NULL);
533		skip_gpm = true;
534	}
535
536	if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_SCHD_INFO) {
537		mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_SCHD_INFO;
538		offset = ath_hal_btcoex_mci_state(sc->sc_ah,
539		    HAL_MCI_STATE_LAST_SCHD_MSG_OFFSET, NULL);
540	}
541
542	/*
543	 * Parse GPM messages.
544	 */
545	if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_GPM) {
546		mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_GPM;
547
548		while (more_data == HAL_MCI_GPM_MORE) {
549			pGpm = (void *) sc->sc_btcoex.gpm_buf;
550			offset = ath_hal_btcoex_mci_state(sc->sc_ah,
551			    HAL_MCI_STATE_NEXT_GPM_OFFSET, &more_data);
552
553			if (offset == HAL_MCI_GPM_INVALID)
554				break;
555			pGpm += (offset >> 2);
556			/*
557			 * The first DWORD is a timer.
558			 * The real data starts from the second DWORD.
559			 */
560			subtype = MCI_GPM_TYPE(pGpm);
561			opcode = MCI_GPM_OPCODE(pGpm);
562
563			if (!skip_gpm) {
564				if (MCI_GPM_IS_CAL_TYPE(subtype)) {
565					ath_btcoex_mci_cal_msg(sc, subtype,
566					    (uint8_t*) pGpm);
567				} else {
568					switch (subtype) {
569					case MCI_GPM_COEX_AGENT:
570						ath_btcoex_mci_coex_msg(sc,
571						    opcode, (uint8_t*) pGpm);
572					break;
573					case MCI_GPM_BT_DEBUG:
574						device_printf(sc->sc_dev,
575						    "(MCI) TODO: GPM_BT_DEBUG!\n");
576					break;
577					default:
578						DPRINTF(sc, ATH_DEBUG_BTCOEX,
579						    "(MCI) Unknown GPM message.\n");
580						break;
581					}
582				}
583			}
584			MCI_GPM_RECYCLE(pGpm);
585		}
586	}
587
588	/*
589	 * This is monitoring/management information messages, so the driver
590	 * layer can hook in and dynamically adjust things like aggregation
591	 * size, expected bluetooth/wifi traffic throughput, etc.
592	 *
593	 * None of that is done right now; it just passes off the values
594	 * to the HAL so it can update its internal state as appropriate.
595	 * This code just prints out the values for debugging purposes.
596	 */
597	if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_MONITOR) {
598		if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL) {
599			mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL;
600			DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) LNA_CONTROL\n");
601		}
602		if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO) {
603			mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO;
604			DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) LNA_INFO\n");
605		}
606		if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO) {
607			value_dbm = ath_hal_btcoex_mci_state(sc->sc_ah,
608			    HAL_MCI_STATE_CONT_RSSI_POWER, NULL);
609
610			mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO;
611			if (ath_hal_btcoex_mci_state(sc->sc_ah,
612			    HAL_MCI_STATE_CONT_TXRX, NULL)) {
613				DPRINTF(sc, ATH_DEBUG_BTCOEX,
614				    "(MCI) CONT_INFO: (tx) pri = %d, pwr = %d dBm\n",
615				ath_hal_btcoex_mci_state(sc->sc_ah,
616				    HAL_MCI_STATE_CONT_PRIORITY, NULL),
617				    value_dbm);
618			} else {
619				DPRINTF(sc, ATH_DEBUG_BTCOEX,
620				    "(MCI) CONT_INFO: (rx) pri = %d, rssi = %d dBm\n",
621				ath_hal_btcoex_mci_state(sc->sc_ah,
622				    HAL_MCI_STATE_CONT_PRIORITY, NULL),
623				    value_dbm);
624			}
625		}
626		if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK) {
627			mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK;
628			DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) CONT_NACK\n");
629		}
630		if (mciIntRxMsg & HAL_MCI_INTERRUPT_RX_MSG_CONT_RST) {
631			mciIntRxMsg &= ~HAL_MCI_INTERRUPT_RX_MSG_CONT_RST;
632			DPRINTF(sc, ATH_DEBUG_BTCOEX, "(MCI) CONT_RST\n");
633		}
634	}
635
636	/*
637	 * Recover the state engine if we hit an invalid header/timeout.
638	 * This is the final part of GPT out-of-sync recovery.
639	 */
640	if ((mciInt & HAL_MCI_INTERRUPT_RX_INVALID_HDR) ||
641	    (mciInt & HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) {
642		ath_btcoex_mci_event(sc, ATH_COEX_EVENT_BT_NOOP, NULL);
643		mciInt &= ~(HAL_MCI_INTERRUPT_RX_INVALID_HDR |
644		    HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT);
645	}
646
647	if (mciIntRxMsg & 0xfffffffe) {
648		DPRINTF(sc, ATH_DEBUG_BTCOEX,
649		    "(MCI) Not processed IntRxMsg = 0x%x\n", mciIntRxMsg);
650	}
651}
652