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