xref: /openbsd-src/sys/dev/sdmmc/if_bwfm_sdio.c (revision 471aeecfc619bc9b69519928152daf993376c2a1)
1*471aeecfSnaddy /* $OpenBSD: if_bwfm_sdio.c,v 1.44 2022/04/06 18:59:30 naddy Exp $ */
232b2494eSpatrick /*
332b2494eSpatrick  * Copyright (c) 2010-2016 Broadcom Corporation
432b2494eSpatrick  * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se>
532b2494eSpatrick  *
632b2494eSpatrick  * Permission to use, copy, modify, and/or distribute this software for any
732b2494eSpatrick  * purpose with or without fee is hereby granted, provided that the above
832b2494eSpatrick  * copyright notice and this permission notice appear in all copies.
932b2494eSpatrick  *
1032b2494eSpatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1132b2494eSpatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1232b2494eSpatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1332b2494eSpatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1432b2494eSpatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1532b2494eSpatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1632b2494eSpatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1732b2494eSpatrick  */
1832b2494eSpatrick 
1932b2494eSpatrick #include "bpfilter.h"
2032b2494eSpatrick 
2132b2494eSpatrick #include <sys/param.h>
2232b2494eSpatrick #include <sys/systm.h>
2332b2494eSpatrick #include <sys/buf.h>
2432b2494eSpatrick #include <sys/kernel.h>
2532b2494eSpatrick #include <sys/malloc.h>
2632b2494eSpatrick #include <sys/device.h>
2732b2494eSpatrick #include <sys/queue.h>
2832b2494eSpatrick #include <sys/socket.h>
29d7d6fed2Spatrick #include <sys/pool.h>
3032b2494eSpatrick 
31571fa1d2Spatrick #if defined(__HAVE_FDT)
32571fa1d2Spatrick #include <machine/fdt.h>
337a025419Sjsg #include <dev/ofw/openfirm.h>
34571fa1d2Spatrick #endif
35571fa1d2Spatrick 
3632b2494eSpatrick #if NBPFILTER > 0
3732b2494eSpatrick #include <net/bpf.h>
3832b2494eSpatrick #endif
3932b2494eSpatrick #include <net/if.h>
4032b2494eSpatrick #include <net/if_dl.h>
4132b2494eSpatrick #include <net/if_media.h>
4232b2494eSpatrick 
4332b2494eSpatrick #include <netinet/in.h>
4432b2494eSpatrick #include <netinet/if_ether.h>
4532b2494eSpatrick 
4632b2494eSpatrick #include <net80211/ieee80211_var.h>
4732b2494eSpatrick 
488acad61eSmiod #include <dev/sdmmc/sdmmcdevs.h>
4932b2494eSpatrick #include <dev/sdmmc/sdmmcvar.h>
5032b2494eSpatrick 
5132b2494eSpatrick #include <dev/ic/bwfmvar.h>
5232b2494eSpatrick #include <dev/ic/bwfmreg.h>
537f7fa296Spatrick #include <dev/sdmmc/if_bwfm_sdio.h>
5432b2494eSpatrick 
5532b2494eSpatrick #define BWFM_SDIO_CCCR_BRCM_CARDCAP			0xf0
5632b2494eSpatrick #define  BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT	0x02
5732b2494eSpatrick #define  BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT		0x04
5832b2494eSpatrick #define  BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC		0x08
5932b2494eSpatrick #define BWFM_SDIO_CCCR_BRCM_CARDCTRL			0xf1
6032b2494eSpatrick #define  BWFM_SDIO_CCCR_BRCM_CARDCTRL_WLANRESET		0x02
6132b2494eSpatrick #define BWFM_SDIO_CCCR_BRCM_SEPINT			0xf2
6232b2494eSpatrick 
63d2df058dSpatrick /* #define BWFM_DEBUG */
6432b2494eSpatrick #ifdef BWFM_DEBUG
6532b2494eSpatrick #define DPRINTF(x)	do { if (bwfm_debug > 0) printf x; } while (0)
6632b2494eSpatrick #define DPRINTFN(n, x)	do { if (bwfm_debug >= (n)) printf x; } while (0)
6732b2494eSpatrick static int bwfm_debug = 1;
6832b2494eSpatrick #else
6932b2494eSpatrick #define DPRINTF(x)	do { ; } while (0)
7032b2494eSpatrick #define DPRINTFN(n, x)	do { ; } while (0)
7132b2494eSpatrick #endif
7232b2494eSpatrick 
7332b2494eSpatrick #undef DEVNAME
7432b2494eSpatrick #define DEVNAME(sc)	((sc)->sc_sc.sc_dev.dv_xname)
7532b2494eSpatrick 
76d2df058dSpatrick enum bwfm_sdio_clkstate {
77d2df058dSpatrick 	CLK_NONE,
78d2df058dSpatrick 	CLK_SDONLY,
79d2df058dSpatrick 	CLK_PENDING,
80d2df058dSpatrick 	CLK_AVAIL,
81d2df058dSpatrick };
82d2df058dSpatrick 
8332b2494eSpatrick struct bwfm_sdio_softc {
8432b2494eSpatrick 	struct bwfm_softc	  sc_sc;
8532b2494eSpatrick 	struct sdmmc_function	**sc_sf;
86d2df058dSpatrick 	struct rwlock		 *sc_lock;
87d2df058dSpatrick 	void			 *sc_ih;
88571fa1d2Spatrick 	int			  sc_oob;
89d2df058dSpatrick 
90972218f3Spatrick 	int			  sc_initialized;
91972218f3Spatrick 
9232b2494eSpatrick 	uint32_t		  sc_bar0;
93d2df058dSpatrick 	int			  sc_clkstate;
94d2df058dSpatrick 	int			  sc_alp_only;
95d2df058dSpatrick 	int			  sc_sr_enabled;
96e4f06f9cSpatrick 	uint32_t		  sc_console_addr;
97e4f06f9cSpatrick 
98d7d6fed2Spatrick 	char			 *sc_bounce_buf;
99d7d6fed2Spatrick 	size_t			  sc_bounce_size;
100d7d6fed2Spatrick 
101e4f06f9cSpatrick 	char			 *sc_console_buf;
102e4f06f9cSpatrick 	size_t			  sc_console_buf_size;
103e4f06f9cSpatrick 	uint32_t		  sc_console_readidx;
104d2df058dSpatrick 
105d2df058dSpatrick 	struct bwfm_core	 *sc_cc;
106d2df058dSpatrick 
107d2df058dSpatrick 	uint8_t			  sc_tx_seq;
10895ad3941Spatrick 	uint8_t			  sc_tx_max_seq;
1092802c178Spatrick 	struct mbuf_list	  sc_tx_queue;
1102802c178Spatrick 	int			  sc_tx_count;
111d2df058dSpatrick 
112d2df058dSpatrick 	struct task		  sc_task;
11332b2494eSpatrick };
11432b2494eSpatrick 
11532b2494eSpatrick int		 bwfm_sdio_match(struct device *, void *, void *);
11632b2494eSpatrick void		 bwfm_sdio_attach(struct device *, struct device *, void *);
117972218f3Spatrick int		 bwfm_sdio_preinit(struct bwfm_softc *);
11832b2494eSpatrick int		 bwfm_sdio_detach(struct device *, int);
11932b2494eSpatrick 
120d2df058dSpatrick int		 bwfm_sdio_intr(void *);
121571fa1d2Spatrick int		 bwfm_sdio_oob_intr(void *);
122d2df058dSpatrick void		 bwfm_sdio_task(void *);
123d2df058dSpatrick int		 bwfm_sdio_load_microcode(struct bwfm_sdio_softc *,
124d2df058dSpatrick 		    u_char *, size_t, u_char *, size_t);
125d2df058dSpatrick 
126d2df058dSpatrick void		 bwfm_sdio_clkctl(struct bwfm_sdio_softc *,
127d2df058dSpatrick 		    enum bwfm_sdio_clkstate, int);
128d2df058dSpatrick void		 bwfm_sdio_htclk(struct bwfm_sdio_softc *, int, int);
129e4f06f9cSpatrick void		 bwfm_sdio_readshared(struct bwfm_sdio_softc *);
130d2df058dSpatrick 
131709ff52bSpatrick void		 bwfm_sdio_backplane(struct bwfm_sdio_softc *, uint32_t);
13232b2494eSpatrick uint8_t		 bwfm_sdio_read_1(struct bwfm_sdio_softc *, uint32_t);
13332b2494eSpatrick uint32_t	 bwfm_sdio_read_4(struct bwfm_sdio_softc *, uint32_t);
13432b2494eSpatrick void		 bwfm_sdio_write_1(struct bwfm_sdio_softc *, uint32_t,
13532b2494eSpatrick 		    uint8_t);
13632b2494eSpatrick void		 bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t,
13732b2494eSpatrick 		    uint32_t);
138d2df058dSpatrick int		 bwfm_sdio_buf_read(struct bwfm_sdio_softc *,
139d2df058dSpatrick 		    struct sdmmc_function *, uint32_t, char *, size_t);
140d2df058dSpatrick int		 bwfm_sdio_buf_write(struct bwfm_sdio_softc *,
141d2df058dSpatrick 		    struct sdmmc_function *, uint32_t, char *, size_t);
142d2df058dSpatrick uint32_t	 bwfm_sdio_ram_read_write(struct bwfm_sdio_softc *,
143d2df058dSpatrick 		    uint32_t, char *, size_t, int);
144d2df058dSpatrick uint32_t	 bwfm_sdio_frame_read_write(struct bwfm_sdio_softc *,
145d2df058dSpatrick 		    char *, size_t, int);
146d2df058dSpatrick 
147d2df058dSpatrick uint32_t	 bwfm_sdio_dev_read(struct bwfm_sdio_softc *, uint32_t);
148d2df058dSpatrick void		 bwfm_sdio_dev_write(struct bwfm_sdio_softc *, uint32_t,
149d2df058dSpatrick 		    uint32_t);
15032b2494eSpatrick 
15132b2494eSpatrick uint32_t	 bwfm_sdio_buscore_read(struct bwfm_softc *, uint32_t);
15232b2494eSpatrick void		 bwfm_sdio_buscore_write(struct bwfm_softc *, uint32_t,
15332b2494eSpatrick 		    uint32_t);
15432b2494eSpatrick int		 bwfm_sdio_buscore_prepare(struct bwfm_softc *);
15532b2494eSpatrick void		 bwfm_sdio_buscore_activate(struct bwfm_softc *, uint32_t);
15632b2494eSpatrick 
157d2df058dSpatrick struct mbuf *	 bwfm_sdio_newbuf(void);
15895ad3941Spatrick int		 bwfm_sdio_tx_ok(struct bwfm_sdio_softc *);
1592802c178Spatrick void		 bwfm_sdio_tx_frames(struct bwfm_sdio_softc *);
1602802c178Spatrick void		 bwfm_sdio_tx_ctrlframe(struct bwfm_sdio_softc *, struct mbuf *);
1612802c178Spatrick void		 bwfm_sdio_tx_dataframe(struct bwfm_sdio_softc *, struct mbuf *);
162d2df058dSpatrick void		 bwfm_sdio_rx_frames(struct bwfm_sdio_softc *);
163bdeccf4cSpatrick void		 bwfm_sdio_rx_glom(struct bwfm_sdio_softc *, uint16_t *, int,
1646f241297Spatrick 		    uint16_t *, struct mbuf_list *);
165d2df058dSpatrick 
166d2df058dSpatrick int		 bwfm_sdio_txcheck(struct bwfm_softc *);
16732b2494eSpatrick int		 bwfm_sdio_txdata(struct bwfm_softc *, struct mbuf *);
1682802c178Spatrick int		 bwfm_sdio_txctl(struct bwfm_softc *, void *);
16932b2494eSpatrick 
170e4f06f9cSpatrick #ifdef BWFM_DEBUG
171e4f06f9cSpatrick void		 bwfm_sdio_debug_console(struct bwfm_sdio_softc *);
172e4f06f9cSpatrick #endif
173e4f06f9cSpatrick 
17432b2494eSpatrick struct bwfm_bus_ops bwfm_sdio_bus_ops = {
175972218f3Spatrick 	.bs_preinit = bwfm_sdio_preinit,
17632b2494eSpatrick 	.bs_stop = NULL,
177d2df058dSpatrick 	.bs_txcheck = bwfm_sdio_txcheck,
17832b2494eSpatrick 	.bs_txdata = bwfm_sdio_txdata,
17932b2494eSpatrick 	.bs_txctl = bwfm_sdio_txctl,
18032b2494eSpatrick };
18132b2494eSpatrick 
18232b2494eSpatrick struct bwfm_buscore_ops bwfm_sdio_buscore_ops = {
18332b2494eSpatrick 	.bc_read = bwfm_sdio_buscore_read,
18432b2494eSpatrick 	.bc_write = bwfm_sdio_buscore_write,
18532b2494eSpatrick 	.bc_prepare = bwfm_sdio_buscore_prepare,
18632b2494eSpatrick 	.bc_reset = NULL,
18732b2494eSpatrick 	.bc_setup = NULL,
18832b2494eSpatrick 	.bc_activate = bwfm_sdio_buscore_activate,
18932b2494eSpatrick };
19032b2494eSpatrick 
191*471aeecfSnaddy const struct cfattach bwfm_sdio_ca = {
19232b2494eSpatrick 	sizeof(struct bwfm_sdio_softc),
19332b2494eSpatrick 	bwfm_sdio_match,
19432b2494eSpatrick 	bwfm_sdio_attach,
19532b2494eSpatrick 	bwfm_sdio_detach,
19632b2494eSpatrick };
19732b2494eSpatrick 
19832b2494eSpatrick int
bwfm_sdio_match(struct device * parent,void * match,void * aux)19932b2494eSpatrick bwfm_sdio_match(struct device *parent, void *match, void *aux)
20032b2494eSpatrick {
20132b2494eSpatrick 	struct sdmmc_attach_args *saa = aux;
20232b2494eSpatrick 	struct sdmmc_function *sf = saa->sf;
20332b2494eSpatrick 	struct sdmmc_cis *cis;
20432b2494eSpatrick 
20532b2494eSpatrick 	/* Not SDIO. */
20632b2494eSpatrick 	if (sf == NULL)
20732b2494eSpatrick 		return 0;
20832b2494eSpatrick 
209680c8428Spatrick 	/* Look for Broadcom. */
21032b2494eSpatrick 	cis = &sf->sc->sc_fn0->cis;
2118acad61eSmiod 	if (cis->manufacturer != SDMMC_VENDOR_BROADCOM)
21232b2494eSpatrick 		return 0;
21332b2494eSpatrick 
214680c8428Spatrick 	/* Look for supported chips. */
215680c8428Spatrick 	switch (cis->product) {
2168acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM4324:
2178acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM4329:
2188acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM4330:
2198acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM4334:
2208acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM4335:
2218acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM4339:
2228acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM4345:
2238acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM4354:
2248acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM4356:
2258acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM4359:
2268acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM43143:
2278acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM43340:
2288acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM43341:
2298acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM43362:
2308acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM43430:
2318acad61eSmiod 	case SDMMC_PRODUCT_BROADCOM_BCM43364:
232680c8428Spatrick 		break;
233680c8428Spatrick 	default:
234680c8428Spatrick 		return 0;
235680c8428Spatrick 	}
236680c8428Spatrick 
23732b2494eSpatrick 	/* We need both functions, but ... */
23832b2494eSpatrick 	if (sf->sc->sc_function_count <= 1)
23932b2494eSpatrick 		return 0;
24032b2494eSpatrick 
24132b2494eSpatrick 	/* ... only attach for one. */
24232b2494eSpatrick 	if (sf->number != 1)
24332b2494eSpatrick 		return 0;
24432b2494eSpatrick 
24532b2494eSpatrick 	return 1;
24632b2494eSpatrick }
24732b2494eSpatrick 
24832b2494eSpatrick void
bwfm_sdio_attach(struct device * parent,struct device * self,void * aux)24932b2494eSpatrick bwfm_sdio_attach(struct device *parent, struct device *self, void *aux)
25032b2494eSpatrick {
25132b2494eSpatrick 	struct bwfm_sdio_softc *sc = (struct bwfm_sdio_softc *)self;
25232b2494eSpatrick 	struct sdmmc_attach_args *saa = aux;
25332b2494eSpatrick 	struct sdmmc_function *sf = saa->sf;
25432b2494eSpatrick 	struct bwfm_core *core;
255d2df058dSpatrick 	uint32_t reg;
25632b2494eSpatrick 
25732b2494eSpatrick 	printf("\n");
25832b2494eSpatrick 
259571fa1d2Spatrick #if defined(__HAVE_FDT)
260571fa1d2Spatrick 	if (sf->cookie)
261db31205aSkettenis 		sc->sc_sc.sc_node = *(int *)sf->cookie;
262571fa1d2Spatrick #endif
263571fa1d2Spatrick 
264d2df058dSpatrick 	task_set(&sc->sc_task, bwfm_sdio_task, sc);
2652802c178Spatrick 	ml_init(&sc->sc_tx_queue);
266d7d6fed2Spatrick 	sc->sc_bounce_size = 64 * 1024;
267d7d6fed2Spatrick 	sc->sc_bounce_buf = dma_alloc(sc->sc_bounce_size, PR_WAITOK);
26895ad3941Spatrick 	sc->sc_tx_seq = 0xff;
269d2df058dSpatrick 
27032b2494eSpatrick 	rw_assert_wrlock(&sf->sc->sc_lock);
271d2df058dSpatrick 	sc->sc_lock = &sf->sc->sc_lock;
27232b2494eSpatrick 
27332b2494eSpatrick 	sc->sc_sf = mallocarray(sf->sc->sc_function_count + 1,
27432b2494eSpatrick 	    sizeof(struct sdmmc_function *), M_DEVBUF, M_WAITOK);
27532b2494eSpatrick 
27632b2494eSpatrick 	/* Copy all function pointers. */
27732b2494eSpatrick 	SIMPLEQ_FOREACH(sf, &saa->sf->sc->sf_head, sf_list) {
27832b2494eSpatrick 		sc->sc_sf[sf->number] = sf;
27932b2494eSpatrick 	}
28032b2494eSpatrick 	sf = saa->sf;
28132b2494eSpatrick 
282df10b811Spatrick 	sdmmc_io_set_blocklen(sc->sc_sf[1], 64);
283df10b811Spatrick 	sdmmc_io_set_blocklen(sc->sc_sf[2], 512);
28432b2494eSpatrick 
28532b2494eSpatrick 	/* Enable Function 1. */
28632b2494eSpatrick 	if (sdmmc_io_function_enable(sc->sc_sf[1]) != 0) {
28732b2494eSpatrick 		printf("%s: cannot enable function 1\n", DEVNAME(sc));
28832b2494eSpatrick 		goto err;
28932b2494eSpatrick 	}
29032b2494eSpatrick 
29132b2494eSpatrick 	DPRINTF(("%s: F1 signature read @0x18000000=%x\n", DEVNAME(sc),
29232b2494eSpatrick 	    bwfm_sdio_read_4(sc, 0x18000000)));
29332b2494eSpatrick 
29432b2494eSpatrick 	/* Force PLL off */
29532b2494eSpatrick 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR,
29632b2494eSpatrick 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF |
29732b2494eSpatrick 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ);
29832b2494eSpatrick 
29932b2494eSpatrick 	sc->sc_sc.sc_buscore_ops = &bwfm_sdio_buscore_ops;
30032b2494eSpatrick 	if (bwfm_chip_attach(&sc->sc_sc) != 0) {
30132b2494eSpatrick 		printf("%s: cannot attach chip\n", DEVNAME(sc));
30232b2494eSpatrick 		goto err;
30332b2494eSpatrick 	}
30432b2494eSpatrick 
305d2df058dSpatrick 	sc->sc_cc = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_CHIPCOMMON);
306d2df058dSpatrick 	if (sc->sc_cc == NULL) {
307d2df058dSpatrick 		printf("%s: cannot find chipcommon core\n", DEVNAME(sc));
308d2df058dSpatrick 		goto err;
309d2df058dSpatrick 	}
310d2df058dSpatrick 
311d2df058dSpatrick 	core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV);
312d2df058dSpatrick 	if (core->co_rev >= 12) {
313d2df058dSpatrick 		reg = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_SLEEPCSR);
314d2df058dSpatrick 		if (!(reg & BWFM_SDIO_FUNC1_SLEEPCSR_KSO)) {
315d2df058dSpatrick 			reg |= BWFM_SDIO_FUNC1_SLEEPCSR_KSO;
316d2df058dSpatrick 			bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SLEEPCSR, reg);
317d2df058dSpatrick 		}
318d2df058dSpatrick 	}
319d2df058dSpatrick 
32032b2494eSpatrick 	/* TODO: drive strength */
32132b2494eSpatrick 
32232b2494eSpatrick 	bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL,
32332b2494eSpatrick 	    bwfm_sdio_read_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL) |
32432b2494eSpatrick 	    BWFM_SDIO_CCCR_BRCM_CARDCTRL_WLANRESET);
32532b2494eSpatrick 
32632b2494eSpatrick 	core = bwfm_chip_get_pmu(&sc->sc_sc);
32732b2494eSpatrick 	bwfm_sdio_write_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL,
32832b2494eSpatrick 	    bwfm_sdio_read_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL) |
32932b2494eSpatrick 	    (BWFM_CHIP_REG_PMUCONTROL_RES_RELOAD <<
33032b2494eSpatrick 	     BWFM_CHIP_REG_PMUCONTROL_RES_SHIFT));
33132b2494eSpatrick 
332d2df058dSpatrick 	sdmmc_io_function_disable(sc->sc_sf[2]);
33332b2494eSpatrick 
334d2df058dSpatrick 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, 0);
335d2df058dSpatrick 	sc->sc_clkstate = CLK_SDONLY;
336d2df058dSpatrick 
337972218f3Spatrick 	sc->sc_sc.sc_bus_ops = &bwfm_sdio_bus_ops;
338972218f3Spatrick 	sc->sc_sc.sc_proto_ops = &bwfm_proto_bcdc_ops;
339972218f3Spatrick 	bwfm_attach(&sc->sc_sc);
340972218f3Spatrick 	config_mountroot(self, bwfm_attachhook);
34132b2494eSpatrick 	return;
34232b2494eSpatrick 
34332b2494eSpatrick err:
34432b2494eSpatrick 	free(sc->sc_sf, M_DEVBUF, 0);
34532b2494eSpatrick }
34632b2494eSpatrick 
347972218f3Spatrick int
bwfm_sdio_preinit(struct bwfm_softc * bwfm)348972218f3Spatrick bwfm_sdio_preinit(struct bwfm_softc *bwfm)
349d2df058dSpatrick {
350972218f3Spatrick 	struct bwfm_sdio_softc *sc = (void *)bwfm;
3519ead8393Spatrick 	const char *chip = NULL;
352d2df058dSpatrick 	uint32_t clk, reg;
353d2df058dSpatrick 	u_char *ucode, *nvram;
3547a025419Sjsg 	size_t size = 0, nvsize, nvlen = 0;
355d2df058dSpatrick 
356972218f3Spatrick 	if (sc->sc_initialized)
357972218f3Spatrick 		return 0;
358972218f3Spatrick 
359d2df058dSpatrick 	rw_enter_write(sc->sc_lock);
360d2df058dSpatrick 
361d2df058dSpatrick 	switch (bwfm->sc_chip.ch_chip)
362d2df058dSpatrick 	{
3633e3c1da1Saoyama 	case BRCM_CC_43241_CHIP_ID:
3643e3c1da1Saoyama 		if (bwfm->sc_chip.ch_chiprev <= 4)
3653e3c1da1Saoyama 			chip = "43241b0";
3663e3c1da1Saoyama 		else if (bwfm->sc_chip.ch_chiprev == 5)
3673e3c1da1Saoyama 			chip = "43241b4";
3683e3c1da1Saoyama 		else
3693e3c1da1Saoyama 			chip = "43241b5";
3703e3c1da1Saoyama 		break;
371d2df058dSpatrick 	case BRCM_CC_4330_CHIP_ID:
3729ead8393Spatrick 		chip = "4330";
373d2df058dSpatrick 		break;
374d2df058dSpatrick 	case BRCM_CC_4334_CHIP_ID:
3759ead8393Spatrick 		chip = "4334";
376d2df058dSpatrick 		break;
377315a0156Spatrick 	case BRCM_CC_4345_CHIP_ID:
3789ead8393Spatrick 		if (bwfm->sc_chip.ch_chiprev == 9)
3799ead8393Spatrick 			chip = "43456";
3809ead8393Spatrick 		else
3819ead8393Spatrick 			chip = "43455";
382315a0156Spatrick 		break;
383d2df058dSpatrick 	case BRCM_CC_43340_CHIP_ID:
3845bc73f03Spatrick 	case BRCM_CC_43341_CHIP_ID:
3859ead8393Spatrick 		chip = "43340";
386d2df058dSpatrick 		break;
387529fc9dfSkettenis 	case BRCM_CC_4335_CHIP_ID:
3889ead8393Spatrick 		if (bwfm->sc_chip.ch_chiprev < 2)
3899ead8393Spatrick 			chip = "4335";
3909ead8393Spatrick 		else
3919ead8393Spatrick 			chip = "4339";
392529fc9dfSkettenis 		break;
393529fc9dfSkettenis 	case BRCM_CC_4339_CHIP_ID:
3949ead8393Spatrick 		chip = "4339";
395529fc9dfSkettenis 		break;
396ca890d05Skettenis 	case BRCM_CC_43430_CHIP_ID:
3979ead8393Spatrick 		if (bwfm->sc_chip.ch_chiprev == 0)
3989ead8393Spatrick 			chip = "43430a0";
399aec445eaSpatrick 		else if (bwfm->sc_chip.ch_chiprev == 2)
400aec445eaSpatrick 			chip = "43436";
4019ead8393Spatrick 		else
4029ead8393Spatrick 			chip = "43430";
403ca890d05Skettenis 		break;
40452b371b7Skettenis 	case BRCM_CC_4356_CHIP_ID:
4059ead8393Spatrick 		chip = "4356";
40652b371b7Skettenis 		break;
4070d580645Skettenis 	case BRCM_CC_4359_CHIP_ID:
4080d580645Skettenis 		chip = "4359";
4090d580645Skettenis 		break;
410d2df058dSpatrick 	default:
411d2df058dSpatrick 		printf("%s: unknown firmware for chip %s\n",
412d2df058dSpatrick 		    DEVNAME(sc), bwfm->sc_chip.ch_name);
413ee8f7f89Spatrick 		goto err;
414d2df058dSpatrick 	}
415d2df058dSpatrick 
41694e4747cSpatrick 	if (bwfm_loadfirmware(bwfm, chip, "-sdio", &ucode, &size,
41794e4747cSpatrick 	    &nvram, &nvsize, &nvlen) != 0)
418ee8f7f89Spatrick 		goto err;
4196c2f85e4Spatrick 
420d2df058dSpatrick 	sc->sc_alp_only = 1;
421d2df058dSpatrick 	if (bwfm_sdio_load_microcode(sc, ucode, size,
422d2df058dSpatrick 	    nvram, nvlen) != 0) {
423d2df058dSpatrick 		printf("%s: could not load microcode\n",
424d2df058dSpatrick 		    DEVNAME(sc));
425d2df058dSpatrick 		free(ucode, M_DEVBUF, size);
426632f7369Spatrick 		free(nvram, M_DEVBUF, nvsize);
427ee8f7f89Spatrick 		goto err;
428d2df058dSpatrick 	}
429d2df058dSpatrick 	sc->sc_alp_only = 0;
430d2df058dSpatrick 	free(ucode, M_DEVBUF, size);
431632f7369Spatrick 	free(nvram, M_DEVBUF, nvsize);
432d2df058dSpatrick 
433d2df058dSpatrick 	bwfm_sdio_clkctl(sc, CLK_AVAIL, 0);
434d2df058dSpatrick 	if (sc->sc_clkstate != CLK_AVAIL)
435ee8f7f89Spatrick 		goto err;
436d2df058dSpatrick 
437d2df058dSpatrick 	clk = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR);
438d2df058dSpatrick 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR,
439d2df058dSpatrick 	    clk | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HT);
440d2df058dSpatrick 
441d2df058dSpatrick 	bwfm_sdio_dev_write(sc, SDPCMD_TOSBMAILBOXDATA,
442d2df058dSpatrick 	    SDPCM_PROT_VERSION << SDPCM_PROT_VERSION_SHIFT);
443d2df058dSpatrick 	if (sdmmc_io_function_enable(sc->sc_sf[2]) != 0) {
444d2df058dSpatrick 		printf("%s: cannot enable function 2\n", DEVNAME(sc));
445ee8f7f89Spatrick 		goto err;
446d2df058dSpatrick 	}
447d2df058dSpatrick 
448d2df058dSpatrick 	bwfm_sdio_dev_write(sc, SDPCMD_HOSTINTMASK,
449d2df058dSpatrick 	    SDPCMD_INTSTATUS_HMB_SW_MASK|SDPCMD_INTSTATUS_CHIPACTIVE);
450d2df058dSpatrick 	bwfm_sdio_write_1(sc, BWFM_SDIO_WATERMARK, 8);
451d2df058dSpatrick 
452d2df058dSpatrick 	if (bwfm_chip_sr_capable(bwfm)) {
453d2df058dSpatrick 		reg = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_WAKEUPCTRL);
454d2df058dSpatrick 		reg |= BWFM_SDIO_FUNC1_WAKEUPCTRL_HTWAIT;
455d2df058dSpatrick 		bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_WAKEUPCTRL, reg);
456d2df058dSpatrick 		bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_CARDCAP,
457d2df058dSpatrick 		    BWFM_SDIO_CCCR_CARDCAP_CMD14_SUPPORT |
458d2df058dSpatrick 		    BWFM_SDIO_CCCR_CARDCAP_CMD14_EXT);
459d2df058dSpatrick 		bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR,
460d2df058dSpatrick 		    BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HT);
461d2df058dSpatrick 		sc->sc_sr_enabled = 1;
462d2df058dSpatrick 	} else {
463d2df058dSpatrick 		bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clk);
464d2df058dSpatrick 	}
465d2df058dSpatrick 
466571fa1d2Spatrick #if defined(__HAVE_FDT)
467db31205aSkettenis 	if (sc->sc_sc.sc_node) {
468db31205aSkettenis 		sc->sc_ih = fdt_intr_establish(sc->sc_sc.sc_node,
469571fa1d2Spatrick 		    IPL_NET, bwfm_sdio_oob_intr, sc, DEVNAME(sc));
470571fa1d2Spatrick 		if (sc->sc_ih != NULL) {
471571fa1d2Spatrick 			bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_SEPINT,
472571fa1d2Spatrick 			    BWFM_SDIO_CCCR_SEPINT_MASK |
473571fa1d2Spatrick 			    BWFM_SDIO_CCCR_SEPINT_OE |
474571fa1d2Spatrick 			    BWFM_SDIO_CCCR_SEPINT_ACT_HI);
475571fa1d2Spatrick 			sc->sc_oob = 1;
476571fa1d2Spatrick 		}
477571fa1d2Spatrick 	}
478571fa1d2Spatrick 	if (sc->sc_ih == NULL)
479571fa1d2Spatrick #endif
480d2df058dSpatrick 	sc->sc_ih = sdmmc_intr_establish(bwfm->sc_dev.dv_parent,
481d2df058dSpatrick 	    bwfm_sdio_intr, sc, DEVNAME(sc));
482d2df058dSpatrick 	if (sc->sc_ih == NULL) {
483d2df058dSpatrick 		printf("%s: can't establish interrupt\n", DEVNAME(sc));
484d2df058dSpatrick 		bwfm_sdio_clkctl(sc, CLK_NONE, 0);
485ee8f7f89Spatrick 		goto err;
486d2df058dSpatrick 	}
487d2df058dSpatrick 	sdmmc_intr_enable(sc->sc_sf[1]);
488d2df058dSpatrick 	rw_exit(sc->sc_lock);
489d2df058dSpatrick 
490972218f3Spatrick 	sc->sc_initialized = 1;
491972218f3Spatrick 	return 0;
492ee8f7f89Spatrick 
493ee8f7f89Spatrick err:
494ee8f7f89Spatrick 	rw_exit(sc->sc_lock);
495ee8f7f89Spatrick 	return 1;
496d2df058dSpatrick }
497d2df058dSpatrick 
498d2df058dSpatrick int
bwfm_sdio_load_microcode(struct bwfm_sdio_softc * sc,u_char * ucode,size_t size,u_char * nvram,size_t nvlen)499d2df058dSpatrick bwfm_sdio_load_microcode(struct bwfm_sdio_softc *sc, u_char *ucode, size_t size,
500d2df058dSpatrick     u_char *nvram, size_t nvlen)
501d2df058dSpatrick {
502d2df058dSpatrick 	struct bwfm_softc *bwfm = (void *)sc;
503d2df058dSpatrick 	char *verify = NULL;
504d2df058dSpatrick 	int err = 0;
505d2df058dSpatrick 
506d2df058dSpatrick 	bwfm_sdio_clkctl(sc, CLK_AVAIL, 0);
507d2df058dSpatrick 
508d2df058dSpatrick 	/* Upload firmware */
509d2df058dSpatrick 	err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase,
510d2df058dSpatrick 	    ucode, size, 1);
511d2df058dSpatrick 	if (err)
512d2df058dSpatrick 		goto out;
513d2df058dSpatrick 
514d2df058dSpatrick 	/* Verify firmware */
515d2df058dSpatrick 	verify = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
516d2df058dSpatrick 	err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase,
517d2df058dSpatrick 	    verify, size, 0);
518d2df058dSpatrick 	if (err || memcmp(verify, ucode, size)) {
519d2df058dSpatrick 		printf("%s: firmware verification failed\n",
520d2df058dSpatrick 		    DEVNAME(sc));
521d2df058dSpatrick 		free(verify, M_TEMP, size);
522d2df058dSpatrick 		goto out;
523d2df058dSpatrick 	}
524d2df058dSpatrick 	free(verify, M_TEMP, size);
525d2df058dSpatrick 
526d2df058dSpatrick 	/* Upload nvram */
527d2df058dSpatrick 	err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase +
528d2df058dSpatrick 	    bwfm->sc_chip.ch_ramsize - nvlen, nvram, nvlen, 1);
529d2df058dSpatrick 	if (err)
530d2df058dSpatrick 		goto out;
531d2df058dSpatrick 
532d2df058dSpatrick 	/* Verify nvram */
533d2df058dSpatrick 	verify = malloc(nvlen, M_TEMP, M_WAITOK | M_ZERO);
534d2df058dSpatrick 	err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase +
535d2df058dSpatrick 	    bwfm->sc_chip.ch_ramsize - nvlen, verify, nvlen, 0);
536d2df058dSpatrick 	if (err || memcmp(verify, nvram, nvlen)) {
537d2df058dSpatrick 		printf("%s: nvram verification failed\n",
538d2df058dSpatrick 		    DEVNAME(sc));
539d2df058dSpatrick 		free(verify, M_TEMP, nvlen);
540d2df058dSpatrick 		goto out;
541d2df058dSpatrick 	}
542d2df058dSpatrick 	free(verify, M_TEMP, nvlen);
543d2df058dSpatrick 
544d2df058dSpatrick 	/* Load reset vector from firmware and kickstart core. */
545d2df058dSpatrick 	bwfm_chip_set_active(bwfm, *(uint32_t *)ucode);
546d2df058dSpatrick 
547d2df058dSpatrick out:
548d2df058dSpatrick 	bwfm_sdio_clkctl(sc, CLK_SDONLY, 0);
549d2df058dSpatrick 	return err;
550d2df058dSpatrick }
551d2df058dSpatrick 
552d2df058dSpatrick void
bwfm_sdio_clkctl(struct bwfm_sdio_softc * sc,enum bwfm_sdio_clkstate newstate,int pendok)553d2df058dSpatrick bwfm_sdio_clkctl(struct bwfm_sdio_softc *sc, enum bwfm_sdio_clkstate newstate,
554d2df058dSpatrick     int pendok)
555d2df058dSpatrick {
556d2df058dSpatrick 	enum bwfm_sdio_clkstate oldstate;
557d2df058dSpatrick 
558d2df058dSpatrick 	oldstate = sc->sc_clkstate;
559d2df058dSpatrick 	if (sc->sc_clkstate == newstate)
560d2df058dSpatrick 		return;
561d2df058dSpatrick 
562d2df058dSpatrick 	switch (newstate) {
563d2df058dSpatrick 	case CLK_AVAIL:
564d2df058dSpatrick 		if (sc->sc_clkstate == CLK_NONE)
565d2df058dSpatrick 			sc->sc_clkstate = CLK_SDONLY;
566d2df058dSpatrick 		bwfm_sdio_htclk(sc, 1, pendok);
567d2df058dSpatrick 		break;
568d2df058dSpatrick 	case CLK_SDONLY:
569d2df058dSpatrick 		if (sc->sc_clkstate == CLK_NONE)
570d2df058dSpatrick 			sc->sc_clkstate = CLK_SDONLY;
571d2df058dSpatrick 		else if (sc->sc_clkstate == CLK_AVAIL)
572d2df058dSpatrick 			bwfm_sdio_htclk(sc, 0, 0);
573d2df058dSpatrick 		else
574d2df058dSpatrick 			printf("%s: request for %d -> %d\n",
575d2df058dSpatrick 			    DEVNAME(sc), sc->sc_clkstate, newstate);
576d2df058dSpatrick 		break;
577d2df058dSpatrick 	case CLK_NONE:
578d2df058dSpatrick 		if (sc->sc_clkstate == CLK_AVAIL)
579d2df058dSpatrick 			bwfm_sdio_htclk(sc, 0, 0);
580d2df058dSpatrick 		sc->sc_clkstate = CLK_NONE;
581d2df058dSpatrick 		break;
582d2df058dSpatrick 	default:
583d2df058dSpatrick 		break;
584d2df058dSpatrick 	}
585d2df058dSpatrick 
586d2df058dSpatrick 	DPRINTF(("%s: %d -> %d = %d\n", DEVNAME(sc), oldstate, newstate,
587d2df058dSpatrick 	    sc->sc_clkstate));
588d2df058dSpatrick }
589d2df058dSpatrick 
590d2df058dSpatrick void
bwfm_sdio_htclk(struct bwfm_sdio_softc * sc,int on,int pendok)591d2df058dSpatrick bwfm_sdio_htclk(struct bwfm_sdio_softc *sc, int on, int pendok)
592d2df058dSpatrick {
593d2df058dSpatrick 	uint32_t clkctl, devctl, req;
594d2df058dSpatrick 	int i;
595d2df058dSpatrick 
596d2df058dSpatrick 	if (sc->sc_sr_enabled) {
597d2df058dSpatrick 		if (on)
598d2df058dSpatrick 			sc->sc_clkstate = CLK_AVAIL;
599d2df058dSpatrick 		else
600d2df058dSpatrick 			sc->sc_clkstate = CLK_SDONLY;
601d2df058dSpatrick 		return;
602d2df058dSpatrick 	}
603d2df058dSpatrick 
604d2df058dSpatrick 	if (on) {
605d2df058dSpatrick 		if (sc->sc_alp_only)
606d2df058dSpatrick 			req = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ;
607d2df058dSpatrick 		else
608d2df058dSpatrick 			req = BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL_REQ;
609d2df058dSpatrick 		bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, req);
610d2df058dSpatrick 
611d2df058dSpatrick 		clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR);
612d2df058dSpatrick 		if (!BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(clkctl, sc->sc_alp_only)
613d2df058dSpatrick 		    && pendok) {
614d2df058dSpatrick 			devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL);
615d2df058dSpatrick 			devctl |= BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY;
616d2df058dSpatrick 			bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl);
617d2df058dSpatrick 			sc->sc_clkstate = CLK_PENDING;
618d2df058dSpatrick 			return;
619d2df058dSpatrick 		} else if (sc->sc_clkstate == CLK_PENDING) {
620d2df058dSpatrick 			devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL);
621d2df058dSpatrick 			devctl &= ~BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY;
622d2df058dSpatrick 			bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl);
623d2df058dSpatrick 		}
624d2df058dSpatrick 
625529fc9dfSkettenis 		for (i = 0; i < 5000; i++) {
626d2df058dSpatrick 			if (BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(clkctl,
627d2df058dSpatrick 			    sc->sc_alp_only))
628d2df058dSpatrick 				break;
629d2df058dSpatrick 			clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR);
630d2df058dSpatrick 			delay(1000);
631d2df058dSpatrick 		}
632d2df058dSpatrick 		if (!BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(clkctl, sc->sc_alp_only)) {
633d2df058dSpatrick 			printf("%s: HT avail timeout\n", DEVNAME(sc));
634d2df058dSpatrick 			return;
635d2df058dSpatrick 		}
636d2df058dSpatrick 
637d2df058dSpatrick 		sc->sc_clkstate = CLK_AVAIL;
638d2df058dSpatrick 	} else {
639d2df058dSpatrick 		if (sc->sc_clkstate == CLK_PENDING) {
640d2df058dSpatrick 			devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL);
641d2df058dSpatrick 			devctl &= ~BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY;
642d2df058dSpatrick 			bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl);
643d2df058dSpatrick 		}
644d2df058dSpatrick 		sc->sc_clkstate = CLK_SDONLY;
645d2df058dSpatrick 		bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, 0);
646d2df058dSpatrick 	}
647d2df058dSpatrick }
648d2df058dSpatrick 
649e4f06f9cSpatrick void
bwfm_sdio_readshared(struct bwfm_sdio_softc * sc)650e4f06f9cSpatrick bwfm_sdio_readshared(struct bwfm_sdio_softc *sc)
651e4f06f9cSpatrick {
652e4f06f9cSpatrick 	struct bwfm_softc *bwfm = (void *)sc;
653e4f06f9cSpatrick 	struct bwfm_sdio_sdpcm sdpcm;
654e4f06f9cSpatrick 	uint32_t addr, shaddr;
655e4f06f9cSpatrick 	int err;
656e4f06f9cSpatrick 
657e4f06f9cSpatrick 	shaddr = bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize - 4;
658e4f06f9cSpatrick 	if (!bwfm->sc_chip.ch_rambase && bwfm_chip_sr_capable(bwfm))
659e4f06f9cSpatrick 		shaddr -= bwfm->sc_chip.ch_srsize;
660e4f06f9cSpatrick 
661e4f06f9cSpatrick 	err = bwfm_sdio_ram_read_write(sc, shaddr, (char *)&addr,
662e4f06f9cSpatrick 	    sizeof(addr), 0);
663e4f06f9cSpatrick 	if (err)
664e4f06f9cSpatrick 		return;
665e4f06f9cSpatrick 
666e4f06f9cSpatrick 	addr = letoh32(addr);
667e4f06f9cSpatrick 	if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff))
668e4f06f9cSpatrick 		return;
669e4f06f9cSpatrick 
670e4f06f9cSpatrick 	err = bwfm_sdio_ram_read_write(sc, addr, (char *)&sdpcm,
671e4f06f9cSpatrick 	    sizeof(sdpcm), 0);
672e4f06f9cSpatrick 	if (err)
673e4f06f9cSpatrick 		return;
674e4f06f9cSpatrick 
675e4f06f9cSpatrick 	sc->sc_console_addr = letoh32(sdpcm.console_addr);
676e4f06f9cSpatrick }
677e4f06f9cSpatrick 
678d2df058dSpatrick int
bwfm_sdio_intr(void * v)679d2df058dSpatrick bwfm_sdio_intr(void *v)
680d2df058dSpatrick {
6815653bc9eSkettenis 	bwfm_sdio_task(v);
682d2df058dSpatrick 	return 1;
683d2df058dSpatrick }
684d2df058dSpatrick 
685571fa1d2Spatrick #if defined(__HAVE_FDT)
686571fa1d2Spatrick int
bwfm_sdio_oob_intr(void * v)687571fa1d2Spatrick bwfm_sdio_oob_intr(void *v)
688571fa1d2Spatrick {
689571fa1d2Spatrick 	struct bwfm_sdio_softc *sc = (void *)v;
690571fa1d2Spatrick 	if (!sc->sc_oob)
691571fa1d2Spatrick 		return 0;
692571fa1d2Spatrick 	fdt_intr_disable(sc->sc_ih);
693571fa1d2Spatrick 	task_add(systq, &sc->sc_task);
694571fa1d2Spatrick 	return 1;
695571fa1d2Spatrick }
696571fa1d2Spatrick #endif
697571fa1d2Spatrick 
698d2df058dSpatrick void
bwfm_sdio_task(void * v)699d2df058dSpatrick bwfm_sdio_task(void *v)
700d2df058dSpatrick {
701d2df058dSpatrick 	struct bwfm_sdio_softc *sc = (void *)v;
702d2df058dSpatrick 	uint32_t clkctl, devctl, intstat, hostint;
703d2df058dSpatrick 
704d2df058dSpatrick 	rw_enter_write(sc->sc_lock);
705d2df058dSpatrick 
706d2df058dSpatrick 	if (!sc->sc_sr_enabled && sc->sc_clkstate == CLK_PENDING) {
707d2df058dSpatrick 		clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR);
708d2df058dSpatrick 		if (BWFM_SDIO_FUNC1_CHIPCLKCSR_HTAV(clkctl)) {
709d2df058dSpatrick 			devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL);
710d2df058dSpatrick 			devctl &= ~BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY;
711d2df058dSpatrick 			bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl);
712d2df058dSpatrick 			sc->sc_clkstate = CLK_AVAIL;
713d2df058dSpatrick 		}
714d2df058dSpatrick 	}
715d2df058dSpatrick 
716d2df058dSpatrick 	intstat = bwfm_sdio_dev_read(sc, BWFM_SDPCMD_INTSTATUS);
717d2df058dSpatrick 	intstat &= (SDPCMD_INTSTATUS_HMB_SW_MASK|SDPCMD_INTSTATUS_CHIPACTIVE);
718d2df058dSpatrick 	/* XXX fc state */
719d2df058dSpatrick 	if (intstat)
720d2df058dSpatrick 		bwfm_sdio_dev_write(sc, BWFM_SDPCMD_INTSTATUS, intstat);
721d2df058dSpatrick 
722d2df058dSpatrick 	if (intstat & SDPCMD_INTSTATUS_HMB_HOST_INT) {
723d2df058dSpatrick 		hostint = bwfm_sdio_dev_read(sc, SDPCMD_TOHOSTMAILBOXDATA);
724d2df058dSpatrick 		bwfm_sdio_dev_write(sc, SDPCMD_TOSBMAILBOX,
725d2df058dSpatrick 		    SDPCMD_TOSBMAILBOX_INT_ACK);
726d2df058dSpatrick 		if (hostint & SDPCMD_TOHOSTMAILBOXDATA_NAKHANDLED)
727d2df058dSpatrick 			intstat |= SDPCMD_INTSTATUS_HMB_FRAME_IND;
728e4f06f9cSpatrick 		if (hostint & SDPCMD_TOHOSTMAILBOXDATA_DEVREADY ||
729e4f06f9cSpatrick 		    hostint & SDPCMD_TOHOSTMAILBOXDATA_FWREADY)
730e4f06f9cSpatrick 			bwfm_sdio_readshared(sc);
731d2df058dSpatrick 	}
732d2df058dSpatrick 
733d2df058dSpatrick 	/* FIXME: Might stall if we don't when not set. */
734d2df058dSpatrick 	if (1 || intstat & SDPCMD_INTSTATUS_HMB_FRAME_IND) {
735d2df058dSpatrick 		bwfm_sdio_rx_frames(sc);
736d2df058dSpatrick 	}
737d2df058dSpatrick 
7382802c178Spatrick 	if (!ml_empty(&sc->sc_tx_queue)) {
7392802c178Spatrick 		bwfm_sdio_tx_frames(sc);
740d2df058dSpatrick 	}
741d2df058dSpatrick 
742e4f06f9cSpatrick #ifdef BWFM_DEBUG
743e4f06f9cSpatrick 	bwfm_sdio_debug_console(sc);
744e4f06f9cSpatrick #endif
745e4f06f9cSpatrick 
746d2df058dSpatrick 	rw_exit(sc->sc_lock);
747571fa1d2Spatrick 
748571fa1d2Spatrick #if defined(__HAVE_FDT)
749571fa1d2Spatrick 	if (sc->sc_oob)
750571fa1d2Spatrick 		fdt_intr_enable(sc->sc_ih);
751571fa1d2Spatrick #endif
752d2df058dSpatrick }
753d2df058dSpatrick 
75432b2494eSpatrick int
bwfm_sdio_detach(struct device * self,int flags)75532b2494eSpatrick bwfm_sdio_detach(struct device *self, int flags)
75632b2494eSpatrick {
75732b2494eSpatrick 	struct bwfm_sdio_softc *sc = (struct bwfm_sdio_softc *)self;
75832b2494eSpatrick 
75932b2494eSpatrick 	bwfm_detach(&sc->sc_sc, flags);
76032b2494eSpatrick 
761d7d6fed2Spatrick 	dma_free(sc->sc_bounce_buf, sc->sc_bounce_size);
76232b2494eSpatrick 	free(sc->sc_sf, M_DEVBUF, 0);
76332b2494eSpatrick 
76432b2494eSpatrick 	return 0;
76532b2494eSpatrick }
76632b2494eSpatrick 
767709ff52bSpatrick void
bwfm_sdio_backplane(struct bwfm_sdio_softc * sc,uint32_t bar0)768709ff52bSpatrick bwfm_sdio_backplane(struct bwfm_sdio_softc *sc, uint32_t bar0)
769709ff52bSpatrick {
770709ff52bSpatrick 	if (sc->sc_bar0 == bar0)
771709ff52bSpatrick 		return;
772709ff52bSpatrick 
773709ff52bSpatrick 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRLOW,
774709ff52bSpatrick 	    (bar0 >>  8) & 0x80);
775709ff52bSpatrick 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRMID,
776709ff52bSpatrick 	    (bar0 >> 16) & 0xff);
777709ff52bSpatrick 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRHIGH,
778709ff52bSpatrick 	    (bar0 >> 24) & 0xff);
779709ff52bSpatrick 	sc->sc_bar0 = bar0;
780709ff52bSpatrick }
781709ff52bSpatrick 
78232b2494eSpatrick uint8_t
bwfm_sdio_read_1(struct bwfm_sdio_softc * sc,uint32_t addr)78332b2494eSpatrick bwfm_sdio_read_1(struct bwfm_sdio_softc *sc, uint32_t addr)
78432b2494eSpatrick {
78532b2494eSpatrick 	struct sdmmc_function *sf;
78632b2494eSpatrick 	uint8_t rv;
78732b2494eSpatrick 
78832b2494eSpatrick 	/*
78932b2494eSpatrick 	 * figure out how to read the register based on address range
79032b2494eSpatrick 	 * 0x00 ~ 0x7FF: function 0 CCCR and FBR
79132b2494eSpatrick 	 * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
79232b2494eSpatrick 	 * The rest: function 1 silicon backplane core registers
79332b2494eSpatrick 	 */
79432b2494eSpatrick 	if ((addr & ~0x7ff) == 0)
79532b2494eSpatrick 		sf = sc->sc_sf[0];
79632b2494eSpatrick 	else
79732b2494eSpatrick 		sf = sc->sc_sf[1];
79832b2494eSpatrick 
79932b2494eSpatrick 	rv = sdmmc_io_read_1(sf, addr);
80032b2494eSpatrick 	return rv;
80132b2494eSpatrick }
80232b2494eSpatrick 
80332b2494eSpatrick uint32_t
bwfm_sdio_read_4(struct bwfm_sdio_softc * sc,uint32_t addr)80432b2494eSpatrick bwfm_sdio_read_4(struct bwfm_sdio_softc *sc, uint32_t addr)
80532b2494eSpatrick {
80632b2494eSpatrick 	struct sdmmc_function *sf;
80732b2494eSpatrick 	uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK;
80832b2494eSpatrick 	uint32_t rv;
80932b2494eSpatrick 
810709ff52bSpatrick 	bwfm_sdio_backplane(sc, bar0);
81132b2494eSpatrick 
81232b2494eSpatrick 	addr &= BWFM_SDIO_SB_OFT_ADDR_MASK;
81332b2494eSpatrick 	addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG;
81432b2494eSpatrick 
81532b2494eSpatrick 	/*
81632b2494eSpatrick 	 * figure out how to read the register based on address range
81732b2494eSpatrick 	 * 0x00 ~ 0x7FF: function 0 CCCR and FBR
81832b2494eSpatrick 	 * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
81932b2494eSpatrick 	 * The rest: function 1 silicon backplane core registers
82032b2494eSpatrick 	 */
82132b2494eSpatrick 	if ((addr & ~0x7ff) == 0)
82232b2494eSpatrick 		sf = sc->sc_sf[0];
82332b2494eSpatrick 	else
82432b2494eSpatrick 		sf = sc->sc_sf[1];
82532b2494eSpatrick 
82632b2494eSpatrick 	rv = sdmmc_io_read_4(sf, addr);
82732b2494eSpatrick 	return rv;
82832b2494eSpatrick }
82932b2494eSpatrick 
83032b2494eSpatrick void
bwfm_sdio_write_1(struct bwfm_sdio_softc * sc,uint32_t addr,uint8_t data)83132b2494eSpatrick bwfm_sdio_write_1(struct bwfm_sdio_softc *sc, uint32_t addr, uint8_t data)
83232b2494eSpatrick {
83332b2494eSpatrick 	struct sdmmc_function *sf;
83432b2494eSpatrick 
83532b2494eSpatrick 	/*
83632b2494eSpatrick 	 * figure out how to read the register based on address range
83732b2494eSpatrick 	 * 0x00 ~ 0x7FF: function 0 CCCR and FBR
83832b2494eSpatrick 	 * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
83932b2494eSpatrick 	 * The rest: function 1 silicon backplane core registers
84032b2494eSpatrick 	 */
84132b2494eSpatrick 	if ((addr & ~0x7ff) == 0)
84232b2494eSpatrick 		sf = sc->sc_sf[0];
84332b2494eSpatrick 	else
84432b2494eSpatrick 		sf = sc->sc_sf[1];
84532b2494eSpatrick 
84632b2494eSpatrick 	sdmmc_io_write_1(sf, addr, data);
84732b2494eSpatrick }
84832b2494eSpatrick 
84932b2494eSpatrick void
bwfm_sdio_write_4(struct bwfm_sdio_softc * sc,uint32_t addr,uint32_t data)85032b2494eSpatrick bwfm_sdio_write_4(struct bwfm_sdio_softc *sc, uint32_t addr, uint32_t data)
85132b2494eSpatrick {
85232b2494eSpatrick 	struct sdmmc_function *sf;
85332b2494eSpatrick 	uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK;
85432b2494eSpatrick 
855709ff52bSpatrick 	bwfm_sdio_backplane(sc, bar0);
85632b2494eSpatrick 
85732b2494eSpatrick 	addr &= BWFM_SDIO_SB_OFT_ADDR_MASK;
85832b2494eSpatrick 	addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG;
85932b2494eSpatrick 
86032b2494eSpatrick 	/*
86132b2494eSpatrick 	 * figure out how to read the register based on address range
86232b2494eSpatrick 	 * 0x00 ~ 0x7FF: function 0 CCCR and FBR
86332b2494eSpatrick 	 * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
86432b2494eSpatrick 	 * The rest: function 1 silicon backplane core registers
86532b2494eSpatrick 	 */
86632b2494eSpatrick 	if ((addr & ~0x7ff) == 0)
86732b2494eSpatrick 		sf = sc->sc_sf[0];
86832b2494eSpatrick 	else
86932b2494eSpatrick 		sf = sc->sc_sf[1];
87032b2494eSpatrick 
87132b2494eSpatrick 	sdmmc_io_write_4(sf, addr, data);
87232b2494eSpatrick }
87332b2494eSpatrick 
874d2df058dSpatrick int
bwfm_sdio_buf_read(struct bwfm_sdio_softc * sc,struct sdmmc_function * sf,uint32_t reg,char * data,size_t size)875d2df058dSpatrick bwfm_sdio_buf_read(struct bwfm_sdio_softc *sc, struct sdmmc_function *sf,
876d2df058dSpatrick     uint32_t reg, char *data, size_t size)
877d2df058dSpatrick {
878d2df058dSpatrick 	int err;
879d2df058dSpatrick 
880d7d6fed2Spatrick 	KASSERT(((vaddr_t)data & 0x3) == 0);
881d7d6fed2Spatrick 	KASSERT((size & 0x3) == 0);
882d7d6fed2Spatrick 
883d2df058dSpatrick 	if (sf == sc->sc_sf[1])
884df10b811Spatrick 		err = sdmmc_io_read_region_1(sf, reg, data, size);
885d2df058dSpatrick 	else
886df10b811Spatrick 		err = sdmmc_io_read_multi_1(sf, reg, data, size);
887d2df058dSpatrick 
888d2df058dSpatrick 	if (err)
889d2df058dSpatrick 		printf("%s: error %d\n", __func__, err);
890d2df058dSpatrick 
891d2df058dSpatrick 	return err;
892d2df058dSpatrick }
893d2df058dSpatrick 
894d2df058dSpatrick int
bwfm_sdio_buf_write(struct bwfm_sdio_softc * sc,struct sdmmc_function * sf,uint32_t reg,char * data,size_t size)895d2df058dSpatrick bwfm_sdio_buf_write(struct bwfm_sdio_softc *sc, struct sdmmc_function *sf,
896d2df058dSpatrick     uint32_t reg, char *data, size_t size)
897d2df058dSpatrick {
898d2df058dSpatrick 	int err;
899d2df058dSpatrick 
900d7d6fed2Spatrick 	KASSERT(((vaddr_t)data & 0x3) == 0);
901d7d6fed2Spatrick 	KASSERT((size & 0x3) == 0);
902d7d6fed2Spatrick 
903df10b811Spatrick 	err = sdmmc_io_write_region_1(sf, reg, data, size);
904d2df058dSpatrick 
905d2df058dSpatrick 	if (err)
906d2df058dSpatrick 		printf("%s: error %d\n", __func__, err);
907d2df058dSpatrick 
908d2df058dSpatrick 	return err;
909d2df058dSpatrick }
910d2df058dSpatrick 
911d2df058dSpatrick uint32_t
bwfm_sdio_ram_read_write(struct bwfm_sdio_softc * sc,uint32_t reg,char * data,size_t left,int write)912d2df058dSpatrick bwfm_sdio_ram_read_write(struct bwfm_sdio_softc *sc, uint32_t reg,
913d2df058dSpatrick     char *data, size_t left, int write)
914d2df058dSpatrick {
915d2df058dSpatrick 	uint32_t sbaddr, sdaddr, off;
916d2df058dSpatrick 	size_t size;
917d2df058dSpatrick 	int err;
918d2df058dSpatrick 
919d2df058dSpatrick 	err = off = 0;
920d2df058dSpatrick 	while (left > 0) {
921d2df058dSpatrick 		sbaddr = reg + off;
922d2df058dSpatrick 		bwfm_sdio_backplane(sc, sbaddr);
923d2df058dSpatrick 
924d7d6fed2Spatrick 		sdaddr = sbaddr & BWFM_SDIO_SB_OFT_ADDR_MASK;
925d2df058dSpatrick 		size = min(left, (BWFM_SDIO_SB_OFT_ADDR_PAGE - sdaddr));
926d2df058dSpatrick 		sdaddr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG;
927d2df058dSpatrick 
928d7d6fed2Spatrick 		if (write) {
929d7d6fed2Spatrick 			memcpy(sc->sc_bounce_buf, data + off, size);
930d7d6fed2Spatrick 			if (roundup(size, 4) != size)
931d7d6fed2Spatrick 				memset(sc->sc_bounce_buf + size, 0,
932d7d6fed2Spatrick 				    roundup(size, 4) - size);
933d2df058dSpatrick 			err = bwfm_sdio_buf_write(sc, sc->sc_sf[1], sdaddr,
934d7d6fed2Spatrick 			    sc->sc_bounce_buf, roundup(size, 4));
935d7d6fed2Spatrick 		} else {
936d2df058dSpatrick 			err = bwfm_sdio_buf_read(sc, sc->sc_sf[1], sdaddr,
937d7d6fed2Spatrick 			    sc->sc_bounce_buf, roundup(size, 4));
938d7d6fed2Spatrick 			memcpy(data + off, sc->sc_bounce_buf, size);
939d7d6fed2Spatrick 		}
940d2df058dSpatrick 		if (err)
941d2df058dSpatrick 			break;
942d2df058dSpatrick 
943d2df058dSpatrick 		off += size;
944d2df058dSpatrick 		left -= size;
945d2df058dSpatrick 	}
946d2df058dSpatrick 
947d2df058dSpatrick 	return err;
948d2df058dSpatrick }
949d2df058dSpatrick 
950d2df058dSpatrick uint32_t
bwfm_sdio_frame_read_write(struct bwfm_sdio_softc * sc,char * data,size_t size,int write)951d2df058dSpatrick bwfm_sdio_frame_read_write(struct bwfm_sdio_softc *sc,
952d2df058dSpatrick     char *data, size_t size, int write)
953d2df058dSpatrick {
954d2df058dSpatrick 	uint32_t addr;
955d2df058dSpatrick 	int err;
956d2df058dSpatrick 
957d2df058dSpatrick 	addr = sc->sc_cc->co_base;
958d2df058dSpatrick 	bwfm_sdio_backplane(sc, addr);
959d2df058dSpatrick 
960d2df058dSpatrick 	addr &= BWFM_SDIO_SB_OFT_ADDR_MASK;
961d2df058dSpatrick 	addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG;
962d2df058dSpatrick 
963d2df058dSpatrick 	if (write)
964d2df058dSpatrick 		err = bwfm_sdio_buf_write(sc, sc->sc_sf[2], addr, data, size);
965d2df058dSpatrick 	else
966d2df058dSpatrick 		err = bwfm_sdio_buf_read(sc, sc->sc_sf[2], addr, data, size);
967d2df058dSpatrick 
968d2df058dSpatrick 	return err;
969d2df058dSpatrick }
970d2df058dSpatrick 
971d2df058dSpatrick uint32_t
bwfm_sdio_dev_read(struct bwfm_sdio_softc * sc,uint32_t reg)972d2df058dSpatrick bwfm_sdio_dev_read(struct bwfm_sdio_softc *sc, uint32_t reg)
973d2df058dSpatrick {
974d2df058dSpatrick 	struct bwfm_core *core;
975d2df058dSpatrick 	core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV);
976d2df058dSpatrick 	return bwfm_sdio_read_4(sc, core->co_base + reg);
977d2df058dSpatrick }
978d2df058dSpatrick 
979d2df058dSpatrick void
bwfm_sdio_dev_write(struct bwfm_sdio_softc * sc,uint32_t reg,uint32_t val)980d2df058dSpatrick bwfm_sdio_dev_write(struct bwfm_sdio_softc *sc, uint32_t reg, uint32_t val)
981d2df058dSpatrick {
982d2df058dSpatrick 	struct bwfm_core *core;
983d2df058dSpatrick 	core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV);
984d2df058dSpatrick 	bwfm_sdio_write_4(sc, core->co_base + reg, val);
985d2df058dSpatrick }
986d2df058dSpatrick 
98732b2494eSpatrick uint32_t
bwfm_sdio_buscore_read(struct bwfm_softc * bwfm,uint32_t reg)98832b2494eSpatrick bwfm_sdio_buscore_read(struct bwfm_softc *bwfm, uint32_t reg)
98932b2494eSpatrick {
99032b2494eSpatrick 	struct bwfm_sdio_softc *sc = (void *)bwfm;
991529fc9dfSkettenis 	return bwfm_sdio_read_4(sc, reg);
99232b2494eSpatrick }
99332b2494eSpatrick 
99432b2494eSpatrick void
bwfm_sdio_buscore_write(struct bwfm_softc * bwfm,uint32_t reg,uint32_t val)99532b2494eSpatrick bwfm_sdio_buscore_write(struct bwfm_softc *bwfm, uint32_t reg, uint32_t val)
99632b2494eSpatrick {
99732b2494eSpatrick 	struct bwfm_sdio_softc *sc = (void *)bwfm;
99832b2494eSpatrick 	bwfm_sdio_write_4(sc, reg, val);
99932b2494eSpatrick }
100032b2494eSpatrick 
100132b2494eSpatrick int
bwfm_sdio_buscore_prepare(struct bwfm_softc * bwfm)100232b2494eSpatrick bwfm_sdio_buscore_prepare(struct bwfm_softc *bwfm)
100332b2494eSpatrick {
100432b2494eSpatrick 	struct bwfm_sdio_softc *sc = (void *)bwfm;
100532b2494eSpatrick 	uint8_t clkval, clkset, clkmask;
100632b2494eSpatrick 	int i;
100732b2494eSpatrick 
100832b2494eSpatrick 	clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ |
100932b2494eSpatrick 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF;
101032b2494eSpatrick 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset);
101132b2494eSpatrick 
101232b2494eSpatrick 	clkmask = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL |
101332b2494eSpatrick 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL;
101432b2494eSpatrick 	clkval = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR);
101532b2494eSpatrick 
101632b2494eSpatrick 	if ((clkval & ~clkmask) != clkset) {
101732b2494eSpatrick 		printf("%s: wrote 0x%02x read 0x%02x\n", DEVNAME(sc),
101832b2494eSpatrick 		    clkset, clkval);
101932b2494eSpatrick 		return 1;
102032b2494eSpatrick 	}
102132b2494eSpatrick 
102232b2494eSpatrick 	for (i = 1000; i > 0; i--) {
102332b2494eSpatrick 		clkval = bwfm_sdio_read_1(sc,
102432b2494eSpatrick 		    BWFM_SDIO_FUNC1_CHIPCLKCSR);
102532b2494eSpatrick 		if (clkval & clkmask)
102632b2494eSpatrick 			break;
102732b2494eSpatrick 	}
102832b2494eSpatrick 	if (i == 0) {
102932b2494eSpatrick 		printf("%s: timeout on ALPAV wait, clkval 0x%02x\n",
103032b2494eSpatrick 		    DEVNAME(sc), clkval);
103132b2494eSpatrick 		return 1;
103232b2494eSpatrick 	}
103332b2494eSpatrick 
103432b2494eSpatrick 	clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF |
103532b2494eSpatrick 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_ALP;
103632b2494eSpatrick 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset);
103732b2494eSpatrick 	delay(65);
103832b2494eSpatrick 
103932b2494eSpatrick 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SDIOPULLUP, 0);
104032b2494eSpatrick 
104132b2494eSpatrick 	return 0;
104232b2494eSpatrick }
104332b2494eSpatrick 
104432b2494eSpatrick void
bwfm_sdio_buscore_activate(struct bwfm_softc * bwfm,uint32_t rstvec)104532b2494eSpatrick bwfm_sdio_buscore_activate(struct bwfm_softc *bwfm, uint32_t rstvec)
104632b2494eSpatrick {
104732b2494eSpatrick 	struct bwfm_sdio_softc *sc = (void *)bwfm;
104832b2494eSpatrick 
1049d2df058dSpatrick 	bwfm_sdio_dev_write(sc, BWFM_SDPCMD_INTSTATUS, 0xFFFFFFFF);
105032b2494eSpatrick 
105132b2494eSpatrick 	if (rstvec)
1052d2df058dSpatrick 		bwfm_sdio_ram_read_write(sc, 0, (char *)&rstvec,
1053d2df058dSpatrick 		    sizeof(rstvec), 1);
1054d2df058dSpatrick }
1055d2df058dSpatrick 
1056d2df058dSpatrick struct mbuf *
bwfm_sdio_newbuf(void)1057d2df058dSpatrick bwfm_sdio_newbuf(void)
1058d2df058dSpatrick {
1059d2df058dSpatrick 	struct mbuf *m;
1060d2df058dSpatrick 
1061d2df058dSpatrick 	MGETHDR(m, M_DONTWAIT, MT_DATA);
1062d2df058dSpatrick 	if (m == NULL)
1063d2df058dSpatrick 		return (NULL);
1064d2df058dSpatrick 
1065d2df058dSpatrick 	MCLGET(m, M_DONTWAIT);
1066d2df058dSpatrick 	if (!(m->m_flags & M_EXT)) {
1067d2df058dSpatrick 		m_freem(m);
1068d2df058dSpatrick 		return (NULL);
1069d2df058dSpatrick 	}
1070d2df058dSpatrick 
1071d2df058dSpatrick 	m->m_len = m->m_pkthdr.len = MCLBYTES;
1072d2df058dSpatrick 
1073d2df058dSpatrick 	return (m);
1074d2df058dSpatrick }
1075d2df058dSpatrick 
107695ad3941Spatrick int
bwfm_sdio_tx_ok(struct bwfm_sdio_softc * sc)107795ad3941Spatrick bwfm_sdio_tx_ok(struct bwfm_sdio_softc *sc)
107895ad3941Spatrick {
107995ad3941Spatrick 	return (uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq) != 0 &&
108095ad3941Spatrick 	    ((uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq) & 0x80) == 0;
108195ad3941Spatrick }
108295ad3941Spatrick 
1083d2df058dSpatrick void
bwfm_sdio_tx_frames(struct bwfm_sdio_softc * sc)10842802c178Spatrick bwfm_sdio_tx_frames(struct bwfm_sdio_softc *sc)
1085d2df058dSpatrick {
10862802c178Spatrick 	struct ifnet *ifp = &sc->sc_sc.sc_ic.ic_if;
10872802c178Spatrick 	struct mbuf *m;
10882802c178Spatrick 	int i;
1089d2df058dSpatrick 
109095ad3941Spatrick 	if (!bwfm_sdio_tx_ok(sc))
109195ad3941Spatrick 		return;
109295ad3941Spatrick 
10932802c178Spatrick 	i = min((uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq), 32);
10942802c178Spatrick 	while (i--) {
10952802c178Spatrick 		m = ml_dequeue(&sc->sc_tx_queue);
10962802c178Spatrick 		if (m == NULL)
10972802c178Spatrick 			break;
1098d2df058dSpatrick 
10992802c178Spatrick 		if (m->m_type == MT_CONTROL)
11002802c178Spatrick 			bwfm_sdio_tx_ctrlframe(sc, m);
11012802c178Spatrick 		else
11022802c178Spatrick 			bwfm_sdio_tx_dataframe(sc, m);
11032802c178Spatrick 
11042802c178Spatrick 		m_freem(m);
11052802c178Spatrick 	}
11062802c178Spatrick 
11072802c178Spatrick 	if (sc->sc_tx_count < 64)
11082802c178Spatrick 		ifq_restart(&ifp->if_snd);
11092802c178Spatrick }
11102802c178Spatrick 
11112802c178Spatrick void
bwfm_sdio_tx_ctrlframe(struct bwfm_sdio_softc * sc,struct mbuf * m)11122802c178Spatrick bwfm_sdio_tx_ctrlframe(struct bwfm_sdio_softc *sc, struct mbuf *m)
11132802c178Spatrick {
11142802c178Spatrick 	struct bwfm_sdio_hwhdr *hwhdr;
11152802c178Spatrick 	struct bwfm_sdio_swhdr *swhdr;
11162802c178Spatrick 	size_t len, roundto;
11172802c178Spatrick 
11182802c178Spatrick 	len = sizeof(*hwhdr) + sizeof(*swhdr) + m->m_len;
1119d2df058dSpatrick 
1120d7d6fed2Spatrick 	/* Zero-pad to either block-size or 4-byte alignment. */
1121d7d6fed2Spatrick 	if (len > 512 && (len % 512) != 0)
1122d7d6fed2Spatrick 		roundto = 512;
1123d7d6fed2Spatrick 	else
1124d7d6fed2Spatrick 		roundto = 4;
1125d7d6fed2Spatrick 
1126d7d6fed2Spatrick 	KASSERT(roundup(len, roundto) <= sc->sc_bounce_size);
1127d7d6fed2Spatrick 
1128d7d6fed2Spatrick 	hwhdr = (void *)sc->sc_bounce_buf;
1129d2df058dSpatrick 	hwhdr->frmlen = htole16(len);
1130d2df058dSpatrick 	hwhdr->cksum = htole16(~len);
1131d2df058dSpatrick 
1132d2df058dSpatrick 	swhdr = (void *)&hwhdr[1];
1133d2df058dSpatrick 	swhdr->seqnr = sc->sc_tx_seq++;
1134d2df058dSpatrick 	swhdr->chanflag = BWFM_SDIO_SWHDR_CHANNEL_CONTROL;
1135d2df058dSpatrick 	swhdr->nextlen = 0;
1136d2df058dSpatrick 	swhdr->dataoff = sizeof(*hwhdr) + sizeof(*swhdr);
1137d2df058dSpatrick 	swhdr->maxseqnr = 0;
1138d2df058dSpatrick 
11392802c178Spatrick 	m_copydata(m, 0, m->m_len, (caddr_t)&swhdr[1]);
1140d2df058dSpatrick 
1141d7d6fed2Spatrick 	if (roundup(len, roundto) != len)
1142d7d6fed2Spatrick 		memset(sc->sc_bounce_buf + len, 0,
1143d7d6fed2Spatrick 		    roundup(len, roundto) - len);
1144d7d6fed2Spatrick 
1145d7d6fed2Spatrick 	bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf,
1146d7d6fed2Spatrick 	    roundup(len, roundto), 1);
1147d2df058dSpatrick }
1148d2df058dSpatrick 
1149d2df058dSpatrick void
bwfm_sdio_tx_dataframe(struct bwfm_sdio_softc * sc,struct mbuf * m)11502802c178Spatrick bwfm_sdio_tx_dataframe(struct bwfm_sdio_softc *sc, struct mbuf *m)
1151d2df058dSpatrick {
1152d2df058dSpatrick 	struct bwfm_sdio_hwhdr *hwhdr;
1153d2df058dSpatrick 	struct bwfm_sdio_swhdr *swhdr;
1154d2df058dSpatrick 	struct bwfm_proto_bcdc_hdr *bcdc;
1155d7d6fed2Spatrick 	size_t len, roundto;
1156d2df058dSpatrick 
1157d2df058dSpatrick 	len = sizeof(*hwhdr) + sizeof(*swhdr) + sizeof(*bcdc)
1158d2df058dSpatrick 	    + m->m_pkthdr.len;
1159d2df058dSpatrick 
1160d7d6fed2Spatrick 	/* Zero-pad to either block-size or 4-byte alignment. */
1161d7d6fed2Spatrick 	if (len > 512 && (len % 512) != 0)
1162d7d6fed2Spatrick 		roundto = 512;
1163d7d6fed2Spatrick 	else
1164d7d6fed2Spatrick 		roundto = 4;
1165d7d6fed2Spatrick 
1166d7d6fed2Spatrick 	KASSERT(roundup(len, roundto) <= sc->sc_bounce_size);
1167d7d6fed2Spatrick 
1168d7d6fed2Spatrick 	hwhdr = (void *)sc->sc_bounce_buf;
1169d2df058dSpatrick 	hwhdr->frmlen = htole16(len);
1170d2df058dSpatrick 	hwhdr->cksum = htole16(~len);
1171d2df058dSpatrick 
1172d2df058dSpatrick 	swhdr = (void *)&hwhdr[1];
1173d2df058dSpatrick 	swhdr->seqnr = sc->sc_tx_seq++;
1174d2df058dSpatrick 	swhdr->chanflag = BWFM_SDIO_SWHDR_CHANNEL_DATA;
1175d2df058dSpatrick 	swhdr->nextlen = 0;
1176d2df058dSpatrick 	swhdr->dataoff = sizeof(*hwhdr) + sizeof(*swhdr);
1177d2df058dSpatrick 	swhdr->maxseqnr = 0;
1178d2df058dSpatrick 
1179d2df058dSpatrick 	bcdc = (void *)&swhdr[1];
1180d2df058dSpatrick 	bcdc->data_offset = 0;
1181d2df058dSpatrick 	bcdc->priority = ieee80211_classify(&sc->sc_sc.sc_ic, m);
1182d2df058dSpatrick 	bcdc->flags = BWFM_BCDC_FLAG_VER(BWFM_BCDC_FLAG_PROTO_VER);
1183d2df058dSpatrick 	bcdc->flags2 = 0;
1184d2df058dSpatrick 
1185d2df058dSpatrick 	m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)&bcdc[1]);
1186d2df058dSpatrick 
1187d7d6fed2Spatrick 	if (roundup(len, roundto) != len)
1188d7d6fed2Spatrick 		memset(sc->sc_bounce_buf + len, 0,
1189d7d6fed2Spatrick 		    roundup(len, roundto) - len);
1190d2df058dSpatrick 
1191d7d6fed2Spatrick 	bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf,
1192d7d6fed2Spatrick 	    roundup(len, roundto), 1);
1193d7d6fed2Spatrick 
11942802c178Spatrick 	sc->sc_tx_count--;
1195d2df058dSpatrick }
1196d2df058dSpatrick 
1197d2df058dSpatrick void
bwfm_sdio_rx_frames(struct bwfm_sdio_softc * sc)1198d2df058dSpatrick bwfm_sdio_rx_frames(struct bwfm_sdio_softc *sc)
1199d2df058dSpatrick {
12006f241297Spatrick 	struct ifnet *ifp = &sc->sc_sc.sc_ic.ic_if;
12016f241297Spatrick 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
1202bdeccf4cSpatrick 	struct bwfm_sdio_hwhdr *hwhdr;
1203bdeccf4cSpatrick 	struct bwfm_sdio_swhdr *swhdr;
1204bdeccf4cSpatrick 	uint16_t *sublen, nextlen = 0;
1205d2df058dSpatrick 	struct mbuf *m;
1206d2df058dSpatrick 	size_t flen;
1207bdeccf4cSpatrick 	char *data;
1208d2df058dSpatrick 	off_t off;
1209bdeccf4cSpatrick 	int nsub;
1210bdeccf4cSpatrick 
1211d7d6fed2Spatrick 	hwhdr = (struct bwfm_sdio_hwhdr *)sc->sc_bounce_buf;
1212bdeccf4cSpatrick 	swhdr = (struct bwfm_sdio_swhdr *)&hwhdr[1];
1213bdeccf4cSpatrick 	data = (char *)&swhdr[1];
1214d2df058dSpatrick 
12152d669484Spatrick 	for (;;) {
1216bdeccf4cSpatrick 		/* If we know the next size, just read ahead. */
1217bdeccf4cSpatrick 		if (nextlen) {
1218d7d6fed2Spatrick 			if (bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf,
1219bdeccf4cSpatrick 			    nextlen, 0))
1220bdeccf4cSpatrick 				break;
1221bdeccf4cSpatrick 		} else {
1222d7d6fed2Spatrick 			if (bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf,
1223bdeccf4cSpatrick 			    sizeof(*hwhdr) + sizeof(*swhdr), 0))
1224bdeccf4cSpatrick 				break;
1225bdeccf4cSpatrick 		}
1226bdeccf4cSpatrick 
1227bdeccf4cSpatrick 		hwhdr->frmlen = letoh16(hwhdr->frmlen);
1228bdeccf4cSpatrick 		hwhdr->cksum = letoh16(hwhdr->cksum);
1229bdeccf4cSpatrick 
1230bdeccf4cSpatrick 		if (hwhdr->frmlen == 0 && hwhdr->cksum == 0)
1231d2df058dSpatrick 			break;
1232d2df058dSpatrick 
1233bdeccf4cSpatrick 		if ((hwhdr->frmlen ^ hwhdr->cksum) != 0xffff) {
1234d2df058dSpatrick 			printf("%s: checksum error\n", DEVNAME(sc));
1235d2df058dSpatrick 			break;
1236d2df058dSpatrick 		}
1237d2df058dSpatrick 
1238bdeccf4cSpatrick 		if (hwhdr->frmlen < sizeof(*hwhdr) + sizeof(*swhdr)) {
1239d2df058dSpatrick 			printf("%s: length error\n", DEVNAME(sc));
1240d2df058dSpatrick 			break;
1241d2df058dSpatrick 		}
1242d2df058dSpatrick 
1243bdeccf4cSpatrick 		if (nextlen && hwhdr->frmlen > nextlen) {
1244bdeccf4cSpatrick 			printf("%s: read ahead length error (%u > %u)\n",
1245bdeccf4cSpatrick 			    DEVNAME(sc), hwhdr->frmlen, nextlen);
1246d2df058dSpatrick 			break;
1247bdeccf4cSpatrick 		}
1248d2df058dSpatrick 
1249bdeccf4cSpatrick 		sc->sc_tx_max_seq = swhdr->maxseqnr;
125095ad3941Spatrick 
1251bdeccf4cSpatrick 		flen = hwhdr->frmlen - (sizeof(*hwhdr) + sizeof(*swhdr));
1252bdeccf4cSpatrick 		if (flen == 0) {
1253bdeccf4cSpatrick 			nextlen = swhdr->nextlen << 4;
1254d2df058dSpatrick 			continue;
1255bdeccf4cSpatrick 		}
1256d2df058dSpatrick 
1257bdeccf4cSpatrick 		if (!nextlen) {
1258d7d6fed2Spatrick 			KASSERT(roundup(flen, 4) <= sc->sc_bounce_size -
1259d7d6fed2Spatrick 			    (sizeof(*hwhdr) + sizeof(*swhdr)));
1260d7d6fed2Spatrick 			if (bwfm_sdio_frame_read_write(sc, data,
1261d7d6fed2Spatrick 			    roundup(flen, 4), 0))
1262bdeccf4cSpatrick 				break;
1263bdeccf4cSpatrick 		}
1264bdeccf4cSpatrick 
1265bdeccf4cSpatrick 		if (swhdr->dataoff < (sizeof(*hwhdr) + sizeof(*swhdr)))
1266d2df058dSpatrick 			break;
1267d2df058dSpatrick 
1268bdeccf4cSpatrick 		off = swhdr->dataoff - (sizeof(*hwhdr) + sizeof(*swhdr));
1269d2df058dSpatrick 		if (off > flen)
1270d2df058dSpatrick 			break;
1271d2df058dSpatrick 
1272bdeccf4cSpatrick 		switch (swhdr->chanflag & BWFM_SDIO_SWHDR_CHANNEL_MASK) {
1273d2df058dSpatrick 		case BWFM_SDIO_SWHDR_CHANNEL_CONTROL:
1274029d6dd5Spatrick 			sc->sc_sc.sc_proto_ops->proto_rxctl(&sc->sc_sc,
1275bdeccf4cSpatrick 			    data + off, flen - off);
1276bdeccf4cSpatrick 			nextlen = swhdr->nextlen << 4;
1277d2df058dSpatrick 			break;
1278d2df058dSpatrick 		case BWFM_SDIO_SWHDR_CHANNEL_EVENT:
1279d2df058dSpatrick 		case BWFM_SDIO_SWHDR_CHANNEL_DATA:
1280d2df058dSpatrick 			m = bwfm_sdio_newbuf();
1281d2df058dSpatrick 			if (m == NULL)
1282d2df058dSpatrick 				break;
1283d2df058dSpatrick 			if (flen - off > m->m_len) {
1284d2df058dSpatrick 				printf("%s: frame bigger than anticipated\n",
1285d2df058dSpatrick 				    DEVNAME(sc));
1286cb2afc74Spatrick 				m_freem(m);
1287d2df058dSpatrick 				break;
1288d2df058dSpatrick 			}
1289d2df058dSpatrick 			m->m_len = m->m_pkthdr.len = flen - off;
1290bdeccf4cSpatrick 			memcpy(mtod(m, char *), data + off, flen - off);
12916f241297Spatrick 			sc->sc_sc.sc_proto_ops->proto_rx(&sc->sc_sc, m, &ml);
1292bdeccf4cSpatrick 			nextlen = swhdr->nextlen << 4;
1293d2df058dSpatrick 			break;
1294d2df058dSpatrick 		case BWFM_SDIO_SWHDR_CHANNEL_GLOM:
1295d2df058dSpatrick 			if ((flen % sizeof(uint16_t)) != 0)
1296d2df058dSpatrick 				break;
1297d2df058dSpatrick 			nsub = flen / sizeof(uint16_t);
1298d2df058dSpatrick 			sublen = mallocarray(nsub, sizeof(uint16_t),
1299d2df058dSpatrick 			    M_DEVBUF, M_WAITOK | M_ZERO);
1300bdeccf4cSpatrick 			memcpy(sublen, data, nsub * sizeof(uint16_t));
13016f241297Spatrick 			bwfm_sdio_rx_glom(sc, sublen, nsub, &nextlen, &ml);
1302d2df058dSpatrick 			free(sublen, M_DEVBUF, nsub * sizeof(uint16_t));
1303d2df058dSpatrick 			break;
1304d2df058dSpatrick 		default:
1305d2df058dSpatrick 			printf("%s: unknown channel\n", DEVNAME(sc));
1306d2df058dSpatrick 			break;
1307d2df058dSpatrick 		}
13082d669484Spatrick 	}
13096f241297Spatrick 
13106f241297Spatrick 	if_input(ifp, &ml);
1311d2df058dSpatrick }
1312d2df058dSpatrick 
1313d2df058dSpatrick void
bwfm_sdio_rx_glom(struct bwfm_sdio_softc * sc,uint16_t * sublen,int nsub,uint16_t * nextlen,struct mbuf_list * ml)1314bdeccf4cSpatrick bwfm_sdio_rx_glom(struct bwfm_sdio_softc *sc, uint16_t *sublen, int nsub,
13156f241297Spatrick     uint16_t *nextlen, struct mbuf_list *ml)
1316d2df058dSpatrick {
1317d2df058dSpatrick 	struct bwfm_sdio_hwhdr hwhdr;
1318d2df058dSpatrick 	struct bwfm_sdio_swhdr swhdr;
13196f241297Spatrick 	struct mbuf_list glom, drop;
1320d2df058dSpatrick 	struct mbuf *m;
1321d2df058dSpatrick 	size_t flen;
1322d2df058dSpatrick 	off_t off;
1323d2df058dSpatrick 	int i;
1324d2df058dSpatrick 
13256f241297Spatrick 	ml_init(&glom);
1326d2df058dSpatrick 	ml_init(&drop);
1327d2df058dSpatrick 
1328d2df058dSpatrick 	if (nsub == 0)
1329d2df058dSpatrick 		return;
1330d2df058dSpatrick 
1331d2df058dSpatrick 	for (i = 0; i < nsub; i++) {
1332d2df058dSpatrick 		m = bwfm_sdio_newbuf();
1333d2df058dSpatrick 		if (m == NULL) {
13346f241297Spatrick 			ml_purge(&glom);
1335d2df058dSpatrick 			return;
1336d2df058dSpatrick 		}
13376f241297Spatrick 		ml_enqueue(&glom, m);
1338d2df058dSpatrick 		if (letoh16(sublen[i]) > m->m_len) {
13396f241297Spatrick 			ml_purge(&glom);
1340d2df058dSpatrick 			return;
1341d2df058dSpatrick 		}
1342d2df058dSpatrick 		if (bwfm_sdio_frame_read_write(sc, mtod(m, char *),
1343d2df058dSpatrick 		    letoh16(sublen[i]), 0)) {
13446f241297Spatrick 			ml_purge(&glom);
1345d2df058dSpatrick 			return;
1346d2df058dSpatrick 		}
13474ecea62eSpatrick 		m->m_len = m->m_pkthdr.len = letoh16(sublen[i]);
1348d2df058dSpatrick 	}
1349d2df058dSpatrick 
1350d2df058dSpatrick 	/* TODO: Verify actual superframe header */
13516f241297Spatrick 	m = MBUF_LIST_FIRST(&glom);
1352bdeccf4cSpatrick 	if (m->m_len >= sizeof(hwhdr) + sizeof(swhdr)) {
1353bdeccf4cSpatrick 		m_copydata(m, 0, sizeof(hwhdr), (caddr_t)&hwhdr);
1354bdeccf4cSpatrick 		m_copydata(m, sizeof(hwhdr), sizeof(swhdr), (caddr_t)&swhdr);
1355bdeccf4cSpatrick 		*nextlen = swhdr.nextlen << 4;
1356bdeccf4cSpatrick 		m_adj(m, sizeof(struct bwfm_sdio_hwhdr) +
1357bdeccf4cSpatrick 		    sizeof(struct bwfm_sdio_swhdr));
1358bdeccf4cSpatrick 	}
1359d2df058dSpatrick 
13606f241297Spatrick 	while ((m = ml_dequeue(&glom)) != NULL) {
1361d2df058dSpatrick 		if (m->m_len < sizeof(hwhdr) + sizeof(swhdr))
1362d2df058dSpatrick 			goto drop;
1363d2df058dSpatrick 
1364d2df058dSpatrick 		m_copydata(m, 0, sizeof(hwhdr), (caddr_t)&hwhdr);
1365d2df058dSpatrick 		m_copydata(m, sizeof(hwhdr), sizeof(swhdr), (caddr_t)&swhdr);
1366d2df058dSpatrick 
1367d2df058dSpatrick 		hwhdr.frmlen = letoh16(hwhdr.frmlen);
1368d2df058dSpatrick 		hwhdr.cksum = letoh16(hwhdr.cksum);
1369d2df058dSpatrick 
1370d2df058dSpatrick 		if (hwhdr.frmlen == 0 && hwhdr.cksum == 0)
1371d2df058dSpatrick 			goto drop;
1372d2df058dSpatrick 
1373d2df058dSpatrick 		if ((hwhdr.frmlen ^ hwhdr.cksum) != 0xffff) {
1374d2df058dSpatrick 			printf("%s: checksum error\n", DEVNAME(sc));
1375d2df058dSpatrick 			goto drop;
1376d2df058dSpatrick 		}
1377d2df058dSpatrick 
1378d2df058dSpatrick 		if (hwhdr.frmlen < sizeof(hwhdr) + sizeof(swhdr)) {
1379d2df058dSpatrick 			printf("%s: length error\n", DEVNAME(sc));
1380d2df058dSpatrick 			goto drop;
1381d2df058dSpatrick 		}
1382d2df058dSpatrick 
1383d2df058dSpatrick 		flen = hwhdr.frmlen - (sizeof(hwhdr) + sizeof(swhdr));
1384d2df058dSpatrick 		if (flen == 0)
1385d2df058dSpatrick 			goto drop;
1386d2df058dSpatrick 		if (m->m_len < flen)
1387d2df058dSpatrick 			goto drop;
1388d2df058dSpatrick 
1389d2df058dSpatrick 		if (swhdr.dataoff < (sizeof(hwhdr) + sizeof(swhdr)))
1390d2df058dSpatrick 			goto drop;
1391d2df058dSpatrick 
1392d2df058dSpatrick 		off = swhdr.dataoff - (sizeof(hwhdr) + sizeof(swhdr));
1393d2df058dSpatrick 		if (off > flen)
1394d2df058dSpatrick 			goto drop;
1395d2df058dSpatrick 
1396d2df058dSpatrick 		switch (swhdr.chanflag & BWFM_SDIO_SWHDR_CHANNEL_MASK) {
1397d2df058dSpatrick 		case BWFM_SDIO_SWHDR_CHANNEL_CONTROL:
1398d2df058dSpatrick 			printf("%s: control channel not allowed in glom\n",
1399d2df058dSpatrick 			    DEVNAME(sc));
1400d2df058dSpatrick 			goto drop;
1401d2df058dSpatrick 		case BWFM_SDIO_SWHDR_CHANNEL_EVENT:
1402d2df058dSpatrick 		case BWFM_SDIO_SWHDR_CHANNEL_DATA:
1403d2df058dSpatrick 			m_adj(m, swhdr.dataoff);
14046f241297Spatrick 			sc->sc_sc.sc_proto_ops->proto_rx(&sc->sc_sc, m, ml);
1405d2df058dSpatrick 			break;
1406d2df058dSpatrick 		case BWFM_SDIO_SWHDR_CHANNEL_GLOM:
1407d2df058dSpatrick 			printf("%s: glom not allowed in glom\n",
1408d2df058dSpatrick 			    DEVNAME(sc));
1409d2df058dSpatrick 			goto drop;
1410d2df058dSpatrick 		default:
1411d2df058dSpatrick 			printf("%s: unknown channel\n", DEVNAME(sc));
1412d2df058dSpatrick 			goto drop;
1413d2df058dSpatrick 		}
1414d2df058dSpatrick 
1415d2df058dSpatrick 		continue;
1416d2df058dSpatrick drop:
1417d2df058dSpatrick 		ml_enqueue(&drop, m);
1418d2df058dSpatrick 	}
1419d2df058dSpatrick 
1420d2df058dSpatrick 	ml_purge(&drop);
1421d2df058dSpatrick }
1422d2df058dSpatrick 
1423d2df058dSpatrick int
bwfm_sdio_txcheck(struct bwfm_softc * bwfm)1424d2df058dSpatrick bwfm_sdio_txcheck(struct bwfm_softc *bwfm)
1425d2df058dSpatrick {
1426d2df058dSpatrick 	struct bwfm_sdio_softc *sc = (void *)bwfm;
1427d2df058dSpatrick 
14282802c178Spatrick 	if (sc->sc_tx_count >= 64)
1429d2df058dSpatrick 		return ENOBUFS;
1430d2df058dSpatrick 
1431d2df058dSpatrick 	return 0;
143232b2494eSpatrick }
143332b2494eSpatrick 
143432b2494eSpatrick int
bwfm_sdio_txdata(struct bwfm_softc * bwfm,struct mbuf * m)143532b2494eSpatrick bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf *m)
143632b2494eSpatrick {
143732b2494eSpatrick 	struct bwfm_sdio_softc *sc = (void *)bwfm;
143832b2494eSpatrick 
14392802c178Spatrick 	if (sc->sc_tx_count >= 64)
1440d2df058dSpatrick 		return ENOBUFS;
144132b2494eSpatrick 
14422802c178Spatrick 	sc->sc_tx_count++;
14432802c178Spatrick 	ml_enqueue(&sc->sc_tx_queue, m);
1444d2df058dSpatrick 	task_add(systq, &sc->sc_task);
1445d2df058dSpatrick 	return 0;
144632b2494eSpatrick }
144732b2494eSpatrick 
144832b2494eSpatrick int
bwfm_sdio_txctl(struct bwfm_softc * bwfm,void * arg)14492802c178Spatrick bwfm_sdio_txctl(struct bwfm_softc *bwfm, void *arg)
145032b2494eSpatrick {
145132b2494eSpatrick 	struct bwfm_sdio_softc *sc = (void *)bwfm;
14522802c178Spatrick 	struct bwfm_proto_bcdc_ctl *ctl = arg;
14532802c178Spatrick 	struct mbuf *m;
14542802c178Spatrick 
14558049d49eSkettenis 	KASSERT(ctl->len <= MCLBYTES);
14566e222054Skettenis 
14572802c178Spatrick 	MGET(m, M_DONTWAIT, MT_CONTROL);
14588049d49eSkettenis 	if (m == NULL)
14598049d49eSkettenis 		goto fail;
14608049d49eSkettenis 	if (ctl->len > MLEN) {
14618049d49eSkettenis 		MCLGET(m, M_DONTWAIT);
14628049d49eSkettenis 		if (!(m->m_flags & M_EXT)) {
14638049d49eSkettenis 			m_freem(m);
14648049d49eSkettenis 			goto fail;
14658049d49eSkettenis 		}
14662802c178Spatrick 	}
14672802c178Spatrick 	memcpy(mtod(m, char *), ctl->buf, ctl->len);
14682802c178Spatrick 	m->m_len = ctl->len;
14692802c178Spatrick 
14702802c178Spatrick 	TAILQ_INSERT_TAIL(&sc->sc_sc.sc_bcdc_rxctlq, ctl, next);
14712802c178Spatrick 	ml_enqueue(&sc->sc_tx_queue, m);
1472d2df058dSpatrick 	task_add(systq, &sc->sc_task);
1473d2df058dSpatrick 	return 0;
14748049d49eSkettenis 
14758049d49eSkettenis fail:
14768049d49eSkettenis 	free(ctl->buf, M_TEMP, ctl->len);
14778049d49eSkettenis 	free(ctl, M_TEMP, sizeof(*ctl));
14788049d49eSkettenis 	return 1;
147932b2494eSpatrick }
1480e4f06f9cSpatrick 
1481e4f06f9cSpatrick #ifdef BWFM_DEBUG
1482e4f06f9cSpatrick void
bwfm_sdio_debug_console(struct bwfm_sdio_softc * sc)1483e4f06f9cSpatrick bwfm_sdio_debug_console(struct bwfm_sdio_softc *sc)
1484e4f06f9cSpatrick {
1485e4f06f9cSpatrick 	struct bwfm_sdio_console c;
1486e4f06f9cSpatrick 	uint32_t newidx;
1487e4f06f9cSpatrick 	int err;
1488e4f06f9cSpatrick 
1489e4f06f9cSpatrick 	if (!sc->sc_console_addr)
1490e4f06f9cSpatrick 		return;
1491e4f06f9cSpatrick 
1492e4f06f9cSpatrick 	err = bwfm_sdio_ram_read_write(sc, sc->sc_console_addr,
1493e4f06f9cSpatrick 	    (char *)&c, sizeof(c), 0);
1494e4f06f9cSpatrick 	if (err)
1495e4f06f9cSpatrick 		return;
1496e4f06f9cSpatrick 
1497e4f06f9cSpatrick 	c.log_buf = letoh32(c.log_buf);
1498e4f06f9cSpatrick 	c.log_bufsz = letoh32(c.log_bufsz);
1499e4f06f9cSpatrick 	c.log_idx = letoh32(c.log_idx);
1500e4f06f9cSpatrick 
1501e4f06f9cSpatrick 	if (sc->sc_console_buf == NULL) {
1502e4f06f9cSpatrick 		sc->sc_console_buf = malloc(c.log_bufsz, M_DEVBUF,
1503e4f06f9cSpatrick 		    M_WAITOK|M_ZERO);
1504e4f06f9cSpatrick 		sc->sc_console_buf_size = c.log_bufsz;
1505e4f06f9cSpatrick 	}
1506e4f06f9cSpatrick 
1507e4f06f9cSpatrick 	newidx = c.log_idx;
1508e4f06f9cSpatrick 	if (newidx >= sc->sc_console_buf_size)
1509e4f06f9cSpatrick 		return;
1510e4f06f9cSpatrick 
1511e4f06f9cSpatrick 	err = bwfm_sdio_ram_read_write(sc, c.log_buf, sc->sc_console_buf,
1512e4f06f9cSpatrick 	    sc->sc_console_buf_size, 0);
1513e4f06f9cSpatrick 	if (err)
1514e4f06f9cSpatrick 		return;
1515e4f06f9cSpatrick 
1516e4f06f9cSpatrick 	if (newidx != sc->sc_console_readidx)
1517e4f06f9cSpatrick 		DPRINTFN(3, ("BWFM CONSOLE: "));
1518e4f06f9cSpatrick 	while (newidx != sc->sc_console_readidx) {
1519e4f06f9cSpatrick 		uint8_t ch = sc->sc_console_buf[sc->sc_console_readidx];
1520e4f06f9cSpatrick 		sc->sc_console_readidx++;
1521e4f06f9cSpatrick 		if (sc->sc_console_readidx == sc->sc_console_buf_size)
1522e4f06f9cSpatrick 			sc->sc_console_readidx = 0;
1523e4f06f9cSpatrick 		if (ch == '\r')
1524e4f06f9cSpatrick 			continue;
1525e4f06f9cSpatrick 		DPRINTFN(3, ("%c", ch));
1526e4f06f9cSpatrick 	}
1527e4f06f9cSpatrick }
1528e4f06f9cSpatrick #endif
1529