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