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