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