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