Home | History | Annotate | Line # | Download | only in sdmmc
if_bwfm_sdio.c revision 1.1
      1 /* $NetBSD: if_bwfm_sdio.c,v 1.1 2017/11/07 16:30:32 khorben Exp $ */
      2 /* $OpenBSD: if_bwfm_sdio.c,v 1.1 2017/10/11 17:19:50 patrick Exp $ */
      3 /*
      4  * Copyright (c) 2010-2016 Broadcom Corporation
      5  * Copyright (c) 2016,2017 Patrick Wildt <patrick (at) blueri.se>
      6  *
      7  * Permission to use, copy, modify, and/or distribute this software for any
      8  * purpose with or without fee is hereby granted, provided that the above
      9  * copyright notice and this permission notice appear in all copies.
     10  *
     11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  */
     19 
     20 #include <sys/param.h>
     21 #include <sys/systm.h>
     22 #include <sys/buf.h>
     23 #include <sys/kernel.h>
     24 #include <sys/malloc.h>
     25 #include <sys/device.h>
     26 #include <sys/queue.h>
     27 #include <sys/socket.h>
     28 #include <sys/mutex.h>
     29 #include <sys/workqueue.h>
     30 #include <sys/pcq.h>
     31 
     32 #include <net/bpf.h>
     33 #include <net/if.h>
     34 #include <net/if_dl.h>
     35 #include <net/if_media.h>
     36 #include <net/if_ether.h>
     37 
     38 #include <netinet/in.h>
     39 
     40 #include <net80211/ieee80211_var.h>
     41 
     42 #include <dev/sdmmc/sdmmcvar.h>
     43 
     44 #include <dev/ic/bwfmvar.h>
     45 #include <dev/ic/bwfmreg.h>
     46 
     47 #define BWFM_SDIO_CCCR_BRCM_CARDCAP			0xf0
     48 #define  BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT	0x02
     49 #define  BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT		0x04
     50 #define  BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC		0x08
     51 #define BWFM_SDIO_CCCR_BRCM_CARDCTRL			0xf1
     52 #define  BWFM_SDIO_CCCR_BRCM_CARDCTRL_WLANRESET		0x02
     53 #define BWFM_SDIO_CCCR_BRCM_SEPINT			0xf2
     54 
     55 #ifdef BWFM_DEBUG
     56 #define DPRINTF(x)	do { if (bwfm_debug > 0) printf x; } while (0)
     57 #define DPRINTFN(n, x)	do { if (bwfm_debug >= (n)) printf x; } while (0)
     58 static int bwfm_debug = 2;
     59 #else
     60 #define DPRINTF(x)	do { ; } while (0)
     61 #define DPRINTFN(n, x)	do { ; } while (0)
     62 #endif
     63 
     64 #define DEVNAME(sc)	device_xname((sc)->sc_sc.sc_dev)
     65 
     66 struct bwfm_sdio_softc {
     67 	struct bwfm_softc	  sc_sc;
     68 	struct sdmmc_function	**sc_sf;
     69 	uint32_t		  sc_bar0;
     70 };
     71 
     72 int		 bwfm_sdio_match(device_t, cfdata_t, void *);
     73 void		 bwfm_sdio_attach(device_t, struct device *, void *);
     74 int		 bwfm_sdio_detach(device_t, int);
     75 
     76 uint8_t		 bwfm_sdio_read_1(struct bwfm_sdio_softc *, uint32_t);
     77 uint32_t	 bwfm_sdio_read_4(struct bwfm_sdio_softc *, uint32_t);
     78 void		 bwfm_sdio_write_1(struct bwfm_sdio_softc *, uint32_t,
     79 		     uint8_t);
     80 void		 bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t,
     81 		     uint32_t);
     82 
     83 uint32_t	 bwfm_sdio_buscore_read(struct bwfm_softc *, uint32_t);
     84 void		 bwfm_sdio_buscore_write(struct bwfm_softc *, uint32_t,
     85 		     uint32_t);
     86 int		 bwfm_sdio_buscore_prepare(struct bwfm_softc *);
     87 void		 bwfm_sdio_buscore_activate(struct bwfm_softc *, uint32_t);
     88 
     89 int		 bwfm_sdio_txdata(struct bwfm_softc *, struct mbuf *);
     90 int		 bwfm_sdio_txctl(struct bwfm_softc *, char *, size_t);
     91 int		 bwfm_sdio_rxctl(struct bwfm_softc *, char *, size_t *);
     92 
     93 struct bwfm_bus_ops bwfm_sdio_bus_ops = {
     94 	.bs_init = NULL,
     95 	.bs_stop = NULL,
     96 	.bs_txdata = bwfm_sdio_txdata,
     97 	.bs_txctl = bwfm_sdio_txctl,
     98 	.bs_rxctl = bwfm_sdio_rxctl,
     99 };
    100 
    101 struct bwfm_buscore_ops bwfm_sdio_buscore_ops = {
    102 	.bc_read = bwfm_sdio_buscore_read,
    103 	.bc_write = bwfm_sdio_buscore_write,
    104 	.bc_prepare = bwfm_sdio_buscore_prepare,
    105 	.bc_reset = NULL,
    106 	.bc_setup = NULL,
    107 	.bc_activate = bwfm_sdio_buscore_activate,
    108 };
    109 
    110 CFATTACH_DECL_NEW(bwfm_sdio, sizeof(struct bwfm_sdio_softc),
    111     bwfm_sdio_match, bwfm_sdio_attach, bwfm_sdio_detach, NULL);
    112 
    113 int
    114 bwfm_sdio_match(device_t parent, cfdata_t match, void *aux)
    115 {
    116 	struct sdmmc_attach_args *saa = aux;
    117 	struct sdmmc_function *sf = saa->sf;
    118 	struct sdmmc_cis *cis;
    119 
    120 	/* Not SDIO. */
    121 	if (sf == NULL)
    122 		return 0;
    123 
    124 	/* Look for Broadcom 433[04]. */
    125 	cis = &sf->sc->sc_fn0->cis;
    126 	if (cis->manufacturer != 0x02d0 || (cis->product != 0x4330 &&
    127 	    cis->product != 0x4334))
    128 		return 0;
    129 
    130 	/* We need both functions, but ... */
    131 	if (sf->sc->sc_function_count <= 1)
    132 		return 0;
    133 
    134 	/* ... only attach for one. */
    135 	if (sf->number != 1)
    136 		return 0;
    137 
    138 	return 1;
    139 }
    140 
    141 void
    142 bwfm_sdio_attach(device_t parent, device_t self, void *aux)
    143 {
    144 	struct bwfm_sdio_softc *sc = device_private(self);
    145 	struct sdmmc_attach_args *saa = aux;
    146 	struct sdmmc_function *sf = saa->sf;
    147 	struct bwfm_core *core;
    148 
    149 	aprint_naive("\n");
    150 
    151 	sc->sc_sf = malloc((sf->sc->sc_function_count + 1) *
    152 	    sizeof(struct sdmmc_function *), M_DEVBUF, M_WAITOK);
    153 
    154 	/* Copy all function pointers. */
    155 	SIMPLEQ_FOREACH(sf, &saa->sf->sc->sf_head, sf_list) {
    156 		sc->sc_sf[sf->number] = sf;
    157 	}
    158 	sf = saa->sf;
    159 
    160 	/*
    161 	 * TODO: set block size to 64 for func 1, 512 for func 2.
    162 	 * We might need to work on the SDMMC stack to be able to set
    163 	 * a block size per function.  Currently the IO code uses the
    164 	 * SDHC controller's maximum block length.
    165 	 */
    166 
    167 	/* Enable Function 1. */
    168 	if (sdmmc_io_function_enable(sc->sc_sf[1]) != 0) {
    169 		aprint_error_dev(self, "cannot enable function 1\n");
    170 		goto err;
    171 	}
    172 
    173 	DPRINTFN(2, ("%s: F1 signature read @0x18000000=%x\n", DEVNAME(sc),
    174 	    bwfm_sdio_read_4(sc, 0x18000000)));
    175 
    176 	/* Force PLL off */
    177 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR,
    178 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF |
    179 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ);
    180 
    181 	sc->sc_sc.sc_buscore_ops = &bwfm_sdio_buscore_ops;
    182 	if (bwfm_chip_attach(&sc->sc_sc) != 0) {
    183 		aprint_error_dev(self, "cannot attach chip\n");
    184 		goto err;
    185 	}
    186 
    187 	/* TODO: drive strength */
    188 
    189 	bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL,
    190 	    bwfm_sdio_read_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL) |
    191 	    BWFM_SDIO_CCCR_BRCM_CARDCTRL_WLANRESET);
    192 
    193 	core = bwfm_chip_get_pmu(&sc->sc_sc);
    194 	bwfm_sdio_write_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL,
    195 	    bwfm_sdio_read_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL) |
    196 	    (BWFM_CHIP_REG_PMUCONTROL_RES_RELOAD <<
    197 	     BWFM_CHIP_REG_PMUCONTROL_RES_SHIFT));
    198 
    199 	sc->sc_sc.sc_bus_ops = &bwfm_sdio_bus_ops;
    200 	sc->sc_sc.sc_proto_ops = &bwfm_proto_bcdc_ops;
    201 	bwfm_attach(&sc->sc_sc);
    202 
    203 	return;
    204 
    205 err:
    206 	free(sc->sc_sf, M_DEVBUF);
    207 }
    208 
    209 int
    210 bwfm_sdio_detach(struct device *self, int flags)
    211 {
    212 	struct bwfm_sdio_softc *sc = (struct bwfm_sdio_softc *)self;
    213 
    214 	bwfm_detach(&sc->sc_sc, flags);
    215 
    216 	free(sc->sc_sf, M_DEVBUF);
    217 
    218 	return 0;
    219 }
    220 
    221 uint8_t
    222 bwfm_sdio_read_1(struct bwfm_sdio_softc *sc, uint32_t addr)
    223 {
    224 	struct sdmmc_function *sf;
    225 	uint8_t rv;
    226 
    227 	/*
    228 	 * figure out how to read the register based on address range
    229 	 * 0x00 ~ 0x7FF: function 0 CCCR and FBR
    230 	 * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
    231 	 * The rest: function 1 silicon backplane core registers
    232 	 */
    233 	if ((addr & ~0x7ff) == 0)
    234 		sf = sc->sc_sf[0];
    235 	else
    236 		sf = sc->sc_sf[1];
    237 
    238 	rv = sdmmc_io_read_1(sf, addr);
    239 	return rv;
    240 }
    241 
    242 uint32_t
    243 bwfm_sdio_read_4(struct bwfm_sdio_softc *sc, uint32_t addr)
    244 {
    245 	struct sdmmc_function *sf;
    246 	uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK;
    247 	uint32_t rv;
    248 
    249 	if (sc->sc_bar0 != bar0) {
    250 		bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRLOW,
    251 		    (bar0 >>  8) & 0x80);
    252 		bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRMID,
    253 		    (bar0 >> 16) & 0xff);
    254 		bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRHIGH,
    255 		    (bar0 >> 24) & 0xff);
    256 		sc->sc_bar0 = bar0;
    257 	}
    258 
    259 	addr &= BWFM_SDIO_SB_OFT_ADDR_MASK;
    260 	addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG;
    261 
    262 	/*
    263 	 * figure out how to read the register based on address range
    264 	 * 0x00 ~ 0x7FF: function 0 CCCR and FBR
    265 	 * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
    266 	 * The rest: function 1 silicon backplane core registers
    267 	 */
    268 	if ((addr & ~0x7ff) == 0)
    269 		sf = sc->sc_sf[0];
    270 	else
    271 		sf = sc->sc_sf[1];
    272 
    273 	rv = sdmmc_io_read_4(sf, addr);
    274 	return rv;
    275 }
    276 
    277 void
    278 bwfm_sdio_write_1(struct bwfm_sdio_softc *sc, uint32_t addr, uint8_t data)
    279 {
    280 	struct sdmmc_function *sf;
    281 
    282 	/*
    283 	 * figure out how to read the register based on address range
    284 	 * 0x00 ~ 0x7FF: function 0 CCCR and FBR
    285 	 * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
    286 	 * The rest: function 1 silicon backplane core registers
    287 	 */
    288 	if ((addr & ~0x7ff) == 0)
    289 		sf = sc->sc_sf[0];
    290 	else
    291 		sf = sc->sc_sf[1];
    292 
    293 	sdmmc_io_write_1(sf, addr, data);
    294 }
    295 
    296 void
    297 bwfm_sdio_write_4(struct bwfm_sdio_softc *sc, uint32_t addr, uint32_t data)
    298 {
    299 	struct sdmmc_function *sf;
    300 	uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK;
    301 
    302 	if (sc->sc_bar0 != bar0) {
    303 		bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRLOW,
    304 		    (bar0 >>  8) & 0x80);
    305 		bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRMID,
    306 		    (bar0 >> 16) & 0xff);
    307 		bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRHIGH,
    308 		    (bar0 >> 24) & 0xff);
    309 		sc->sc_bar0 = bar0;
    310 	}
    311 
    312 	addr &= BWFM_SDIO_SB_OFT_ADDR_MASK;
    313 	addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG;
    314 
    315 	/*
    316 	 * figure out how to read the register based on address range
    317 	 * 0x00 ~ 0x7FF: function 0 CCCR and FBR
    318 	 * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
    319 	 * The rest: function 1 silicon backplane core registers
    320 	 */
    321 	if ((addr & ~0x7ff) == 0)
    322 		sf = sc->sc_sf[0];
    323 	else
    324 		sf = sc->sc_sf[1];
    325 
    326 	sdmmc_io_write_4(sf, addr, data);
    327 }
    328 
    329 uint32_t
    330 bwfm_sdio_buscore_read(struct bwfm_softc *bwfm, uint32_t reg)
    331 {
    332 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    333 	uint32_t val;
    334 
    335 	val = bwfm_sdio_read_4(sc, reg);
    336 	/* TODO: Workaround for 4335/4339 */
    337 
    338 	return val;
    339 }
    340 
    341 void
    342 bwfm_sdio_buscore_write(struct bwfm_softc *bwfm, uint32_t reg, uint32_t val)
    343 {
    344 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    345 	bwfm_sdio_write_4(sc, reg, val);
    346 }
    347 
    348 int
    349 bwfm_sdio_buscore_prepare(struct bwfm_softc *bwfm)
    350 {
    351 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    352 	uint8_t clkval, clkset, clkmask;
    353 	int i;
    354 
    355 	clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ |
    356 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF;
    357 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset);
    358 
    359 	clkmask = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL |
    360 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL;
    361 	clkval = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR);
    362 
    363 	if ((clkval & ~clkmask) != clkset) {
    364 		printf("%s: wrote 0x%02x read 0x%02x\n", DEVNAME(sc),
    365 		    clkset, clkval);
    366 		return 1;
    367 	}
    368 
    369 	for (i = 1000; i > 0; i--) {
    370 		clkval = bwfm_sdio_read_1(sc,
    371 		    BWFM_SDIO_FUNC1_CHIPCLKCSR);
    372 		if (clkval & clkmask)
    373 			break;
    374 	}
    375 	if (i == 0) {
    376 		printf("%s: timeout on ALPAV wait, clkval 0x%02x\n",
    377 		    DEVNAME(sc), clkval);
    378 		return 1;
    379 	}
    380 
    381 	clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF |
    382 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_ALP;
    383 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset);
    384 	delay(65);
    385 
    386 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SDIOPULLUP, 0);
    387 
    388 	return 0;
    389 }
    390 
    391 void
    392 bwfm_sdio_buscore_activate(struct bwfm_softc *bwfm, uint32_t rstvec)
    393 {
    394 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    395 	struct bwfm_core *core;
    396 
    397 	core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV);
    398 	bwfm_sdio_buscore_write(&sc->sc_sc,
    399 	    core->co_base + BWFM_SDPCMD_INTSTATUS, 0xFFFFFFFF);
    400 
    401 #if notyet
    402 	if (rstvec)
    403 		bwfm_sdio_ram_write(&sc->sc_sc, 0, &rstvec, sizeof(rstvec));
    404 #endif
    405 }
    406 
    407 int
    408 bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf *m)
    409 {
    410 #ifdef BWFM_DEBUG
    411 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    412 #endif
    413 	int ret = 1;
    414 
    415 	DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
    416 
    417 	return ret;
    418 }
    419 
    420 int
    421 bwfm_sdio_txctl(struct bwfm_softc *bwfm, char *buf, size_t len)
    422 {
    423 #ifdef BWFM_DEBUG
    424 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    425 #endif
    426 	int ret = 1;
    427 
    428 	DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
    429 
    430 	return ret;
    431 }
    432 
    433 int
    434 bwfm_sdio_rxctl(struct bwfm_softc *bwfm, char *buf, size_t *len)
    435 {
    436 #ifdef BWFM_DEBUG
    437 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    438 #endif
    439 	int ret = 1;
    440 
    441 	DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
    442 
    443 	return ret;
    444 }
    445