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.
This commit is contained in:
parent
cd1c6201df
commit
d6735baf16
|
@ -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
|
||||
|
|
|
@ -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 <patrick@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;
|
||||
}
|
Loading…
Reference in New Issue