Module Name: src Committed By: mlelstv Date: Sun Sep 1 05:51:45 UTC 2019
Modified Files: src/sys/dev/sdmmc: if_bwfm_sdio.c Added Files: src/sys/dev/sdmmc: if_bwfm_sdio.h Log Message: >From OpenBSD: - All the missing pieces (firmware load, chip setup, protocol handling) TX queue and interrupt handling via sdmmc_task. Fix locking. Fix packet parsing. Add parser for original firmware config files. To generate a diff of this commit: cvs rdiff -u -r1.3 -r1.4 src/sys/dev/sdmmc/if_bwfm_sdio.c cvs rdiff -u -r0 -r1.1 src/sys/dev/sdmmc/if_bwfm_sdio.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/sdmmc/if_bwfm_sdio.c diff -u src/sys/dev/sdmmc/if_bwfm_sdio.c:1.3 src/sys/dev/sdmmc/if_bwfm_sdio.c:1.4 --- src/sys/dev/sdmmc/if_bwfm_sdio.c:1.3 Fri May 11 07:41:11 2018 +++ src/sys/dev/sdmmc/if_bwfm_sdio.c Sun Sep 1 05:51:45 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: if_bwfm_sdio.c,v 1.3 2018/05/11 07:41:11 maya Exp $ */ +/* $NetBSD: if_bwfm_sdio.c,v 1.4 2019/09/01 05:51:45 mlelstv Exp $ */ /* $OpenBSD: if_bwfm_sdio.c,v 1.1 2017/10/11 17:19:50 patrick Exp $ */ /* * Copyright (c) 2010-2016 Broadcom Corporation @@ -20,14 +20,13 @@ #include <sys/param.h> #include <sys/systm.h> #include <sys/buf.h> +#include <sys/endian.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/device.h> #include <sys/queue.h> #include <sys/socket.h> #include <sys/mutex.h> -#include <sys/workqueue.h> -#include <sys/pcq.h> #include <net/bpf.h> #include <net/if.h> @@ -37,20 +36,16 @@ #include <netinet/in.h> +#include <dev/firmload.h> + #include <net80211/ieee80211_var.h> +#include <dev/sdmmc/sdmmcdevs.h> #include <dev/sdmmc/sdmmcvar.h> #include <dev/ic/bwfmvar.h> #include <dev/ic/bwfmreg.h> - -#define BWFM_SDIO_CCCR_BRCM_CARDCAP 0xf0 -#define BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02 -#define BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04 -#define BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08 -#define BWFM_SDIO_CCCR_BRCM_CARDCTRL 0xf1 -#define BWFM_SDIO_CCCR_BRCM_CARDCTRL_WLANRESET 0x02 -#define BWFM_SDIO_CCCR_BRCM_SEPINT 0xf2 +#include <dev/sdmmc/if_bwfm_sdio.h> #ifdef BWFM_DEBUG #define DPRINTF(x) do { if (bwfm_debug > 0) printf x; } while (0) @@ -63,34 +58,122 @@ static int bwfm_debug = 2; #define DEVNAME(sc) device_xname((sc)->sc_sc.sc_dev) +enum bwfm_sdio_clkstate { + CLK_NONE, + CLK_SDONLY, + CLK_PENDING, + CLK_AVAIL +}; + struct bwfm_sdio_softc { - struct bwfm_softc sc_sc; + struct bwfm_softc sc_sc; + kmutex_t sc_lock; + kmutex_t sc_intr_lock; + + bool sc_bwfm_attached; + struct sdmmc_function **sc_sf; - uint32_t sc_bar0; + size_t sc_sf_size; + + uint32_t sc_bar0; + enum bwfm_sdio_clkstate sc_clkstate; + bool sc_sr_enabled; + bool sc_alp_only; + bool sc_sleeping; + + struct sdmmc_task sc_task; + bool sc_task_queued; + + uint8_t sc_tx_seq; + uint8_t sc_tx_max_seq; + int sc_tx_count; + MBUFQ_HEAD() sc_tx_queue; + + struct mbuf *sc_rxctl_queue; + kcondvar_t sc_rxctl_cv; + + void *sc_ih; + struct bwfm_core *sc_cc; + + char *sc_bounce_buf; + size_t sc_bounce_size; + + uint32_t sc_console_addr; + char *sc_console_buf; + size_t sc_console_buf_size; + uint32_t sc_console_readidx; }; -int bwfm_sdio_match(device_t, cfdata_t, void *); -void bwfm_sdio_attach(device_t, struct device *, void *); -int bwfm_sdio_detach(device_t, int); - -void bwfm_sdio_backplane(struct bwfm_sdio_softc *, uint32_t); -uint8_t bwfm_sdio_read_1(struct bwfm_sdio_softc *, uint32_t); -uint32_t bwfm_sdio_read_4(struct bwfm_sdio_softc *, uint32_t); -void bwfm_sdio_write_1(struct bwfm_sdio_softc *, uint32_t, +int bwfm_sdio_match(device_t, cfdata_t, void *); +void bwfm_sdio_attach(device_t, struct device *, void *); +int bwfm_sdio_detach(device_t, int); +void bwfm_sdio_attachhook(device_t); + +void bwfm_sdio_backplane(struct bwfm_sdio_softc *, uint32_t); +uint8_t bwfm_sdio_read_1(struct bwfm_sdio_softc *, uint32_t); +uint32_t bwfm_sdio_read_4(struct bwfm_sdio_softc *, uint32_t); +void bwfm_sdio_write_1(struct bwfm_sdio_softc *, uint32_t, uint8_t); -void bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t, +void bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t, + uint32_t); + +uint32_t bwfm_sdio_dev_read(struct bwfm_sdio_softc *, uint32_t); +void bwfm_sdio_dev_write(struct bwfm_sdio_softc *, uint32_t, uint32_t); -uint32_t bwfm_sdio_buscore_read(struct bwfm_softc *, uint32_t); -void bwfm_sdio_buscore_write(struct bwfm_softc *, uint32_t, +uint32_t bwfm_sdio_buscore_read(struct bwfm_softc *, uint32_t); +void bwfm_sdio_buscore_write(struct bwfm_softc *, uint32_t, uint32_t); -int bwfm_sdio_buscore_prepare(struct bwfm_softc *); -void bwfm_sdio_buscore_activate(struct bwfm_softc *, uint32_t); +int bwfm_sdio_buscore_prepare(struct bwfm_softc *); +void bwfm_sdio_buscore_activate(struct bwfm_softc *, uint32_t); -int bwfm_sdio_txcheck(struct bwfm_softc *); -int bwfm_sdio_txdata(struct bwfm_softc *, struct mbuf *); -int bwfm_sdio_txctl(struct bwfm_softc *, char *, size_t); -int bwfm_sdio_rxctl(struct bwfm_softc *, char *, size_t *); +int bwfm_sdio_buf_read(struct bwfm_sdio_softc *, + struct sdmmc_function *, uint32_t, char *, size_t); +int bwfm_sdio_buf_write(struct bwfm_sdio_softc *, + struct sdmmc_function *, uint32_t, char *, size_t); + +struct mbuf *bwfm_sdio_newbuf(void); +void bwfm_qput(struct mbuf **, struct mbuf *); +struct mbuf *bwfm_qget(struct mbuf **); + +uint32_t bwfm_sdio_ram_read_write(struct bwfm_sdio_softc *, + uint32_t, char *, size_t, int); +uint32_t bwfm_sdio_frame_read_write(struct bwfm_sdio_softc *, + char *, size_t, int); + +int bwfm_sdio_intr(void *); +void bwfm_sdio_task(void *); +void bwfm_sdio_task1(struct bwfm_sdio_softc *); + +int bwfm_nvram_convert(u_char *, size_t, size_t *); +int bwfm_sdio_load_microcode(struct bwfm_sdio_softc *, + u_char *, size_t, u_char *, size_t); +void bwfm_sdio_clkctl(struct bwfm_sdio_softc *, + enum bwfm_sdio_clkstate, bool); +void bwfm_sdio_htclk(struct bwfm_sdio_softc *, bool, bool); + +int bwfm_sdio_bus_sleep(struct bwfm_sdio_softc *, bool, bool); +void bwfm_sdio_readshared(struct bwfm_sdio_softc *); + +int bwfm_sdio_txcheck(struct bwfm_softc *); +int bwfm_sdio_txdata(struct bwfm_softc *, struct mbuf **); +int bwfm_sdio_txctl(struct bwfm_softc *, char *, size_t); +int bwfm_sdio_rxctl(struct bwfm_softc *, char *, size_t *); + +int bwfm_sdio_tx_ok(struct bwfm_sdio_softc *); +void bwfm_sdio_tx_frames(struct bwfm_sdio_softc *); +void bwfm_sdio_tx_ctrlframe(struct bwfm_sdio_softc *, + struct mbuf *); +void bwfm_sdio_tx_dataframe(struct bwfm_sdio_softc *, + struct mbuf *); + +void bwfm_sdio_rx_frames(struct bwfm_sdio_softc *); +void bwfm_sdio_rx_glom(struct bwfm_sdio_softc *, + uint16_t *, int, uint16_t *); + +#ifdef BWFM_DEBUG +void bwfm_sdio_debug_console(struct bwfm_sdio_softc *); +#endif struct bwfm_bus_ops bwfm_sdio_bus_ops = { .bs_init = NULL, @@ -113,21 +196,54 @@ struct bwfm_buscore_ops bwfm_sdio_buscor CFATTACH_DECL_NEW(bwfm_sdio, sizeof(struct bwfm_sdio_softc), bwfm_sdio_match, bwfm_sdio_attach, bwfm_sdio_detach, NULL); +static const struct bwfm_sdio_product { + uint32_t manufacturer; + uint32_t product; + const char *cisinfo[4]; +} bwfm_sdio_products[] = { + { + SDMMC_VENDOR_BROADCOM, + SDMMC_PRODUCT_BROADCOM_BCM4330, + SDMMC_CIS_BROADCOM_BCM4330 + }, + { + SDMMC_VENDOR_BROADCOM, + SDMMC_PRODUCT_BROADCOM_BCM4334, + SDMMC_CIS_BROADCOM_BCM4334 + }, + { + SDMMC_VENDOR_BROADCOM, + SDMMC_PRODUCT_BROADCOM_BCM43143, + SDMMC_CIS_BROADCOM_BCM43143 + }, + { + SDMMC_VENDOR_BROADCOM, + SDMMC_PRODUCT_BROADCOM_BCM43430, + SDMMC_CIS_BROADCOM_BCM43430 + }, +}; + int bwfm_sdio_match(device_t parent, cfdata_t match, void *aux) { struct sdmmc_attach_args *saa = aux; struct sdmmc_function *sf = saa->sf; struct sdmmc_cis *cis; + const struct bwfm_sdio_product *bsp; + int i; /* Not SDIO. */ if (sf == NULL) return 0; - /* Look for Broadcom 433[04]. */ cis = &sf->sc->sc_fn0->cis; - if (cis->manufacturer != 0x02d0 || (cis->product != 0x4330 && - cis->product != 0x4334)) + for (i = 0; i < __arraycount(bwfm_sdio_products); ++i) { + bsp = &bwfm_sdio_products[i]; + if (cis->manufacturer == bsp->manufacturer && + cis->product == bsp->product) + break; + } + if (i >= __arraycount(bwfm_sdio_products)) return 0; /* We need both functions, but ... */ @@ -148,33 +264,46 @@ bwfm_sdio_attach(device_t parent, device struct sdmmc_attach_args *saa = aux; struct sdmmc_function *sf = saa->sf; struct bwfm_core *core; + uint32_t reg; + + sc->sc_sc.sc_dev = self; aprint_naive("\n"); + aprint_normal("\n"); - sc->sc_sf = malloc((sf->sc->sc_function_count + 1) * - sizeof(struct sdmmc_function *), M_DEVBUF, M_WAITOK); + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); + cv_init(&sc->sc_rxctl_cv, "bwfmctl"); + mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_NONE); + + sdmmc_init_task(&sc->sc_task, bwfm_sdio_task, sc); + sc->sc_task_queued = false; + + sc->sc_bounce_size = 64 * 1024; + sc->sc_bounce_buf = kmem_alloc(sc->sc_bounce_size, KM_SLEEP); + sc->sc_tx_seq = 0xff; + MBUFQ_INIT(&sc->sc_tx_queue); + sc->sc_rxctl_queue = NULL; + + sc->sc_sf_size = (sf->sc->sc_function_count + 1) + * sizeof(struct sdmmc_function *); + sc->sc_sf = kmem_zalloc(sc->sc_sf_size, KM_SLEEP); /* Copy all function pointers. */ SIMPLEQ_FOREACH(sf, &saa->sf->sc->sf_head, sf_list) { sc->sc_sf[sf->number] = sf; } - sf = saa->sf; - /* - * TODO: set block size to 64 for func 1, 512 for func 2. - * We might need to work on the SDMMC stack to be able to set - * a block size per function. Currently the IO code uses the - * SDHC controller's maximum block length. - */ + sdmmc_io_set_blocklen(sc->sc_sf[1]->sc, sc->sc_sf[1], 64); + sdmmc_io_set_blocklen(sc->sc_sf[2]->sc, sc->sc_sf[2], 512); - /* Enable Function 1. */ - if (sdmmc_io_function_enable(sc->sc_sf[1]) != 0) { - aprint_error_dev(self, "cannot enable function 1\n"); - goto err; - } + /* Enable Function 1. */ + if (sdmmc_io_function_enable(sc->sc_sf[1]) != 0) { + printf("%s: cannot enable function 1\n", DEVNAME(sc)); + return; + } - DPRINTFN(2, ("%s: F1 signature read @0x18000000=%x\n", DEVNAME(sc), - bwfm_sdio_read_4(sc, 0x18000000))); + DPRINTF(("%s: F1 signature read @0x18000000=%x\n", DEVNAME(sc), + bwfm_sdio_read_4(sc, 0x18000000))); /* Force PLL off */ bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, @@ -184,14 +313,29 @@ bwfm_sdio_attach(device_t parent, device sc->sc_sc.sc_buscore_ops = &bwfm_sdio_buscore_ops; if (bwfm_chip_attach(&sc->sc_sc) != 0) { aprint_error_dev(self, "cannot attach chip\n"); - goto err; + return; + } + + sc->sc_cc = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_CHIPCOMMON); + if (sc->sc_cc == NULL) { + aprint_error_dev(self, "cannot find chipcommon core\n"); + return; + } + + core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV); + if (core->co_rev >= 12) { + reg = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_SLEEPCSR); + if ((reg & BWFM_SDIO_FUNC1_SLEEPCSR_KSO) == 0) { + reg |= BWFM_SDIO_FUNC1_SLEEPCSR_KSO; + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SLEEPCSR, reg); + } } /* TODO: drive strength */ - bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL, - bwfm_sdio_read_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL) | - BWFM_SDIO_CCCR_BRCM_CARDCTRL_WLANRESET); + bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_CARDCTRL, + bwfm_sdio_read_1(sc, BWFM_SDIO_CCCR_CARDCTRL) | + BWFM_SDIO_CCCR_CARDCTRL_WLANRESET); core = bwfm_chip_get_pmu(&sc->sc_sc); bwfm_sdio_write_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL, @@ -199,14 +343,193 @@ bwfm_sdio_attach(device_t parent, device (BWFM_CHIP_REG_PMUCONTROL_RES_RELOAD << BWFM_CHIP_REG_PMUCONTROL_RES_SHIFT)); + sdmmc_io_function_disable(sc->sc_sf[2]); + + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, 0); + sc->sc_clkstate = CLK_SDONLY; + + config_mountroot(self, bwfm_sdio_attachhook); +} + +void +bwfm_sdio_attachhook(device_t self) +{ + struct bwfm_sdio_softc *sc = device_private(self); + struct bwfm_softc *bwfm = &sc->sc_sc; + firmware_handle_t fwh; + const char *name, *nvname; + u_char *ucode, *nvram; + size_t size, nvlen, nvsize; + uint32_t reg, clk; + int error; + + DPRINTF(("%s: chip 0x%08x rev %u\n", DEVNAME(sc), + bwfm->sc_chip.ch_chip, bwfm->sc_chip.ch_chiprev)); + switch (bwfm->sc_chip.ch_chip) { + case BRCM_CC_4330_CHIP_ID: + name = "brcmfmac4330-sdio.bin"; + nvname = "brcmfmac4330-sdio.txt"; + break; + case BRCM_CC_4334_CHIP_ID: + name = "brcmfmac4334-sdio.bin"; + nvname = "brcmfmac4334-sdio.txt"; + break; + case BRCM_CC_4345_CHIP_ID: + name = "brcmfmac43455-sdio.bin"; + nvname = "brcmfmac43455-sdio.txt"; + break; + case BRCM_CC_43340_CHIP_ID: + name = "brcmfmac43340-sdio.bin"; + nvname = "brcmfmac43340-sdio.txt"; + break; + case BRCM_CC_4335_CHIP_ID: + if (bwfm->sc_chip.ch_chiprev < 2) { + name = "brcmfmac4335-sdio.bin"; + nvname = "brcmfmac4335-sdio.txt"; + } else { + name = "brcmfmac4339-sdio.bin"; + nvname = "brcmfmac4339-sdio.txt"; + } + break; + case BRCM_CC_4339_CHIP_ID: + name = "brcmfmac4339-sdio.bin"; + nvname = "brcmfmac4339-sdio.txt"; + break; + case BRCM_CC_43430_CHIP_ID: + if (bwfm->sc_chip.ch_chiprev == 0) { + name = "brcmfmac43430a0-sdio.bin"; + nvname = "brcmfmac43430a0-sdio.txt"; + } else { + name = "brcmfmac43430-sdio.bin"; + nvname = "brcmfmac43430-sdio.txt"; + } + break; + case BRCM_CC_4356_CHIP_ID: + name = "brcmfmac4356-sdio.bin"; + nvname = "brcmfmac4356-sdio.txt"; + break; + default: + printf("%s: unknown firmware for chip %s\n", + DEVNAME(sc), bwfm->sc_chip.ch_name); + goto err; + } + + if (firmware_open("if_bwfm", name, &fwh) != 0) { + printf("%s: failed firmware_open of file %s\n", + DEVNAME(sc), name); + goto err; + } + size = firmware_get_size(fwh); + ucode = firmware_malloc(size); + if (ucode == NULL) { + printf("%s: failed firmware_open of file %s\n", + DEVNAME(sc), name); + firmware_close(fwh); + goto err; + } + error = firmware_read(fwh, 0, ucode, size); + firmware_close(fwh); + if (error != 0) { + printf("%s: failed to read firmware (error %d)\n", + DEVNAME(sc), error); + goto err1; + } + + if (firmware_open("if_bwfm", nvname, &fwh) != 0) { + printf("%s: failed firmware_open of file %s\n", + DEVNAME(sc), nvname); + goto err1; + } + nvlen = firmware_get_size(fwh); + nvram = firmware_malloc(nvlen); + if (nvram == NULL) { + printf("%s: failed firmware_open of file %s\n", + DEVNAME(sc), name); + firmware_close(fwh); + goto err1; + } + error = firmware_read(fwh, 0, nvram, nvlen); + firmware_close(fwh); + if (error != 0) { + printf("%s: failed to read firmware (error %d)\n", + DEVNAME(sc), error); + goto err2; + } + + if (bwfm_nvram_convert(nvram, nvlen, &nvsize)) { + printf("%s: failed to convert nvram\n", DEVNAME(sc)); + goto err2; + } + + sc->sc_alp_only = true; + if (bwfm_sdio_load_microcode(sc, ucode, size, nvram, nvsize) != 0) { + printf("%s: could not load microcode\n", + DEVNAME(sc)); + goto err2; + } + sc->sc_alp_only = false; + + firmware_free(nvram, nvlen); + firmware_free(ucode, size); + + bwfm_sdio_clkctl(sc, CLK_AVAIL, false); + if (sc->sc_clkstate != CLK_AVAIL) { + printf("%s: could not access clock\n", + DEVNAME(sc)); + goto err; + } + + clk = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, + clk | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HT); + + bwfm_sdio_dev_write(sc, SDPCMD_TOSBMAILBOXDATA, + SDPCM_PROT_VERSION << SDPCM_PROT_VERSION_SHIFT); + if (sdmmc_io_function_enable(sc->sc_sf[2])) { + printf("%s: cannot enable function 2\n", DEVNAME(sc)); + goto err; + } + + bwfm_sdio_dev_write(sc, SDPCMD_HOSTINTMASK, + SDPCMD_INTSTATUS_HMB_SW_MASK | SDPCMD_INTSTATUS_CHIPACTIVE); + bwfm_sdio_write_1(sc, BWFM_SDIO_WATERMARK, 8); + + if (bwfm_chip_sr_capable(bwfm)) { + reg = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_WAKEUPCTRL); + reg |= BWFM_SDIO_FUNC1_WAKEUPCTRL_HTWAIT; + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_WAKEUPCTRL, reg); + bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_CARDCAP, + BWFM_SDIO_CCCR_CARDCAP_CMD14_SUPPORT | + BWFM_SDIO_CCCR_CARDCAP_CMD14_EXT); + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, + BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HT); + sc->sc_sr_enabled = 1; + } else { + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clk); + } + + sc->sc_ih = sdmmc_intr_establish(sc->sc_sc.sc_dev->dv_parent, + bwfm_sdio_intr, sc, DEVNAME(sc)); + if (sc->sc_ih == NULL) { + aprint_error_dev(self, "could not establish interrupt\n"); + bwfm_sdio_clkctl(sc, CLK_NONE, false); + return; + } + sdmmc_intr_enable(sc->sc_sf[1]); + sc->sc_sc.sc_bus_ops = &bwfm_sdio_bus_ops; sc->sc_sc.sc_proto_ops = &bwfm_proto_bcdc_ops; bwfm_attach(&sc->sc_sc); + sc->sc_bwfm_attached = true; return; +err2: + firmware_free(nvram, nvlen); +err1: + firmware_free(ucode, size); err: - free(sc->sc_sf, M_DEVBUF); + return; } int @@ -214,21 +537,37 @@ bwfm_sdio_detach(struct device *self, in { struct bwfm_sdio_softc *sc = (struct bwfm_sdio_softc *)self; - bwfm_detach(&sc->sc_sc, flags); +#ifdef BWFM_DEBUG + bwfm_sdio_debug_console(sc); +#endif + + if (sc->sc_ih) { + sdmmc_intr_disable(sc->sc_sf[1]); + sdmmc_intr_disestablish(sc->sc_ih); + } + if (sc->sc_bwfm_attached) + bwfm_detach(&sc->sc_sc, flags); - free(sc->sc_sf, M_DEVBUF); + kmem_free(sc->sc_sf, sc->sc_sf_size); + kmem_free(sc->sc_bounce_buf, sc->sc_bounce_size); + + mutex_destroy(&sc->sc_intr_lock); + cv_destroy(&sc->sc_rxctl_cv); + mutex_destroy(&sc->sc_lock); return 0; } void -bwfm_sdio_backplane(struct bwfm_sdio_softc *sc, uint32_t bar0) +bwfm_sdio_backplane(struct bwfm_sdio_softc *sc, uint32_t addr) { + uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK; + if (sc->sc_bar0 == bar0) return; bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRLOW, - (bar0 >> 8) & 0x80); + (bar0 >> 8) & 0xff); bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRMID, (bar0 >> 16) & 0xff); bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRHIGH, @@ -261,10 +600,9 @@ uint32_t bwfm_sdio_read_4(struct bwfm_sdio_softc *sc, uint32_t addr) { struct sdmmc_function *sf; - uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK; uint32_t rv; - bwfm_sdio_backplane(sc, bar0); + bwfm_sdio_backplane(sc, addr); addr &= BWFM_SDIO_SB_OFT_ADDR_MASK; addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; @@ -307,9 +645,8 @@ void bwfm_sdio_write_4(struct bwfm_sdio_softc *sc, uint32_t addr, uint32_t data) { struct sdmmc_function *sf; - uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK; - bwfm_sdio_backplane(sc, bar0); + bwfm_sdio_backplane(sc, addr); addr &= BWFM_SDIO_SB_OFT_ADDR_MASK; addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; @@ -328,14 +665,135 @@ bwfm_sdio_write_4(struct bwfm_sdio_softc sdmmc_io_write_4(sf, addr, data); } +int +bwfm_sdio_buf_read(struct bwfm_sdio_softc *sc, struct sdmmc_function *sf, + uint32_t reg, char *data, size_t size) +{ + int err; + + KASSERT(((vaddr_t)data & 0x3) == 0); + KASSERT((size & 0x3) == 0); + + if (sf == sc->sc_sf[1]) + err = sdmmc_io_read_region_1(sf, reg, data, size); + else + err = sdmmc_io_read_multi_1(sf, reg, data, size); + + if (err) + printf("%s: error %d\n", __func__, err); + + return err; +} + +int +bwfm_sdio_buf_write(struct bwfm_sdio_softc *sc, struct sdmmc_function *sf, + uint32_t reg, char *data, size_t size) +{ + int err; + + KASSERT(((vaddr_t)data & 0x3) == 0); + KASSERT((size & 0x3) == 0); + + err = sdmmc_io_write_region_1(sf, reg, data, size); + + if (err) + printf("%s: error %d\n", __func__, err); + + return err; +} + +uint32_t +bwfm_sdio_ram_read_write(struct bwfm_sdio_softc *sc, uint32_t reg, + char *data, size_t left, int write) +{ + uint32_t sbaddr, sdaddr, off; + size_t size; + int err; + + err = off = 0; + while (left > 0) { + sbaddr = reg + off; + bwfm_sdio_backplane(sc, sbaddr); + + sdaddr = sbaddr & BWFM_SDIO_SB_OFT_ADDR_MASK; + size = ulmin(left, (BWFM_SDIO_SB_OFT_ADDR_PAGE - sdaddr)); + sdaddr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; + + if (write) { + memcpy(sc->sc_bounce_buf, data + off, size); + if (roundup(size, 4) != size) + memset(sc->sc_bounce_buf + size, 0, + roundup(size, 4) - size); + err = bwfm_sdio_buf_write(sc, sc->sc_sf[1], sdaddr, + sc->sc_bounce_buf, roundup(size, 4)); + } else { + err = bwfm_sdio_buf_read(sc, sc->sc_sf[1], sdaddr, + sc->sc_bounce_buf, roundup(size, 4)); + memcpy(data + off, sc->sc_bounce_buf, size); + } + if (err) + break; + + off += size; + left -= size; + } + + return err; +} + +uint32_t +bwfm_sdio_frame_read_write(struct bwfm_sdio_softc *sc, + char *data, size_t size, int write) +{ + uint32_t addr; + int err; + + addr = sc->sc_cc->co_base; + bwfm_sdio_backplane(sc, addr); + + addr &= BWFM_SDIO_SB_OFT_ADDR_MASK; + addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; + + if (write) + err = bwfm_sdio_buf_write(sc, sc->sc_sf[2], addr, data, size); + else + err = bwfm_sdio_buf_read(sc, sc->sc_sf[2], addr, data, size); + + return err; +} + +uint32_t +bwfm_sdio_dev_read(struct bwfm_sdio_softc *sc, uint32_t reg) +{ + struct bwfm_core *core; + uint32_t val; + + core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV); + val = bwfm_sdio_read_4(sc, core->co_base + reg); + /* TODO: Workaround for 4335/4339 */ + + return val; +} + +void +bwfm_sdio_dev_write(struct bwfm_sdio_softc *sc, uint32_t reg, uint32_t val) +{ + struct bwfm_core *core; + + core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV); + bwfm_sdio_write_4(sc, core->co_base + reg, val); +} + uint32_t bwfm_sdio_buscore_read(struct bwfm_softc *bwfm, uint32_t reg) { struct bwfm_sdio_softc *sc = (void *)bwfm; uint32_t val; + mutex_enter(&sc->sc_lock); val = bwfm_sdio_read_4(sc, reg); /* TODO: Workaround for 4335/4339 */ + mutex_exit(&sc->sc_lock); return val; } @@ -344,7 +802,10 @@ void bwfm_sdio_buscore_write(struct bwfm_softc *bwfm, uint32_t reg, uint32_t val) { struct bwfm_sdio_softc *sc = (void *)bwfm; + + mutex_enter(&sc->sc_lock); bwfm_sdio_write_4(sc, reg, val); + mutex_exit(&sc->sc_lock); } int @@ -352,7 +813,9 @@ bwfm_sdio_buscore_prepare(struct bwfm_so { struct bwfm_sdio_softc *sc = (void *)bwfm; uint8_t clkval, clkset, clkmask; - int i; + int i, error = 0; + + mutex_enter(&sc->sc_lock); clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF; @@ -365,7 +828,8 @@ bwfm_sdio_buscore_prepare(struct bwfm_so if ((clkval & ~clkmask) != clkset) { printf("%s: wrote 0x%02x read 0x%02x\n", DEVNAME(sc), clkset, clkval); - return 1; + error = 1; + goto done; } for (i = 1000; i > 0; i--) { @@ -377,7 +841,8 @@ bwfm_sdio_buscore_prepare(struct bwfm_so if (i == 0) { printf("%s: timeout on ALPAV wait, clkval 0x%02x\n", DEVNAME(sc), clkval); - return 1; + error = 1; + goto done; } clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF | @@ -387,7 +852,10 @@ bwfm_sdio_buscore_prepare(struct bwfm_so bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SDIOPULLUP, 0); - return 0; +done: + mutex_exit(&sc->sc_lock); + + return error; } void @@ -400,56 +868,953 @@ bwfm_sdio_buscore_activate(struct bwfm_s bwfm_sdio_buscore_write(&sc->sc_sc, core->co_base + BWFM_SDPCMD_INTSTATUS, 0xFFFFFFFF); -#if notyet + mutex_enter(&sc->sc_lock); if (rstvec) - bwfm_sdio_ram_write(&sc->sc_sc, 0, &rstvec, sizeof(rstvec)); -#endif + bwfm_sdio_ram_read_write(sc, 0, (char *)&rstvec, + sizeof(rstvec), 1); + mutex_exit(&sc->sc_lock); +} + +struct mbuf * +bwfm_sdio_newbuf(void) +{ + struct mbuf *m; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return NULL; + + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_freem(m); + return NULL; + } + + m->m_len = m->m_pkthdr.len = MCLBYTES; + return m; +} + +struct mbuf * +bwfm_qget(struct mbuf **q) +{ + struct mbuf *m = NULL; + + if (*q != NULL) { + m = *q; + *q = m->m_next; + m->m_next = NULL; + } + + return m; +} + +void +bwfm_qput(struct mbuf **q, struct mbuf *m) +{ + + if (*q == NULL) + *q = m; + else + m_cat(*q, m); } int -bwfm_sdio_txcheck(struct bwfm_softc *bwfm, struct mbuf *m) +bwfm_sdio_txcheck(struct bwfm_softc *bwfm) { - DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); + struct bwfm_sdio_softc *sc = (void *)bwfm; + int error = 0; - return 0; + mutex_enter(&sc->sc_lock); + if (sc->sc_tx_count >= 64) + error = ENOBUFS; + mutex_exit(&sc->sc_lock); + + return error; } int -bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf *m) +bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf **mp) { -#ifdef BWFM_DEBUG struct bwfm_sdio_softc *sc = (void *)bwfm; -#endif - int ret = 1; - DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); + if (sc->sc_tx_count >= 64) { + printf("%s: tx count limit reached\n",DEVNAME(sc)); + return ENOBUFS; + } + + mutex_enter(&sc->sc_lock); + sc->sc_tx_count++; + MBUFQ_ENQUEUE(&sc->sc_tx_queue, *mp); + mutex_exit(&sc->sc_lock); - return ret; + bwfm_sdio_intr(sc); + + return 0; } int bwfm_sdio_txctl(struct bwfm_softc *bwfm, char *buf, size_t len) { -#ifdef BWFM_DEBUG struct bwfm_sdio_softc *sc = (void *)bwfm; + struct mbuf *m; + + KASSERT(len <= MCLBYTES); + + MGET(m, M_DONTWAIT, MT_CONTROL); + if (m == NULL) + goto fail; + if (len > MLEN) { + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_freem(m); + goto fail; + } + } + memcpy(mtod(m, char *), buf, len); + m->m_len = len; + + mutex_enter(&sc->sc_lock); + MBUFQ_ENQUEUE(&sc->sc_tx_queue, m); + mutex_exit(&sc->sc_lock); + + bwfm_sdio_intr(sc); + + return 0; + +fail: + return ENOBUFS; +} + +int +bwfm_nvram_convert(u_char *buf, size_t len, size_t *newlenp) +{ + u_char *src, *dst, *end = buf + len; + bool skip = false; + size_t count = 0, pad; + uint32_t token; + + for (src = buf, dst = buf; src != end; ++src) { + if (*src == '\n') { + if (count > 0) + *dst++ = '\0'; + count = 0; + skip = false; + continue; + } + if (skip) + continue; + if (*src == '#' && count == 0) { + skip = true; + continue; + } + if (*src == '\r') + continue; + *dst++ = *src; + ++count; + } + + count = dst - buf; + pad = roundup(count + 1, 4) - count; + + if (count + pad + sizeof(token) > len) + return 1; + + memset(dst, 0, pad); + count += pad; + dst += pad; + + token = (count / 4) & 0xffff; + token |= ~token << 16; + token = htole32(token); + + memcpy(dst, &token, sizeof(token)); + count += sizeof(token); + + *newlenp = count ; + + return 0; +} + +int +bwfm_sdio_load_microcode(struct bwfm_sdio_softc *sc, u_char *ucode, size_t size, + u_char *nvram, size_t nvlen) +{ + struct bwfm_softc *bwfm = &sc->sc_sc; + char *verify = NULL; + int err = 0; + + bwfm_sdio_clkctl(sc, CLK_AVAIL, false); + + DPRINTF(("ucode %zu bytes to 0x%08lx\n", size, + (u_long)bwfm->sc_chip.ch_rambase)); + /* Upload firmware */ + err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase, + ucode, size, 1); + if (err) + goto out; + + /* Verify firmware */ + verify = kmem_zalloc(size, KM_SLEEP); + err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase, + verify, size, 0); + if (err || memcmp(verify, ucode, size)) { + printf("%s: firmware verification failed\n", + DEVNAME(sc)); + kmem_free(verify, size); + goto out; + } + kmem_free(verify, size); + + DPRINTF(("nvram %zu bytes to 0x%08lx\n", nvlen, + (u_long)bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize + - nvlen)); + /* Upload nvram */ + err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase + + bwfm->sc_chip.ch_ramsize - nvlen, nvram, nvlen, 1); + if (err) + goto out; + + /* Verify nvram */ + verify = kmem_zalloc(nvlen, KM_SLEEP); + err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase + + bwfm->sc_chip.ch_ramsize - nvlen, verify, nvlen, 0); + if (err || memcmp(verify, nvram, nvlen)) { + printf("%s: nvram verification failed\n", + DEVNAME(sc)); + kmem_free(verify, nvlen); + goto out; + } + kmem_free(verify, nvlen); + + DPRINTF(("Reset core 0x%08x\n", *(uint32_t *)ucode)); + /* Load reset vector from firmware and kickstart core. */ + bwfm_chip_set_active(bwfm, *(uint32_t *)ucode); + +out: + bwfm_sdio_clkctl(sc, CLK_SDONLY, false); + return err; +} + +void +bwfm_sdio_clkctl(struct bwfm_sdio_softc *sc, enum bwfm_sdio_clkstate newstate, + bool pendok) +{ + enum bwfm_sdio_clkstate oldstate; + + oldstate = sc->sc_clkstate; + if (oldstate == newstate) + return; + + switch (newstate) { + case CLK_AVAIL: + if (oldstate == CLK_NONE) + sc->sc_clkstate = CLK_SDONLY; /* XXX */ + bwfm_sdio_htclk(sc, true, pendok); + break; + case CLK_SDONLY: + if (oldstate == CLK_NONE) + sc->sc_clkstate = newstate; + else if (oldstate == CLK_AVAIL) + bwfm_sdio_htclk(sc, false, false); + else + printf("%s: clkctl %d -> %d\n", DEVNAME(sc), + sc->sc_clkstate, newstate); + break; + case CLK_NONE: + if (oldstate == CLK_AVAIL) + bwfm_sdio_htclk(sc, false, false); + sc->sc_clkstate = newstate; + break; + default: + break; + } + + DPRINTF(("%s: %d -> %d = %d\n", DEVNAME(sc), oldstate, newstate, + sc->sc_clkstate)); +} + +void +bwfm_sdio_htclk(struct bwfm_sdio_softc *sc, bool on, bool pendok) +{ + uint32_t clkctl, devctl, req; + int i; + + if (sc->sc_sr_enabled) { + if (on) + sc->sc_clkstate = CLK_AVAIL; + else + sc->sc_clkstate = CLK_SDONLY; + return; + } + + if (on) { + if (sc->sc_alp_only) + req = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ; + else + req = BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL_REQ; + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, req); + + clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); + if (!BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(clkctl, sc->sc_alp_only) + && pendok) { + devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL); + devctl |= BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY; + bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl); + sc->sc_clkstate = CLK_PENDING; + return; + } else if (sc->sc_clkstate == CLK_PENDING) { + devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL); + devctl &= ~BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY; + bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl); + } + + for (i = 0; i < 5000; i++) { + if (BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(clkctl, + sc->sc_alp_only)) + break; + clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR +); + delay(1000); + } + if (!BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(clkctl, sc->sc_alp_only)) +{ + printf("%s: HT avail timeout\n", DEVNAME(sc)); + return; + } + + sc->sc_clkstate = CLK_AVAIL; + } else { + if (sc->sc_clkstate == CLK_PENDING) { + devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL); + devctl &= ~BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY; + bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl); + } + sc->sc_clkstate = CLK_SDONLY; + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, 0); + } +} + +#if notyet +int +bwfm_sdio_bus_sleep(struct bwfm_sdio_softc *sc, bool sleep, bool pendok) +{ + uint32_t clkctl; + + if (sc->sleeping == sleep) + return 0; + + if (sc->sc_sr_enabled) { + if (sleep) { + clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); + if ((clkctl & BWFM_SDIO_FUNC1_CHIPCLKCSR_CSR_MASK) == 0) + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ); + } + /* kso_ctrl(sc, sleep) */ + } + + if (sleep) { + if (!sc->sc_sr_enabled) + bwfm_sdio_clkctl(sc, CLK_NONE, pendok); + } else { + bwfm_sdio_clkctl(sc, CLK_AVAIL, pendok); + } + + sc->sleeping = sleep; + + return 0; +} #endif - int ret = 1; - DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); +void +bwfm_sdio_readshared(struct bwfm_sdio_softc *sc) +{ + struct bwfm_softc *bwfm = &sc->sc_sc; + struct bwfm_sdio_sdpcm sdpcm; + uint32_t addr, shaddr; + int err; + + bwfm_sdio_clkctl(sc, CLK_AVAIL, false); + if (sc->sc_clkstate != CLK_AVAIL) + return; + + shaddr = bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize - 4; + if (!bwfm->sc_chip.ch_rambase && sc->sc_sr_enabled) + shaddr -= bwfm->sc_chip.ch_srsize; + + err = bwfm_sdio_ram_read_write(sc, shaddr, (char *)&addr, + sizeof(addr), 0); + if (err) + return; - return ret; + addr = le32toh(addr); + if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) + return; + + err = bwfm_sdio_ram_read_write(sc, addr, (char *)&sdpcm, + sizeof(sdpcm), 0); + if (err) + return; + + sc->sc_console_addr = le32toh(sdpcm.console_addr); } int -bwfm_sdio_rxctl(struct bwfm_softc *bwfm, char *buf, size_t *len) +bwfm_sdio_intr(void *v) { + struct bwfm_sdio_softc *sc = (void *)v; + + DPRINTF(("%s: sdio_intr\n", DEVNAME(sc))); + + mutex_enter(&sc->sc_intr_lock); + if (!sc->sc_task_queued) { + sc->sc_task_queued = true; + sdmmc_add_task(sc->sc_sf[1]->sc, &sc->sc_task); + } + mutex_exit(&sc->sc_intr_lock); + return 1; +} + +void +bwfm_sdio_task(void *v) +{ + struct bwfm_sdio_softc *sc = (void *)v; + + mutex_enter(&sc->sc_intr_lock); + while (sc->sc_task_queued) { + sc->sc_task_queued = false; + mutex_exit(&sc->sc_intr_lock); + + mutex_enter(&sc->sc_lock); + bwfm_sdio_task1(sc); #ifdef BWFM_DEBUG - struct bwfm_sdio_softc *sc = (void *)bwfm; + bwfm_sdio_debug_console(sc); #endif - int ret = 1; + mutex_exit(&sc->sc_lock); + + mutex_enter(&sc->sc_intr_lock); + } + mutex_exit(&sc->sc_intr_lock); +} + +void +bwfm_sdio_task1(struct bwfm_sdio_softc *sc) +{ + uint32_t clkctl, devctl, intstat, hostint; + + if (!sc->sc_sr_enabled && sc->sc_clkstate == CLK_PENDING) { + clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); + if (BWFM_SDIO_FUNC1_CHIPCLKCSR_HTAV(clkctl)) { + devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL); + devctl &= ~BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY; + bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl); + sc->sc_clkstate = CLK_AVAIL; + } + } + + intstat = bwfm_sdio_dev_read(sc, BWFM_SDPCMD_INTSTATUS); + DPRINTF(("%s: intstat 0x%" PRIx32 "\n", DEVNAME(sc), intstat)); + intstat &= (SDPCMD_INTSTATUS_HMB_SW_MASK|SDPCMD_INTSTATUS_CHIPACTIVE); + /* XXX fc state */ + if (intstat) + bwfm_sdio_dev_write(sc, BWFM_SDPCMD_INTSTATUS, intstat); + + if (intstat & SDPCMD_INTSTATUS_HMB_HOST_INT) { + hostint = bwfm_sdio_dev_read(sc, SDPCMD_TOHOSTMAILBOXDATA); + DPRINTF(("%s: hostint 0x%" PRIx32 "\n", DEVNAME(sc), hostint)); + bwfm_sdio_dev_write(sc, SDPCMD_TOSBMAILBOX, + SDPCMD_TOSBMAILBOX_INT_ACK); + if (hostint & SDPCMD_TOHOSTMAILBOXDATA_NAKHANDLED) + intstat |= SDPCMD_INTSTATUS_HMB_FRAME_IND; + if (hostint & SDPCMD_TOHOSTMAILBOXDATA_DEVREADY || + hostint & SDPCMD_TOHOSTMAILBOXDATA_FWREADY) + bwfm_sdio_readshared(sc); + } + + /* FIXME: Might stall if we don't when not set. */ + if (intstat & SDPCMD_INTSTATUS_HMB_FRAME_IND) + bwfm_sdio_rx_frames(sc); + + if (MBUFQ_FIRST(&sc->sc_tx_queue)) + bwfm_sdio_tx_frames(sc); +} + +int +bwfm_sdio_tx_ok(struct bwfm_sdio_softc *sc) +{ + return (uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq) != 0 && + ((uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq) & 0x80) == 0; +} + +void +bwfm_sdio_tx_frames(struct bwfm_sdio_softc *sc) +{ + struct mbuf *m; + struct ifnet *ifp = sc->sc_sc.sc_ic.ic_ifp; + bool ifstart = false; + int i; + + if (!bwfm_sdio_tx_ok(sc)) + return; + + i = uimin((uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq), 32); + while (i--) { + MBUFQ_DEQUEUE(&sc->sc_tx_queue, m); + if (m == NULL) + break; + + if (m->m_type == MT_CONTROL) + bwfm_sdio_tx_ctrlframe(sc, m); + else { + bwfm_sdio_tx_dataframe(sc, m); + ifp->if_opackets++; + ifstart = true; + } + + m_freem(m); + } + + if (ifstart) { + ifp->if_flags &= ~IFF_OACTIVE; + if_schedule_deferred_start(ifp); + } +} + +void +bwfm_sdio_tx_ctrlframe(struct bwfm_sdio_softc *sc, struct mbuf *m) +{ + struct bwfm_sdio_hwhdr *hwhdr; + struct bwfm_sdio_swhdr *swhdr; + size_t len, roundto; + + len = sizeof(*hwhdr) + sizeof(*swhdr) + m->m_len; + + /* Zero-pad to either block-size or 4-byte alignment. */ + if (len > 512 && (len % 512) != 0) + roundto = 512; + else + roundto = 4; + + KASSERT(roundup(len, roundto) <= sc->sc_bounce_size); + + hwhdr = (void *)sc->sc_bounce_buf; + hwhdr->frmlen = htole16(len); + hwhdr->cksum = htole16(~len); + + swhdr = (void *)&hwhdr[1]; + swhdr->seqnr = sc->sc_tx_seq++; + swhdr->chanflag = BWFM_SDIO_SWHDR_CHANNEL_CONTROL; + swhdr->nextlen = 0; + swhdr->dataoff = sizeof(*hwhdr) + sizeof(*swhdr); + swhdr->maxseqnr = 0; + + m_copydata(m, 0, m->m_len, &swhdr[1]); + + if (roundup(len, roundto) != len) + memset(sc->sc_bounce_buf + len, 0, + roundup(len, roundto) - len); + + bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf, + roundup(len, roundto), 1); +} + +void +bwfm_sdio_tx_dataframe(struct bwfm_sdio_softc *sc, struct mbuf *m) +{ + struct bwfm_sdio_hwhdr *hwhdr; + struct bwfm_sdio_swhdr *swhdr; + struct bwfm_proto_bcdc_hdr *bcdc; + size_t len, roundto; + + len = sizeof(*hwhdr) + sizeof(*swhdr) + sizeof(*bcdc) + + m->m_pkthdr.len; + + /* Zero-pad to either block-size or 4-byte alignment. */ + if (len > 512 && (len % 512) != 0) + roundto = 512; + else + roundto = 4; + + KASSERT(roundup(len, roundto) <= sc->sc_bounce_size); + + hwhdr = (void *)sc->sc_bounce_buf; + hwhdr->frmlen = htole16(len); + hwhdr->cksum = htole16(~len); + + swhdr = (void *)&hwhdr[1]; + swhdr->seqnr = sc->sc_tx_seq++; + swhdr->chanflag = BWFM_SDIO_SWHDR_CHANNEL_DATA; + swhdr->nextlen = 0; + swhdr->dataoff = sizeof(*hwhdr) + sizeof(*swhdr); + swhdr->maxseqnr = 0; + + bcdc = (void *)&swhdr[1]; + bcdc->data_offset = 0; + bcdc->priority = WME_AC_BE; + bcdc->flags = BWFM_BCDC_FLAG_VER(BWFM_BCDC_FLAG_PROTO_VER); + bcdc->flags2 = 0; + + m_copydata(m, 0, m->m_pkthdr.len, &bcdc[1]); + + if (roundup(len, roundto) != len) + memset(sc->sc_bounce_buf + len, 0, + roundup(len, roundto) - len); + + bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf, + roundup(len, roundto), 1); + + sc->sc_tx_count--; +} + +int +bwfm_sdio_rxctl(struct bwfm_softc *bwfm, char *buf, size_t *lenp) +{ + struct bwfm_sdio_softc *sc = (void *)bwfm; + struct mbuf *m; + int err = 0; + + mutex_enter(&sc->sc_lock); + while ((m = bwfm_qget(&sc->sc_rxctl_queue)) == NULL) { + err = cv_timedwait(&sc->sc_rxctl_cv, &sc->sc_lock, + mstohz(5000)); + if (err == EWOULDBLOCK) + break; + } + mutex_exit(&sc->sc_lock); + + if (err) + return 1; + + if (m->m_len < *lenp) { + m_freem(m); + return 1; + } + + *lenp = m->m_len; + m_copydata(m, 0, m->m_len, buf); + m_freem(m); + return 0; +} + +void +bwfm_sdio_rx_frames(struct bwfm_sdio_softc *sc) +{ + struct bwfm_sdio_hwhdr *hwhdr; + struct bwfm_sdio_swhdr *swhdr; + struct bwfm_proto_bcdc_hdr *bcdc; + uint16_t *sublen, nextlen = 0; + struct mbuf *m; + size_t flen, off, hoff; + char *data; + int nsub; + + hwhdr = (struct bwfm_sdio_hwhdr *)sc->sc_bounce_buf; + swhdr = (struct bwfm_sdio_swhdr *)&hwhdr[1]; + data = (char *)&swhdr[1]; + + for (;;) { + /* If we know the next size, just read ahead. */ + if (nextlen) { + if (bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf, + nextlen, 0)) + break; + } else { + if (bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf, + sizeof(*hwhdr) + sizeof(*swhdr), 0)) + break; + } + + hwhdr->frmlen = le16toh(hwhdr->frmlen); + hwhdr->cksum = le16toh(hwhdr->cksum); + + if (hwhdr->frmlen == 0 && hwhdr->cksum == 0) + break; + + if ((hwhdr->frmlen ^ hwhdr->cksum) != 0xffff) { + printf("%s: checksum error\n", DEVNAME(sc)); + break; + } + + if (hwhdr->frmlen < sizeof(*hwhdr) + sizeof(*swhdr)) { + printf("%s: length error\n", DEVNAME(sc)); + break; + } + + if (nextlen && hwhdr->frmlen > nextlen) { + printf("%s: read ahead length error (%u > %u)\n", + DEVNAME(sc), hwhdr->frmlen, nextlen); + break; + } - DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); + sc->sc_tx_max_seq = swhdr->maxseqnr; - return ret; + flen = hwhdr->frmlen - (sizeof(*hwhdr) + sizeof(*swhdr)); + if (flen == 0) { + nextlen = swhdr->nextlen << 4; + continue; + } + + if (!nextlen) { + KASSERT(roundup(flen, 4) <= sc->sc_bounce_size - + (sizeof(*hwhdr) + sizeof(*swhdr))); + if (bwfm_sdio_frame_read_write(sc, data, + roundup(flen, 4), 0)) + break; + } + + if (swhdr->dataoff < (sizeof(*hwhdr) + sizeof(*swhdr))) { + printf("%s: data offset %u in header\n", + DEVNAME(sc), swhdr->dataoff); + break; + } + + off = swhdr->dataoff - (sizeof(*hwhdr) + sizeof(*swhdr)); + if (off > flen) { + printf("%s: offset %zu beyond end %zu\n", + DEVNAME(sc), off, flen); + break; + } + + switch (swhdr->chanflag & BWFM_SDIO_SWHDR_CHANNEL_MASK) { + case BWFM_SDIO_SWHDR_CHANNEL_CONTROL: + m = bwfm_sdio_newbuf(); + if (m == NULL) + break; + if (flen - off > m->m_len) { + printf("%s: ctl bigger than anticipated\n", + DEVNAME(sc)); + m_freem(m); + break; + } + m->m_len = m->m_pkthdr.len = flen - off; + memcpy(mtod(m, char *), data + off, flen - off); + bwfm_qput(&sc->sc_rxctl_queue, m); + cv_broadcast(&sc->sc_rxctl_cv); + nextlen = swhdr->nextlen << 4; + break; + case BWFM_SDIO_SWHDR_CHANNEL_EVENT: + case BWFM_SDIO_SWHDR_CHANNEL_DATA: + m = bwfm_sdio_newbuf(); + if (m == NULL) + break; + if (flen - off > m->m_len) { + printf("%s: frame bigger than anticipated\n", + DEVNAME(sc)); + m_freem(m); + break; + } + m->m_len = m->m_pkthdr.len = flen - off; + memcpy(mtod(m, char *), data + off, flen - off); + bcdc = mtod(m, struct bwfm_proto_bcdc_hdr *); + hoff = sizeof(*bcdc) + ((size_t)bcdc->data_offset << 2); + if (m->m_len < hoff) { + printf("%s: short bcdc packet %d < %zu\n", + DEVNAME(sc), m->m_len, hoff); + m_freem(m); + break; + } + m_adj(m, hoff); + bwfm_rx(&sc->sc_sc, m); + nextlen = swhdr->nextlen << 4; + break; + case BWFM_SDIO_SWHDR_CHANNEL_GLOM: + if ((flen % sizeof(uint16_t)) != 0) { + printf("%s: odd length (%zu) glom table\n", + DEVNAME(sc), flen); + break; + } + nsub = flen / sizeof(uint16_t); + sublen = kmem_zalloc(nsub * sizeof(uint16_t), KM_SLEEP); + memcpy(sublen, data, nsub * sizeof(uint16_t)); + bwfm_sdio_rx_glom(sc, sublen, nsub, &nextlen); + kmem_free(sublen, nsub * sizeof(uint16_t)); + break; + default: + printf("%s: unknown channel\n", DEVNAME(sc)); + break; + } + } } + +void +bwfm_sdio_rx_glom(struct bwfm_sdio_softc *sc, uint16_t *sublen, int nsub, + uint16_t *nextlen) +{ + struct bwfm_sdio_hwhdr hwhdr; + struct bwfm_sdio_swhdr swhdr; + struct bwfm_proto_bcdc_hdr *bcdc; + struct mbuf *m, *m0; + size_t flen, off, hoff; + int i; + + if (nsub == 0) + return; + + m0 = NULL; + for (i = 0; i < nsub; i++) { + m = bwfm_sdio_newbuf(); + if (m == NULL) { + m_freem(m0); + return; + } + bwfm_qput(&m0, m); + if (le16toh(sublen[i]) > m->m_len) { + m_freem(m0); + return; + } + if (bwfm_sdio_frame_read_write(sc, mtod(m, char *), + le16toh(sublen[i]), 0)) { + m_freem(m0); + return; + } + m->m_len = m->m_pkthdr.len = le16toh(sublen[i]); + } + + if (m0->m_len >= sizeof(hwhdr) + sizeof(swhdr)) { + m_copydata(m0, 0, sizeof(hwhdr), &hwhdr); + m_copydata(m0, sizeof(hwhdr), sizeof(swhdr), &swhdr); + + /* TODO: Verify actual superframe header */ + + /* remove superframe header */ + if (m0->m_len >= swhdr.dataoff) + m_adj(m0, swhdr.dataoff); + } + + *nextlen = 0; + while ((m = bwfm_qget(&m0)) != NULL) { + if (m->m_len < sizeof(hwhdr) + sizeof(swhdr)) { + printf("%s: tiny mbuf %d < %zu\n", DEVNAME(sc), + m->m_len, sizeof(hwhdr) + sizeof(swhdr)); + goto drop; + } + + m_copydata(m, 0, sizeof(hwhdr), &hwhdr); + m_copydata(m, sizeof(hwhdr), sizeof(swhdr), &swhdr); + + hwhdr.frmlen = le16toh(hwhdr.frmlen); + hwhdr.cksum = le16toh(hwhdr.cksum); + + if (hwhdr.frmlen == 0 && hwhdr.cksum == 0) + goto drop; + + if ((hwhdr.frmlen ^ hwhdr.cksum) != 0xffff) { + printf("%s: checksum error\n", DEVNAME(sc)); + goto drop; + } + + + if (hwhdr.frmlen < sizeof(hwhdr) + sizeof(swhdr)) { + printf("%s: length error\n", DEVNAME(sc)); + goto drop; + } + + flen = hwhdr.frmlen - (sizeof(hwhdr) + sizeof(swhdr)); + if (flen == 0) + goto drop; + + if (hwhdr.frmlen > m->m_len) { + printf("%s: short mbuf %d < %zu\n", + DEVNAME(sc),m->m_len,flen); + goto drop; + } + + if (swhdr.dataoff < (sizeof(hwhdr) + sizeof(swhdr))) { + printf("%s: data offset %u in header\n", + DEVNAME(sc), swhdr.dataoff); + goto drop; + } + + off = swhdr.dataoff - (sizeof(hwhdr) + sizeof(swhdr)); + if (off > flen) { + printf("%s: offset %zu beyond end %zu\n", + DEVNAME(sc), off, flen); + goto drop; + } + + m_adj(m, (int)hwhdr.frmlen - m->m_len); + *nextlen = swhdr.nextlen << 4; + + switch (swhdr.chanflag & BWFM_SDIO_SWHDR_CHANNEL_MASK) { + case BWFM_SDIO_SWHDR_CHANNEL_CONTROL: + printf("%s: control channel not allowed in glom\n", + DEVNAME(sc)); + goto drop; + case BWFM_SDIO_SWHDR_CHANNEL_EVENT: + case BWFM_SDIO_SWHDR_CHANNEL_DATA: + m_adj(m, swhdr.dataoff); + bcdc = mtod(m, struct bwfm_proto_bcdc_hdr *); + hoff = sizeof(*bcdc) + ((size_t)bcdc->data_offset << 2); + if (m->m_len < hoff) { + printf("%s: short bcdc packet %d < %zu\n", + DEVNAME(sc), m->m_len, hoff); + m_freem(m); + break; + } + m_adj(m, hoff); + bwfm_rx(&sc->sc_sc, m); + break; + case BWFM_SDIO_SWHDR_CHANNEL_GLOM: + printf("%s: glom not allowed in glom\n", + DEVNAME(sc)); + goto drop; + default: + printf("%s: unknown channel\n", DEVNAME(sc)); + goto drop; + } + + continue; +drop: + printf("rx dropped %p len %d\n",mtod(m, char *),m->m_pkthdr.len); + m_free(m); + } +} + +#ifdef BWFM_DEBUG +void +bwfm_sdio_debug_console(struct bwfm_sdio_softc *sc) +{ + struct bwfm_sdio_console c; + uint32_t newidx; + int err; + + if (!sc->sc_console_addr) + return; + + err = bwfm_sdio_ram_read_write(sc, sc->sc_console_addr, + (char *)&c, sizeof(c), 0); + if (err) + return; + + c.log_buf = le32toh(c.log_buf); + c.log_bufsz = le32toh(c.log_bufsz); + c.log_idx = le32toh(c.log_idx); + + if (sc->sc_console_buf == NULL) { + sc->sc_console_buf = malloc(c.log_bufsz, M_DEVBUF, + M_WAITOK|M_ZERO); + sc->sc_console_buf_size = c.log_bufsz; + } + + newidx = c.log_idx; + if (newidx >= sc->sc_console_buf_size) + return; + + err = bwfm_sdio_ram_read_write(sc, c.log_buf, sc->sc_console_buf, + sc->sc_console_buf_size, 0); + if (err) + return; + + if (newidx != sc->sc_console_readidx) + DPRINTFN(3, ("BWFM CONSOLE: ")); + while (newidx != sc->sc_console_readidx) { + uint8_t ch = sc->sc_console_buf[sc->sc_console_readidx]; + sc->sc_console_readidx++; + if (sc->sc_console_readidx == sc->sc_console_buf_size) + sc->sc_console_readidx = 0; + if (ch == '\r') + continue; + DPRINTFN(3, ("%c", ch)); + } +} +#endif Added files: Index: src/sys/dev/sdmmc/if_bwfm_sdio.h diff -u /dev/null src/sys/dev/sdmmc/if_bwfm_sdio.h:1.1 --- /dev/null Sun Sep 1 05:51:45 2019 +++ src/sys/dev/sdmmc/if_bwfm_sdio.h Sun Sep 1 05:51:45 2019 @@ -0,0 +1,199 @@ +/* $OpenBSD: if_bwfm_sdio.h,v 1.2 2018/05/19 10:43:10 patrick Exp $ */ +/* + * Copyright (c) 2010-2016 Broadcom Corporation + * Copyright (c) 2018 Patrick Wildt <patr...@blueri.se> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Registers */ +#define BWFM_SDIO_CCCR_CARDCAP 0xf0 +#define BWFM_SDIO_CCCR_CARDCAP_CMD14_SUPPORT (1 << 1) +#define BWFM_SDIO_CCCR_CARDCAP_CMD14_EXT (1 << 2) +#define BWFM_SDIO_CCCR_CARDCAP_CMD_NODEC (1 << 3) +#define BWFM_SDIO_CCCR_CARDCTRL 0xf1 +#define BWFM_SDIO_CCCR_CARDCTRL_WLANRESET (1 << 1) +#define BWFM_SDIO_CCCR_SEPINT 0xf2 +#define BWFM_SDIO_CCCR_SEPINT_MASK 0x01 +#define BWFM_SDIO_CCCR_SEPINT_OE (1 << 1) +#define BWFM_SDIO_CCCR_SEPINT_ACT_HI (1 << 2) + +#define BWFM_SDIO_WATERMARK 0x10008 +#define BWFM_SDIO_DEVICE_CTL 0x10009 +#define BWFM_SDIO_DEVICE_CTL_SETBUSY 0x01 +#define BWFM_SDIO_DEVICE_CTL_SPI_INTR_SYNC 0x02 +#define BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY 0x04 +#define BWFM_SDIO_DEVICE_CTL_PADS_ISO 0x08 +#define BWFM_SDIO_DEVICE_CTL_SB_RST_CTL 0x30 +#define BWFM_SDIO_DEVICE_CTL_RST_CORECTL 0x00 +#define BWFM_SDIO_DEVICE_CTL_RST_BPRESET 0x10 +#define BWFM_SDIO_DEVICE_CTL_RST_NOBPRESET 0x20 +#define BWFM_SDIO_FUNC1_SBADDRLOW 0x1000A +#define BWFM_SDIO_FUNC1_SBADDRMID 0x1000B +#define BWFM_SDIO_FUNC1_SBADDRHIGH 0x1000C +#define BWFM_SDIO_FUNC1_CHIPCLKCSR 0x1000E +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_ALP 0x01 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HT 0x02 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_ILP 0x04 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ 0x08 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL_REQ 0x10 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF 0x20 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL 0x40 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL 0x80 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_CSR_MASK 0x1F +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_AVBITS \ + (BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL | \ + BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL) +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_ALPAV(regval) \ + ((regval) & BWFM_SDIO_FUNC1_CHIPCLKCSR_AVBITS) +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_HTAV(regval) \ + (((regval) & BWFM_SDIO_FUNC1_CHIPCLKCSR_AVBITS) == BWFM_SDIO_FUNC1_CHIPCLKCSR_AVBITS) +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_ALPONLY(regval) \ + (BWFM_SDIO_FUNC1_CHIPCLKCSR_ALPAV(regval) && \ + !BWFM_SDIO_FUNC1_CHIPCLKCSR_HTAV(regval)) +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(regval, alponly) \ + (BWFM_SDIO_FUNC1_CHIPCLKCSR_ALPAV(regval) && \ + (alponly ? 1 : BWFM_SDIO_FUNC1_CHIPCLKCSR_HTAV(regval))) +#define BWFM_SDIO_FUNC1_SDIOPULLUP 0x1000F +#define BWFM_SDIO_FUNC1_WAKEUPCTRL 0x1001E +#define BWFM_SDIO_FUNC1_WAKEUPCTRL_HTWAIT (1 << 1) +#define BWFM_SDIO_FUNC1_SLEEPCSR 0x1001F +#define BWFM_SDIO_FUNC1_SLEEPCSR_KSO (1 << 0) +#define BWFM_SDIO_FUNC1_SLEEPCSR_DEVON (1 << 2) + +#define BWFM_SDIO_SB_OFT_ADDR_PAGE 0x08000 +#define BWFM_SDIO_SB_OFT_ADDR_MASK 0x07FFF +#define BWFM_SDIO_SB_ACCESS_2_4B_FLAG 0x08000 + +/* Protocol defines */ +#define SDPCM_PROT_VERSION 4 +#define SDPCM_PROT_VERSION_SHIFT 16 +#define SDPCM_PROT_VERSION_MASK 0x00ff0000 +#define SDPCM_SHARED_VERSION 0x0003 +#define SDPCM_SHARED_VERSION_MASK 0x00FF +#define SDPCM_SHARED_ASSERT_BUILT 0x0100 +#define SDPCM_SHARED_ASSERT 0x0200 +#define SDPCM_SHARED_TRAP 0x0400 + +#define SDPCMD_INTSTATUS 0x020 +#define SDPCMD_INTSTATUS_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */ +#define SDPCMD_INTSTATUS_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */ +#define SDPCMD_INTSTATUS_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */ +#define SDPCMD_INTSTATUS_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */ +#define SDPCMD_INTSTATUS_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */ +#define SDPCMD_INTSTATUS_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */ +#define SDPCMD_INTSTATUS_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */ +#define SDPCMD_INTSTATUS_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */ +#define SDPCMD_INTSTATUS_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */ +#define SDPCMD_INTSTATUS_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */ +#define SDPCMD_INTSTATUS_HMB_FC_STATE SDPCMD_INTSTATUS_HMB_SW0 +#define SDPCMD_INTSTATUS_HMB_FC_CHANGE SDPCMD_INTSTATUS_HMB_SW1 +#define SDPCMD_INTSTATUS_HMB_FRAME_IND SDPCMD_INTSTATUS_HMB_SW2 +#define SDPCMD_INTSTATUS_HMB_HOST_INT SDPCMD_INTSTATUS_HMB_SW3 +#define SDPCMD_INTSTATUS_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */ +#define SDPCMD_INTSTATUS_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */ +#define SDPCMD_INTSTATUS_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */ +#define SDPCMD_INTSTATUS_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */ +#define SDPCMD_INTSTATUS_PC (1 << 10)/* descriptor error */ +#define SDPCMD_INTSTATUS_PD (1 << 11)/* data error */ +#define SDPCMD_INTSTATUS_DE (1 << 12)/* Descriptor protocol Error */ +#define SDPCMD_INTSTATUS_RU (1 << 13)/* Receive descriptor Underflow */ +#define SDPCMD_INTSTATUS_RO (1 << 14)/* Receive fifo Overflow */ +#define SDPCMD_INTSTATUS_XU (1 << 15)/* Transmit fifo Underflow */ +#define SDPCMD_INTSTATUS_RI (1 << 16)/* Receive Interrupt */ +#define SDPCMD_INTSTATUS_BUSPWR (1 << 17)/* SDIO Bus Power Change (rev 9) */ +#define SDPCMD_INTSTATUS_XMTDATA_AVAIL (1 << 23)/* bits in fifo */ +#define SDPCMD_INTSTATUS_XI (1 << 24)/* Transmit Interrupt */ +#define SDPCMD_INTSTATUS_RF_TERM (1 << 25)/* Read Frame Terminate */ +#define SDPCMD_INTSTATUS_WF_TERM (1 << 26)/* Write Frame Terminate */ +#define SDPCMD_INTSTATUS_PCMCIA_XU (1 << 27)/* PCMCIA Transmit FIFO Underflow */ +#define SDPCMD_INTSTATUS_SBINT (1 << 28)/* sbintstatus Interrupt */ +#define SDPCMD_INTSTATUS_CHIPACTIVE (1 << 29)/* chip from doze to active state */ +#define SDPCMD_INTSTATUS_SRESET (1 << 30)/* CCCR RES interrupt */ +#define SDPCMD_INTSTATUS_IOE2 (1U << 31)/* CCCR IOE2 Bit Changed */ +#define SDPCMD_INTSTATUS_ERRORS (SDPCMD_INTSTATUS_PC | \ + SDPCMD_INTSTATUS_PD | \ + SDPCMD_INTSTATUS_DE | \ + SDPCMD_INTSTATUS_RU | \ + SDPCMD_INTSTATUS_RO | \ + SDPCMD_INTSTATUS_XU) +#define SDPCMD_INTSTATUS_DMA (SDPCMD_INTSTATUS_RI | \ + SDPCMD_INTSTATUS_XI | \ + SDPCMD_INTSTATUS_ERRORS) +#define SDPCMD_HOSTINTMASK 0x024 +#define SDPCMD_INTMASK 0x028 +#define SDPCMD_SBINTSTATUS 0x02c +#define SDPCMD_SBINTMASK 0x030 +#define SDPCMD_FUNCTINTMASK 0x034 +#define SDPCMD_TOSBMAILBOX 0x040 +#define SDPCMD_TOSBMAILBOX_NAK (1 << 0) +#define SDPCMD_TOSBMAILBOX_INT_ACK (1 << 1) +#define SDPCMD_TOSBMAILBOX_USE_OOB (1 << 2) +#define SDPCMD_TOSBMAILBOX_DEV_INT (1 << 3) +#define SDPCMD_TOHOSTMAILBOX 0x044 +#define SDPCMD_TOSBMAILBOXDATA 0x048 +#define SDPCMD_TOHOSTMAILBOXDATA 0x04C +#define SDPCMD_TOHOSTMAILBOXDATA_NAKHANDLED (1 << 0) +#define SDPCMD_TOHOSTMAILBOXDATA_DEVREADY (1 << 1) +#define SDPCMD_TOHOSTMAILBOXDATA_FC (1 << 2) +#define SDPCMD_TOHOSTMAILBOXDATA_FWREADY (1 << 3) +#define SDPCMD_TOHOSTMAILBOXDATA_FWHALT (1 << 4) + +struct bwfm_sdio_hwhdr { + uint16_t frmlen; + uint16_t cksum; +}; + +struct bwfm_sdio_hwexthdr { + uint16_t pktlen; + uint8_t res0; + uint8_t flags; + uint16_t res1; + uint16_t padlen; +}; + +struct bwfm_sdio_swhdr { + uint8_t seqnr; + uint8_t chanflag; /* channel + flag */ +#define BWFM_SDIO_SWHDR_CHANNEL_CONTROL 0x00 +#define BWFM_SDIO_SWHDR_CHANNEL_EVENT 0x01 +#define BWFM_SDIO_SWHDR_CHANNEL_DATA 0x02 +#define BWFM_SDIO_SWHDR_CHANNEL_GLOM 0x03 +#define BWFM_SDIO_SWHDR_CHANNEL_TEST 0x0F +#define BWFM_SDIO_SWHDR_CHANNEL_MASK 0x0F + uint8_t nextlen; + uint8_t dataoff; + uint8_t flowctl; + uint8_t maxseqnr; + uint16_t res0; +}; + +struct bwfm_sdio_sdpcm { + uint32_t flags; + uint32_t trap_addr; + uint32_t assert_exp_addr; + uint32_t assert_file_addr; + uint32_t assert_line; + uint32_t console_addr; + uint32_t msgtrace_addr; + uint8_t tag[32]; + uint32_t brpt_addr; +}; + +struct bwfm_sdio_console { + uint32_t vcons_in; + uint32_t vcons_out; + uint32_t log_buf; + uint32_t log_bufsz; + uint32_t log_idx; +};