Module Name: src Committed By: khorben Date: Tue Nov 7 16:30:32 UTC 2017
Modified Files: src/sys/dev/sdmmc: files.sdmmc Added Files: src/sys/dev/sdmmc: if_bwfm_sdio.c Log Message: Add driver for Broadcom 802.11a/b/g/n/ac SDIO wireless devices, based on the OpenBSD bwfm(4) driver. I could not test this on any hardware yet, as it does not attach as-is on my Raspberry PI 3. To generate a diff of this commit: cvs rdiff -u -r1.4 -r1.5 src/sys/dev/sdmmc/files.sdmmc cvs rdiff -u -r0 -r1.1 src/sys/dev/sdmmc/if_bwfm_sdio.c 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/files.sdmmc diff -u src/sys/dev/sdmmc/files.sdmmc:1.4 src/sys/dev/sdmmc/files.sdmmc:1.5 --- src/sys/dev/sdmmc/files.sdmmc:1.4 Thu Oct 2 21:49:22 2014 +++ src/sys/dev/sdmmc/files.sdmmc Tue Nov 7 16:30:32 2017 @@ -1,4 +1,4 @@ -# $NetBSD: files.sdmmc,v 1.4 2014/10/02 21:49:22 jmcneill Exp $ +# $NetBSD: files.sdmmc,v 1.5 2017/11/07 16:30:32 khorben Exp $ # $OpenBSD: files.sdmmc,v 1.2 2006/06/01 21:53:41 uwe Exp $ # # Config file and device description for machine-independent SD/MMC code. @@ -21,3 +21,7 @@ file dev/sdmmc/ld_sdmmc.c ld_sdmmc device sbt: btbus, bluetooth attach sbt at sdmmc file dev/sdmmc/sbt.c sbt + +# Broadcom FullMAC SDIO wireless adapter +attach bwfm at sdmmc with bwfm_sdio +file dev/sdmmc/if_bwfm_sdio.c bwfm_sdio Added files: Index: src/sys/dev/sdmmc/if_bwfm_sdio.c diff -u /dev/null src/sys/dev/sdmmc/if_bwfm_sdio.c:1.1 --- /dev/null Tue Nov 7 16:30:32 2017 +++ src/sys/dev/sdmmc/if_bwfm_sdio.c Tue Nov 7 16:30:32 2017 @@ -0,0 +1,444 @@ +/* $NetBSD: if_bwfm_sdio.c,v 1.1 2017/11/07 16:30:32 khorben Exp $ */ +/* $OpenBSD: if_bwfm_sdio.c,v 1.1 2017/10/11 17:19:50 patrick Exp $ */ +/* + * Copyright (c) 2010-2016 Broadcom Corporation + * Copyright (c) 2016,2017 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.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> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_ether.h> + +#include <netinet/in.h> + +#include <net80211/ieee80211_var.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 + +#ifdef BWFM_DEBUG +#define DPRINTF(x) do { if (bwfm_debug > 0) printf x; } while (0) +#define DPRINTFN(n, x) do { if (bwfm_debug >= (n)) printf x; } while (0) +static int bwfm_debug = 2; +#else +#define DPRINTF(x) do { ; } while (0) +#define DPRINTFN(n, x) do { ; } while (0) +#endif + +#define DEVNAME(sc) device_xname((sc)->sc_sc.sc_dev) + +struct bwfm_sdio_softc { + struct bwfm_softc sc_sc; + struct sdmmc_function **sc_sf; + uint32_t sc_bar0; +}; + +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); + +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, + 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_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 *); + +struct bwfm_bus_ops bwfm_sdio_bus_ops = { + .bs_init = NULL, + .bs_stop = NULL, + .bs_txdata = bwfm_sdio_txdata, + .bs_txctl = bwfm_sdio_txctl, + .bs_rxctl = bwfm_sdio_rxctl, +}; + +struct bwfm_buscore_ops bwfm_sdio_buscore_ops = { + .bc_read = bwfm_sdio_buscore_read, + .bc_write = bwfm_sdio_buscore_write, + .bc_prepare = bwfm_sdio_buscore_prepare, + .bc_reset = NULL, + .bc_setup = NULL, + .bc_activate = bwfm_sdio_buscore_activate, +}; + +CFATTACH_DECL_NEW(bwfm_sdio, sizeof(struct bwfm_sdio_softc), + bwfm_sdio_match, bwfm_sdio_attach, bwfm_sdio_detach, NULL); + +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; + + /* 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)) + return 0; + + /* We need both functions, but ... */ + if (sf->sc->sc_function_count <= 1) + return 0; + + /* ... only attach for one. */ + if (sf->number != 1) + return 0; + + return 1; +} + +void +bwfm_sdio_attach(device_t parent, device_t self, void *aux) +{ + struct bwfm_sdio_softc *sc = device_private(self); + struct sdmmc_attach_args *saa = aux; + struct sdmmc_function *sf = saa->sf; + struct bwfm_core *core; + + aprint_naive("\n"); + + sc->sc_sf = malloc((sf->sc->sc_function_count + 1) * + sizeof(struct sdmmc_function *), M_DEVBUF, M_WAITOK); + + /* 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. + */ + + /* Enable Function 1. */ + if (sdmmc_io_function_enable(sc->sc_sf[1]) != 0) { + aprint_error_dev(self, "cannot enable function 1\n"); + goto err; + } + + DPRINTFN(2, ("%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, + BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF | + BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ); + + 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; + } + + /* 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); + + core = bwfm_chip_get_pmu(&sc->sc_sc); + bwfm_sdio_write_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL, + bwfm_sdio_read_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL) | + (BWFM_CHIP_REG_PMUCONTROL_RES_RELOAD << + BWFM_CHIP_REG_PMUCONTROL_RES_SHIFT)); + + 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); + + return; + +err: + free(sc->sc_sf, M_DEVBUF); +} + +int +bwfm_sdio_detach(struct device *self, int flags) +{ + struct bwfm_sdio_softc *sc = (struct bwfm_sdio_softc *)self; + + bwfm_detach(&sc->sc_sc, flags); + + free(sc->sc_sf, M_DEVBUF); + + return 0; +} + +uint8_t +bwfm_sdio_read_1(struct bwfm_sdio_softc *sc, uint32_t addr) +{ + struct sdmmc_function *sf; + uint8_t rv; + + /* + * figure out how to read the register based on address range + * 0x00 ~ 0x7FF: function 0 CCCR and FBR + * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers + * The rest: function 1 silicon backplane core registers + */ + if ((addr & ~0x7ff) == 0) + sf = sc->sc_sf[0]; + else + sf = sc->sc_sf[1]; + + rv = sdmmc_io_read_1(sf, addr); + return rv; +} + +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; + + if (sc->sc_bar0 != bar0) { + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRLOW, + (bar0 >> 8) & 0x80); + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRMID, + (bar0 >> 16) & 0xff); + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRHIGH, + (bar0 >> 24) & 0xff); + sc->sc_bar0 = bar0; + } + + addr &= BWFM_SDIO_SB_OFT_ADDR_MASK; + addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; + + /* + * figure out how to read the register based on address range + * 0x00 ~ 0x7FF: function 0 CCCR and FBR + * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers + * The rest: function 1 silicon backplane core registers + */ + if ((addr & ~0x7ff) == 0) + sf = sc->sc_sf[0]; + else + sf = sc->sc_sf[1]; + + rv = sdmmc_io_read_4(sf, addr); + return rv; +} + +void +bwfm_sdio_write_1(struct bwfm_sdio_softc *sc, uint32_t addr, uint8_t data) +{ + struct sdmmc_function *sf; + + /* + * figure out how to read the register based on address range + * 0x00 ~ 0x7FF: function 0 CCCR and FBR + * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers + * The rest: function 1 silicon backplane core registers + */ + if ((addr & ~0x7ff) == 0) + sf = sc->sc_sf[0]; + else + sf = sc->sc_sf[1]; + + sdmmc_io_write_1(sf, addr, data); +} + +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; + + if (sc->sc_bar0 != bar0) { + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRLOW, + (bar0 >> 8) & 0x80); + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRMID, + (bar0 >> 16) & 0xff); + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRHIGH, + (bar0 >> 24) & 0xff); + sc->sc_bar0 = bar0; + } + + addr &= BWFM_SDIO_SB_OFT_ADDR_MASK; + addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; + + /* + * figure out how to read the register based on address range + * 0x00 ~ 0x7FF: function 0 CCCR and FBR + * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers + * The rest: function 1 silicon backplane core registers + */ + if ((addr & ~0x7ff) == 0) + sf = sc->sc_sf[0]; + else + sf = sc->sc_sf[1]; + + sdmmc_io_write_4(sf, addr, data); +} + +uint32_t +bwfm_sdio_buscore_read(struct bwfm_softc *bwfm, uint32_t reg) +{ + struct bwfm_sdio_softc *sc = (void *)bwfm; + uint32_t val; + + val = bwfm_sdio_read_4(sc, reg); + /* TODO: Workaround for 4335/4339 */ + + return val; +} + +void +bwfm_sdio_buscore_write(struct bwfm_softc *bwfm, uint32_t reg, uint32_t val) +{ + struct bwfm_sdio_softc *sc = (void *)bwfm; + bwfm_sdio_write_4(sc, reg, val); +} + +int +bwfm_sdio_buscore_prepare(struct bwfm_softc *bwfm) +{ + struct bwfm_sdio_softc *sc = (void *)bwfm; + uint8_t clkval, clkset, clkmask; + int i; + + clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ | + BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF; + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset); + + clkmask = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL | + BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL; + clkval = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); + + if ((clkval & ~clkmask) != clkset) { + printf("%s: wrote 0x%02x read 0x%02x\n", DEVNAME(sc), + clkset, clkval); + return 1; + } + + for (i = 1000; i > 0; i--) { + clkval = bwfm_sdio_read_1(sc, + BWFM_SDIO_FUNC1_CHIPCLKCSR); + if (clkval & clkmask) + break; + } + if (i == 0) { + printf("%s: timeout on ALPAV wait, clkval 0x%02x\n", + DEVNAME(sc), clkval); + return 1; + } + + clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF | + BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_ALP; + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset); + delay(65); + + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SDIOPULLUP, 0); + + return 0; +} + +void +bwfm_sdio_buscore_activate(struct bwfm_softc *bwfm, uint32_t rstvec) +{ + struct bwfm_sdio_softc *sc = (void *)bwfm; + struct bwfm_core *core; + + core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV); + bwfm_sdio_buscore_write(&sc->sc_sc, + core->co_base + BWFM_SDPCMD_INTSTATUS, 0xFFFFFFFF); + +#if notyet + if (rstvec) + bwfm_sdio_ram_write(&sc->sc_sc, 0, &rstvec, sizeof(rstvec)); +#endif +} + +int +bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf *m) +{ +#ifdef BWFM_DEBUG + struct bwfm_sdio_softc *sc = (void *)bwfm; +#endif + int ret = 1; + + DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); + + return ret; +} + +int +bwfm_sdio_txctl(struct bwfm_softc *bwfm, char *buf, size_t len) +{ +#ifdef BWFM_DEBUG + struct bwfm_sdio_softc *sc = (void *)bwfm; +#endif + int ret = 1; + + DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); + + return ret; +} + +int +bwfm_sdio_rxctl(struct bwfm_softc *bwfm, char *buf, size_t *len) +{ +#ifdef BWFM_DEBUG + struct bwfm_sdio_softc *sc = (void *)bwfm; +#endif + int ret = 1; + + DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); + + return ret; +}